David McCullough wrote:

> Some quick bits.

Thanks for the answer, David.

> > Strictly speaking, in contrast to what the man page says, execve()
is
> > the only member of the exec family that is safe, since everything
else
> > is (probably) part of libc and is prone to modify some data.
> 
> Not sure this is entirely true,  but yes,  any libc function can
> misbehave,  you need to check them to be 100% sure.

My assumption is that execl*() need to allocate memory in order to
construct the argv, so the're probably a big risk.  execvp/execv
_might_ be safe _if_ they use local storage for their work.  But
you never can be sure unless you know exactly the internals of your
libc.

> > Since strictly following the man page leads to such a disappointing
> > result, it is tempting to think about what would actually happen in
> > the real world.
> 
> My experience says the man page is correct :-)

That's my experience, too.  But sometimes it is hard to understand
exactly what the man page tries to say ;-)  I'm trying to make sure
whether this strict interpretation is really the way it was meant or
maybe I'm just too paranoid.

> > The showstopper with the widest consequences is errno.  About every
> > system call can change errno.  As first resort, this can easily be
> > worked around by saving/restoring errno.  But on a second thought,
> > not even this is needed.  By definition, errno (in parent context)
is 
> > only valid if vfork() fails.  But since the child already _is_
> > executing, there's no way for vfork() to signal an error.  Therefore
> > the parent is not allowed to check errno and thus we are safe to
modify
> > errno.
> 
> if you only call execve after a vfork then you will not mess up errno.
> execve only returns in the parent context.

Oh, in the above paragraph I was not talking about execve() messing with
errno.  After all, the manpage explicitely allows to call execve().
In the above paragraph I tried to clarify that _if_ modification to
errno would be forbidden, then _every other_ system call (except execve)
would be forbidden, too.

> > Next interesting question is the "call any other function" part.
Why
> > is this restriction?  A callee should never clobber caller's stack
> > frame.
> > In the rest of this discussion I assume that it is safe to call
other
> > functions _if_ they obey the above rules.  (Please correct me if I'm
> > wrong).  If this assumption is wrong, we are back to the construct
> > shown above being the only valid construct.  Would some guru please
> > clarify this question?
> 
> Other functions can:
> 
> 1) return,  returning from a function after a vfork is very bad,
>    you will start using the parents active stack.

s/from a function/from the function/

The function that is forbidden to return is the function that called
vfork.  The callees of this function shold not mess with the stack
of their callers. (except they start fiddling on the stack, but who
would do _that_?)

> 2) modify globals with unexpected results.
> 3) allocate memory we corrupts the parents malloc/heap data 
>    structures.
> 4) Do things to variables on the stack that have side affects when the
>    parent gets to run.

This is why I wrote "_if_ they obey the above rules".

> 5) use stdio (bad ;-)

I think printf should be OK, but anything else from stdio I agree with
you.

> > There are lots of system calls to retrieve/set administrative data
of
> > the process like getpid/setpid/chdir and lots more.  Such functions
> > operate on kernel's data structures.  The kernel keeps separate
memory
> > for every process to store this information.  Thus there should be
no
> > problem with such calls _if_ the results of such functions are
stored
> > in separate variables which are not accessed by the parent.
> 
> Yes,  there are things you can run,  in practice you will find plenty
> of examples in the uCLinux-dist where fd's are closed/opened 
> (NOT fopen mind you). things like
> 
>       pid = vfork()
>       if (pid == 0) {
>               close(0);
>               close(1);
>               close(2);
>               open("/dev/null", ...)
>               ...
>               execv(...)
>       }

David Howells mentiones dup/dup2 would also be OK.  It would be fine
if we could collect a list of "safe" functions/syscalls and classify
anything else as "dangerous" or "forbidden"

> > Now let's take a look at things like printf() and friends.  They
look
> > very dangerous at first sight.  But OTOH, they should keep their
data
> > structures consistent.  Real problems are to be expected by
> > fopen/fclose because they invalidate parent's bookkeeping about
which 
> > files are open.
> 
> Yes,  you can get away with printf most of the time.
> 
> > malloc/free _can_ be safe, too _if_ _exactly_ the malloc'ed 
> > areas are freed.
> 
> I wouldn't agree here,  since free doesn't always free, etc.

OK, this will result in a memory leak.

> Use mmap to get memory and avoid the malloc lib.

This means mmap is safe if you take care to properly munmap?

> > A big can of worms seem to be signal handlers.  I can imagine only
> > one way to get them safe: disable all of them before vfork is
called.
> 
> If you signal handlers are well behaved you will be fine.  

I don't agree here.  The average handler will modify some data.  I can
only imagine of three sorts of handlers which don't modify anything:
- noop
- reap zombies (but the client should not have zombies since it is not
  allowed to call vfork again. (is this actually true?))
- immediately call _exit()

> 2) If you are working with existing code that you do not want to hack
>    too much,  then check for all the known bad things (some listed
>    above) to see if you "might" get by.

Well, the point of this thread is to get a list of "known good/bad
things"
_______________________________________________
uClinux-dev mailing list
[email protected]
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by [email protected]
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

Reply via email to