Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 16 Apr 1997 18:07:19 +1000
From:      Bruce Evans <bde@zeta.org.au>
To:        current@FreeBSD.org, toor@dyson.iquest.net
Subject:   Re: You will need to recompile your libc and apps!!!
Message-ID:  <199704160807.SAA03190@godzilla.zeta.org.au>

next in thread | raw e-mail | index | archive | help
>There is a serious bug in popen that has gone undetected until now.  I

The bug came from Lite2, so it has not gone undetected for long :-).

>have just committed a fix that makes the shared address space vfork
>problem go away (which was due to the popen boo-boo.)  I will not

The fix is far too complicated.  It is sufficient to remove one (unused
except for its corruption of of the parent) assignment to pdes[1].
Function calls do not use call by reference in C.

I think vfork() is still broken as designed.  It makes unwarranted
assumptions about the C implementation.  Look at what happens if a
(very reasonable) implementation preserves caller-saved registers
in a natural way:

main()
{
	asm("
		xorl	%esi,%esi	# 0
		pushl	%esi		# save %esi
		call	_vfork
		popl	%esi		# attempt to restore %esi
		testl	%eax,%eax
		jge	1f	
		call	_abort		# fork failed
	1:
		jne	2f
		incl	%eax		# child, set %eax = 1
		pushl	%eax		# corrupt stack as part of exiting
		call	__exit		# exit code doesn't matter
	2:
		pushl	%esi		# parent
		call	__exit		# exit code 1 iff %esi was corrupted
	");
}

When the parent gains control, its `popl %esi' restores garbage instead of
the saved %esi.  Worse things may happen for unreasonable implementations.
There may be no stack...

I wrote the following about system() before I understood the full
brokenness of vfork().  Calling sigaction(), etc., should be no worse
than calling execve().  Both depend on the C implementation not changing
the stack level significantly before making the call making the call.
Just returning to the parent depends on the vfork() implementation not
doing too much with the stack (another layer of function calls would
corrupt return addresses) :-(.

popen.c happens to be the only place in libc that uses vfork.  I think
that system() should also use it.  The child in system() does only
sigaction() and sigprocmask() calls.  This is a good example of where
it is critical to know the difference between system calls and library
functions - it is hard to tell whether a library function has any side
effects.  OTOH, all the functions that are safe to call in signal handlers
are probably safe to call in vfork()ed children, since any side effects
would make them unsafe to call in signal handlers.  Unfortunately, _no_
syscalls are safe to call from signal handlers in FreeBSD, since all
syscalls may have the side effect of clobbering errno.  Fortunately,
clobbering errno probably is not a problem for vfork() - after a
successful vfork(), the parent has no reason to check errno, and usually
has reasons to distrust it (I forget if successful syscalls can clobber
errno - what happens for

	{ errno = 0; susccessful_syscall(); assert(errno == 0); } ?).

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199704160807.SAA03190>