[Bug 7700] New: busybox init calls vfork() but does not immediately call exec(), probably causing downstream pain

bugzilla at busybox.net bugzilla at busybox.net
Tue Dec 9 04:21:05 UTC 2014


https://bugs.busybox.net/show_bug.cgi?id=7700

           Summary: busybox init calls vfork() but does not immediately
                    call exec(), probably causing downstream pain
           Product: Busybox
           Version: 1.22.x
          Platform: All
        OS/Version: Linux
            Status: NEW
          Severity: major
          Priority: P5
         Component: Standard Compliance
        AssignedTo: unassigned at busybox.net
        ReportedBy: eric at aterlo.com
                CC: busybox-cvs at busybox.net
   Estimated Hours: 0.0


The vfork() man page says that after a vfork(), the child needs to just call
exec(), and nothing else.  Don't write to stack variables (with the sole
exception of your pid_t).  Don't call any other functions.  Don't even return
from the current function.  Just call exec(), and if that fails you can call
_exit().

For convenience, here is the relevant hunk of the vfork() man page:

==
DESCRIPTION
   Standard Description
       (From POSIX.1) The vfork() function has the  same  effect  as  fork(2),
       except that the behavior is undefined if the process created by vfork()
       either modifies any data other than a variable of type  pid_t  used  to
       store  the  return  value from vfork(), or returns from the function in
       which vfork() was called, or calls any other function  before  success-
       fully calling _exit(2) or one of the exec(3) family of functions.

   Linux Description
       vfork(),  just  like  fork(2),  creates  a child process of the calling
       process.  For details and return value and errors, see fork(2).

       vfork() is a special case of clone(2).  It is used to create  new  pro-
       cesses  without  copying the page tables of the parent process.  It may
       be useful in performance-sensitive applications where a child  is  cre-
       ated which then immediately issues an execve(2).

       vfork()  differs from fork(2) in that the parent is suspended until the
       child terminates (either normally, by calling _exit(2), or  abnormally,
       after  delivery  of  a  fatal signal), or it makes a call to execve(2).
       Until that point, the child shares all memory with its parent,  includ-
       ing  the stack.  The child must not return from the current function or
       call exit(3), but may call _exit(2).

       Signal handlers are inherited, but not shared.  Signals to  the  parent
       arrive  after  the  child releases the parent's memory (i.e., after the
       child terminates or calls execve(2)).
==

In busybox's init.c, run() violates most of those requirements.

I am currently investigating a bug on ASUS routers (which make heavy use of
busybox) in which certain processes unexpectedly are blocking a wide variety of
signals, presumably because they are inheriting bad masks from their parents
across fork()/exec() calls.  This may have something to do with the races in
busybox's init.c:run(), though I have not yet proven this.

For example, after running 'tail -F', I can't type control-C to kill it,
because it has inherited a blocking mask that includes SIGINT:

==
admin@(none):/proc/2741# ps w | grep tail
 6778 admin     1552 S    tail -F /tmp/syslog.log
admin@(none):/proc/2741# cat /proc/6778/status | grep Sig
SigQ:   10/2047
SigPnd: 00000000000000000000000000000000
SigBlk: 0000000000000000000000008001e007
SigIgn: 00000000000000000000000040000006
SigCgt: 00000000000000000000000000000000
admin@(none):/proc/2741# kill 6778
admin@(none):/proc/2741# ps w | grep tail
 6778 admin     1552 S    tail -F /tmp/syslog.log
admin@(none):/proc/2741# 
==

The SigBlk value of 8001e007 corresponds to SIGHUP, SIGINT, SIGQUIT, SIGALRM,
SIGTERM, SIGUSR1, and SIGUSR2.

As you can imagine, this kind of thing makes life pretty miserable on the OS in
general -- all those rc.d scripts that rely on TERM to shut things down, all
those programs that rely on the delivery of SIGALRM for timer events, or on
SIGHUP to re-read their configs, etc etc.

Again, I am not 100% certain that the unclean handling of vfork() is to blame. 
I will follow up if I am able to prove that it is.

-- 
Configure bugmail: https://bugs.busybox.net/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are on the CC list for the bug.


More information about the busybox-cvs mailing list