[comp.bugs.4bsd] /bin/sh may loop if SIGSEGV is blocked at startup

mark@sickkids.UUCP (Mark Bartelt) (03/01/90)

If a process with SIGSEGV blocked spawns /bin/sh as a subprocess, the
shell may loop forever.  The following is likely to exhibit the problem:

(1)  Create the following program called, say, "x":

        #include <signal.h>

        main()
        {
                sigblock(sigmask(SIGSEGV));
                system("echo foo");
        }

(2)  Define an environment variable with a gigantically long name, then
     run the program (in the background, so it's easier to kill things!):

        $ FOO='Some enormously long string of characters.  I have no
        idea how long it needs to be.  Several thousand is certainly
        sufficient; a couple hundred is probably adequate.'
        $ export FOO
        $ x &

(3)  Take a look at things with "ps".  You should see something like:

        16869 01 R     2:37 sh -c echo foo

     Watch the CPU time of the process accumulate without bound.

What's happening is that if the environment is sufficiently large, then
when the shell attempts to build an environment to pass to execve(), it
eventually runs past the end of valid memory.  Not a problem (it thinks),
since it's catching SIGSEGV, so that it can just do an sbrk() and return
from the signal catching routine.  But if SIGSEGV is blocked (note that
under 4.3bsd the signal mask is inherited across fork() and exec()), then
when the kernel takes the fault and invokes trap(), sendsig() never gets
called.  So when the kernel returns from the trap, the instruction which
caused the fault gets executed again, causing another fault, and the whole
thing continues, forever.

The fix is trivial.  In fault.c, at the beginning of stdsigs(), add the
following:

        sigsetmask(sigblock(0)&~sigmask(MEMF));

You also have to add "#include <signal.h>" so that the sigmask() macro is
defined.  Once you do this, though, the compiler is unhappy with another
statement, complaining that ...

        operands of & have incompatible types

... so you have to make one other change:  In ignsig(), change

        IF (s=signal(i=n,1)&01)==0
to
        IF (s=(int)signal(i=n,1)&01)==0

It may well be that the more sensible change might be to make the first
statement in stdsigs() ...

        sigsetmask(sigblock(0)&~(sigmask(INTR)|sigmask(MEMF)|sigmask(ALARM)));

... but I haven't given any thought to what the implications of doing so
might be.  (And why is it catching SIGALRM anyway?)

Mark Bartelt                          INTERNET: mark@sickkids.toronto.edu
Hospital for Sick Children, Toronto             mark@sickkids.utoronto.ca
416/598-6442                          UUCP: {utzoo,decvax}!sickkids!mark