[Top][All Lists]

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

Re: BASH recursion segfault, FUNCNEST doesn't help

From: Gergely
Subject: Re: BASH recursion segfault, FUNCNEST doesn't help
Date: Tue, 07 Jun 2022 14:17:45 +0000

On 6/7/22 15:49, Chet Ramey wrote:

> On 6/7/22 7:57 AM, Gergely wrote:
>>> Because you haven't forced bash to write outside its own address space or
>>> corrupt another area on the stack. This is a resource exhaustion issue,
>>> no more.
>> I did force it to write out of bounds, hence the segfault.
> That's backwards. You got a SIGSEGV, but it doesn't mean you forced bash to
> write beyond its address space. You get SIGSEGV when you exceed your stack
> or VM resource limits. Given the nature of the original script, it's
> probably the former.

I am not saying the write was successful, but the only reason it wasn't is 
because the kernel doesn't map pages there. Bash not caring about this makes 
it's relying on the kernel behaving "right".

Here's a very trivial example that'll show $rsp containing an address that is 
outside of the stack:

$ gdb --args bash -c 'cat /proc/self/maps; echo ". a" > a; . a'
(gdb) r
Starting program: /usr/bin/bash -c cat\ /proc/self/maps\;\ echo\ \".\ a\"\ \>\ 
a\;\ .\ a
[Detaching after fork from child process 45053]
555555554000-555555556000 r--p 00000000 fd:00 1573537                    
555555556000-55555555b000 r-xp 00002000 fd:00 1573537                    
55555555b000-55555555d000 r--p 00007000 fd:00 1573537                    
55555555e000-55555555f000 r--p 00009000 fd:00 1573537                    
55555555f000-555555560000 rw-p 0000a000 fd:00 1573537                    
555555560000-555555581000 rw-p 00000000 00:00 0                          [heap]
7ffff7803000-7ffff7dbb000 r--p 00000000 fd:00 1573144                    
7ffff7dbb000-7ffff7dbe000 rw-p 00000000 00:00 0
7ffff7dbe000-7ffff7de0000 r--p 00000000 fd:00 1576105                    
7ffff7de0000-7ffff7f38000 r-xp 00022000 fd:00 1576105                    
7ffff7f38000-7ffff7f88000 r--p 0017a000 fd:00 1576105                    
7ffff7f88000-7ffff7f8c000 r--p 001c9000 fd:00 1576105                    
7ffff7f8c000-7ffff7f8e000 rw-p 001cd000 fd:00 1576105                    
7ffff7f8e000-7ffff7f97000 rw-p 00000000 00:00 0
7ffff7fa2000-7ffff7fc6000 rw-p 00000000 00:00 0
7ffff7fc6000-7ffff7fca000 r--p 00000000 00:00 0                          [vvar]
7ffff7fca000-7ffff7fcc000 r-xp 00000000 00:00 0                          [vdso]
7ffff7fcc000-7ffff7fcd000 r--p 00000000 fd:00 1576101                    
7ffff7fcd000-7ffff7ff1000 r-xp 00001000 fd:00 1576101                    
7ffff7ff1000-7ffff7ffb000 r--p 00025000 fd:00 1576101                    
7ffff7ffb000-7ffff7ffd000 r--p 0002e000 fd:00 1576101                    
7ffff7ffd000-7ffff7fff000 rw-p 00030000 fd:00 1576101                    
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]

Program received signal SIGSEGV, Segmentation fault.
0x000055555558e963 in yyparse () at ./build-bash/y.tab.c:1744
1744    ./build-bash/y.tab.c: No such file or directory.
(gdb) info frame 0
Stack frame at 0x7fffff7ffc40:
 rip = 0x55555558e963 in yyparse (./build-bash/y.tab.c:1744); saved rip = 
 called by frame at 0x7fffff7ffc60
 source language c.
 Arglist at 0x7fffff7fed98, args:
 Locals at 0x7fffff7fed98, Previous frame's sp is 0x7fffff7ffc40
 Saved registers:
  rbx at 0x7fffff7ffc08, rbp at 0x7fffff7ffc10, r12 at 0x7fffff7ffc18, r13 at 
0x7fffff7ffc20, r14 at 0x7fffff7ffc28, r15 at 0x7fffff7ffc30, rip at 
(gdb) disas yyparse
Dump of assembler code for function yyparse:
   0x000055555558e959 <+57>:    xor    %eax,%eax
   0x000055555558e95b <+59>:    lea    0x1d0(%rsp),%rbx
=> 0x000055555558e963 <+67>:    mov    %ax,0x40(%rsp)
   0x000055555558e968 <+72>:    mov    %r8,%rbp

Here $rsp points to an invalid address. In this case reading fails, but it 
might as well be a write operation depending on what the given function does 
with it's local variables.

>> Not really, a programmer can't know how large the stack is and how many
>> more recursions bash can take. This is also kernel/distro/platform
>> dependent. I get that it's a hard limit to hit, but to say the programmer
>> has complete control is not quite true.
> True, the programmer can't know the stack size. But in a scenario where you
> really need to recurse hundreds or thousands of times (is there one?), the
> programmer can try to increase the stack size with `ulimit -s' and warn the
> user if that fails.

If there's a way for an attacker to make bash allocate very large stack frames, 
this number doesn't have to be very big.

>> Sure, for unmitigated disasters of code like infinite recursions, I agree
>> with you. This problem is not about that though. It's about a bounded -
>> albeit large - number of recursions.
> This is not an example of a bounded number of recursions, since the second
> process sends a continuous stream of SIGUSR1s.

This was meant to be an example of some more reasonable code that is likely to 
exist in the wild.

>> For the sake of example, consider a program with a somewhat slow signal
>> handler. This program might be forced to segfault by another program that
>> can send it large amounts of signals in quick succession.
> This is another example of recursive execution that results in a stack size
> resource limit failure, and wouldn't be helped by any of the things we're
> talking about -- though there is an EVALNEST_MAX define that could.

Not sure what "stack size resource limit failure" means in this context, but 
this does end in a segfault.

In any case, I feel like this conversation is not going anywhere so I will stop 
writing to this list.

Thank you for your attention,


reply via email to

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