[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: SEGFAULT if bash script make "source" for itself

From: Eric Blake
Subject: Re: SEGFAULT if bash script make "source" for itself
Date: Thu, 28 Aug 2014 15:43:29 -0600
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.7.0

On 08/28/2014 03:00 PM, address@hidden wrote:
>>> Whey there is check on division by zero? We can predict this? - No. But
>> we
>>> can detect it... and we out nice, detailed error message.
>> Actually, division by zero is fairly easy to check, and this is probably
>> a case where bash is checking for division by 0 up front rather than
>> handling SIGFPE after the fact.
> Is it so heavy to check length of $BASH_SOURCE array?

Checking the length of $BASH_SOURCE array (or indeed, ANY check of some
counter compared to the current recursion depth) is only an
approximation.  It tells whether you are nearing an artificial limit.
It does NOT tell you if you will overflow the stack (it's possible to
set the variable too high, and still trigger a stack overflow; more
likely, if you set the variable too low, someone can come up with a
recursive program that _would_ have completed had it been granted full
access to the stack but now fails because your limit got in the way).

For _certain_ cases of programming, determining maximum stack usage is
computible (start at the leaves, figure out how much they allocate, then
work your way up the call stack).  But the moment you introduce
recursion into the mix, where the recursion is conditionally gated on
user input, it is _inherently impossible_ to compute the maximum stack
depth for all possible program execution flows, shy of actually
executing the program.  Bash, and many other scripting languages, are in
such a boat - by giving the end user the power to write a recursive
function, they are also giving the end user the power to exhaust an
unknowable stack depth.

As long as the interpreter implements user recursion by using recursive
functions itself, there is no way to say "if I call your next function,
I will overflow the stack, so pre-emptively error out now, but keep my
interpreter running".  The _BEST_ we can do is detect that "I just ran
out of stack, but in jumping to the SIGSEGV handler, I can't guarantee
whether I interrupted a malloc or any other locked code, therefore I
cannot safely use malloc or any other locking function between now and
calling _exit()".

It is possible to write a class of programs that GUARANTEE that if stack
overflow happens, that it did not happen within any core function that
might hold a lock, and therefore the program can longjmp back to a safe
point, abort the overflowing operation, and carry on with life.  But it
is EXTREMELY TRICKY to do - you have to be absolutely vigilant that you
separate your code into two buckets - the set of code that might obtain
any lock, but is used non-recursively (and therefore you can compute the
maximum stack depth of that code), and the set of code that recurses,
but cannot obtain any lock without first checking that the current stack
depth plus the maximum depth of the locking code will still fit in the
stack.  With a program like that, you can then pre-emptively detect
stack overflow for the next call into non-recursive code without relying
on SIGSEGV (you'd still want the SIGSEGV handler for the recursive part,
but can now longjmp back to your non-recursive outer handler).  But it
is not practical, and would mean a complete rewrite of the bash source
code, and probably even a parallel stripped-down rewrite of glibc.

In many cases, it is also possible to convert recursive code into
iterative code; but usually, conversions like this involve trade-offs,
such as requiring heap storage to track progress between iterations
where the old code used the stack.  Again, doing such conversions to the
bash code base would mean a complete rewrite.  And such a conversion is
worthwhile only if everything doable in one leg of the recursion is
known up front - but bash is a scripting language and can't predict what
all user input code will want to do at each level of recursion, short of
executing the script.

>> So why I should got SIGSEGV instead of nice, detailed error message in
>>> recursion? We can detect it?
>> GNU libsigsegv proves that it is possible to detect when SIGSEGV was
>> caused by stack overflow.  It can't help prevent stack overflow, and you
>> _don't_ want to penalize your code by adding checking code into the
>> common case (if I'm about to overflow, error out instead), but leave
>> stack overflow as the exceptional case (if I've already overflowed and
>> received SIGSEGV, convert it into a nice error message to the user
>> before exiting cleanly, instead of the default behavior of dumping
>> core).  But someone would have to write the patch for bash to link
>> against libsigsegv.
> I undestand it. It better than getting SIGSEGV, but not a solution for this
> issue. As I think.

It's not clear what issue you think needs solving.

If you are trying to solve the issue of "prevent all possible SIGSEGV
from stack overflow", the answer is "it's impossible".

If you are trying to solve the issue of "bash dumps core based on user
input, but I'd prefer a nice error message telling me my program is
buggy", the answer is "write a patch to use libsigsegv".

If you are trying to solve the issue of "bash needs to give me a way to
optionally artificially limit source recursion, so that I reduce the set
of programs that can be run, but am less likely to trigger stack
overflow", the answer is "write a patch to copy (or reuse) FUNCNEST
limitations when sourcing" - but such a solution will not be on by
default.  Which means that you will only know if the solution makes a
difference for your program if you first run the program unlimited - but
then you are back to the earlier question of whether bash can give a
nice error message instead of a core dump when exiting due to stack

Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

Attachment: signature.asc
Description: OpenPGP digital signature

reply via email to

[Prev in Thread] Current Thread [Next in Thread]