Re: Implementing vfork

1999-08-03 Thread Robert de Bath

On Tue, 3 Aug 1999, Alan Cox wrote:

> > Hmm, looking at that it needs somebody to go through one instruction at
> > a time very carefully. Eg AX and SI are stashed in the code segment,
> > they could be stashed in the DS. Interrupts don't have to be
> > disabled 'cause you can use the SS feature:
> >mov  ss,ax
> >mov  sp,[bx]
> 
> That doesnt work on early 8088's

!
You're right; and I quote:

   If the destination is SS interrupts are disabled except on early buggy
   808x CPUs. Some CPUs disable interrupts if the destination is any of
   the segment registers.

I suppose we can't just ignore a few buggered old chips :-)

-- 
Rob.  (Robert de Bath )
 




Re: Implementing vfork

1999-08-03 Thread Alan Cox

> Hmm, looking at that it needs somebody to go through one instruction at
> a time very carefully. Eg AX and SI are stashed in the code segment,
> they could be stashed in the DS. Interrupts don't have to be
> disabled 'cause you can use the SS feature:
>movss,ax
>mov  sp,[bx]

That doesnt work on early 8088's



RE: Implementing vfork

1999-08-03 Thread Robert de Bath

Version 0.0.78

process.c(234) 

Segment register es is set to the same as ds and ss in _ret_from_syscall.
It's not saved on entry to _syscall_int either.

Hmm, looking at that it needs somebody to go through one instruction at
a time very carefully. Eg AX and SI are stashed in the code segment,
they could be stashed in the DS. Interrupts don't have to be
disabled 'cause you can use the SS feature:
   mov  ss,ax
   mov  sp,[bx]

In fact by judicious use of the stack and '__segoff' you probably don't
need any code segment stashing ... except I'm a little confused where
the user registers are stored, there's space in the task struct but
it looks like it isn't being used ?

FX: A little later:  I've attached a new insertion in process.c for you
lot to destroy ..

-- 
Rob.  (Robert de Bath )
 

On Tue, 3 Aug 1999, Greg Haerr wrote:

> : One other thing I noticed is that you trash the es register, the C library
> : treats this as callee saves, like si & di, but if I do that round every
> : int $80 it'll go an defeat this vfork() thing :-)
> : 
>   It's important that the ELKS kernel save all segment registers
> and si, di, just like the C library standard.  I thought I looked thru irq.c
> and irqtab.c and found that es is saved.  Where do you notice
> that it's trashed?
> 
> Greg
> 


!
!   System Call Vector
!
!   On entry we are on the wrong stack, DS, ES are wrong
!

.globl _syscall_int
!
!   System calls enter here with ax as function and bx,cx,dx as
!   parameters (and di,si if elks_syscall in elksemu is to be believed)
!   syscall returns a value in ax
!
_syscall_int:
!
!   We know the process DS, we can discard it (indeed may change it)
!
push ax
push bx
mov ax,cs
add ax,#__segoff
mov ds,ax
!
!   Find our TCB
!
mov bx,_current
!
!   Stash user mode stack - needed for stack checking!
!
pop 8[bx]
pop 6[bx]
mov 2[bx],sp
mov 20[bx],es
!
!   Finish switching to the right things
!
mov ss,ax
mov sp,[bx]

mov es,ax
cld
!
!   Stack is now right, we can take interrupts OK
!
sti
pushsi
pushdi
pushdx
pushcx
push8[bx]   ! saved bx
mov ax,6[bx]! restore ax
#ifdef CONFIG_STRACE
!
!   strace(syscall#, params...)
!



RE: Implementing vfork

1999-08-03 Thread Greg Haerr

: One other thing I noticed is that you trash the es register, the C library
: treats this as callee saves, like si & di, but if I do that round every
: int $80 it'll go an defeat this vfork() thing :-)
: 
It's important that the ELKS kernel save all segment registers
and si, di, just like the C library standard.  I thought I looked thru irq.c
and irqtab.c and found that es is saved.  Where do you notice
that it's trashed?

Greg



Re: Implementing vfork

1999-08-02 Thread Robert de Bath

I've used a machine with a real vfork() before now. The parent would crash
if the child left the function that vfork() is called from.

vfork is designed for just one purpose, a cheap replacment for fork() for
when you're spawning a new process from a shell (like program). It must
be used in code something like this:

   if( !(pid=vfork()) )
   {
  close(pipes[fd]);
  close(!fd);
  dup(pipes[!fd]);
  close(pipes[!fd]);
  /* Or something similar */

  exec(...);
  _exit(255);
   }

To quote a manual:
  The  use of vfork() for any purpose except as a prelude to
  an immediate exec() or exit() is not supported.  Any  pro­
  gram  that  relies upon the differences between fork() and
  vfork() is not portable across UNIX systems.

Also:
   vfork()  can  normally  be used just like fork().  It does
   not work, however, to return while running in the  child's
   context  from the procedure which called vfork() since the
   eventual return from vfork() would then  return  to  a  no
   longer  existent  stack  frame.  Be careful, also, to call
   _exit() rather than exit() if  you  cannot  exec(),  since
   exit()  flushes  and closes standard I/O channels, thereby
   damaging the parent process's  standard  I/O  data  struc­
   tures.  (Even with fork() it is wrong to call exit() since
   buffered data would then be flushed twice.)

The manual also notes that calling signal(2) can have weird effects!


The kernel implementation is not too difficult as the vfork() code in the
library is just:

_vfork:
  mov   ax,#32
  int   $80
  test  ax,ax
  jge  syscall_ok
  neg   ax
  mov   [_errno],ax
  mov   ax,#-1
syscall_ok:
  ret

This means that all you need to save is the stack pointer and the return
address for the last RET. The sp is already saved and the return address
can be saved somewhere else in *(current) ... say in BX, I imagine you 
need AX for the child PID. 

One other thing I noticed is that you trash the es register, the C library
treats this as callee saves, like si & di, but if I do that round every
int $80 it'll go an defeat this vfork() thing :-)

Putting a copy of the stack in the 'free space' is a _very_ bad idea,
it's sure to get overwritten sometime.

An interesting point is that you can nearly do this in userspace, but
unfortunatly the file descriptor semantics will bite you.

-- 
Rob.  (Robert de Bath )
 



Re: Implementing vfork

1999-07-28 Thread Eric J. Korpela

> > Ok.  When vfork() is called, save the stack pointer in the kernel.
> > Then, when the child calls exec(), more items are stored on the stack. (at
> > least the return address from the exec call)
> > When the parent is rescheduled, the system can reset the stack pointer
> > to the value it was when vfork was called, providing that it the child
> > never returned from the procedure that called fork, but instead called exec from 
>there...
> 
> The problem is that the child will pull data off the stack as it returns,

You could duplicate the return data on the stack for the child, or else use 
an indirect jump to return in child process.  You'd need to set the base
pointer correctly, so local variables are still accessable.

Because the data and stack are shared between parent and child, I'd expect
there are programs that expect changes to stack based variables made in the 
child be reflected to the parent.

Eric




RE: Implementing vfork

1999-07-28 Thread Greg Haerr

> : 
: > : The scheme I am using at the moment, that of copying the bottom 100 bytes
: > : of the stack for the child to use, works, but does not really offer any
: > : kind of safety net. Is it fair to just accept that if a process vfork()s,
: > : and does not exec or exit, but instead carries on, it may well crash?
: > : 
: > 
: > It's not acceptable to carry on and crash.
: 
: I can't yet work out a way to make sure this can't happen. We could set up
: some kind of timer that kills the child if it does not exec, or watch it to
: see if it shrinks its stack to much and kill it then.
: 
We need to agree on the semantics of vfork().  If the process
calling vfork() isn't allowed to call any other system call after vfork other than
exec() or maybe some dup() calls, then you're version could work.

If the calling process declares more than 100 bytes of stack and
returns from that procedure, you're crashed, even if it later calls exec.

I still don't see what's wrong with my first suggestion - don't allow
the caller to return from the procedure that it called vfork() from.  Then, the stack
doesn't need to be copied, and system calls don't need to be monitored.  The
stack frame will be ok when the parent returns from vfork().

Greg



Re: Implementing vfork

1999-07-28 Thread Alistair Riddoch

Greg Haerr writes:
> 
> : This is essentially what I have done. The only problem with doing this is
> : that when fork() returns to the child, and the child calls exec(), the stack
> : will be modified, so when the parent comes to return from fork(), it will
> : crash, or at best do something odd.
> 
>   Ok.  When vfork() is called, save the stack pointer in the kernel.
> Then, when the child calls exec(), more items are stored on the stack. (at
> least the return address from the exec call)
>   When the parent is rescheduled, the system can reset the stack pointer
> to the value it was when vfork was called, providing that it the child
> never returned from the procedure that called fork, but instead called exec from 
>there...

The problem is that the child will pull data off the stack as it returns,
and overwrite it when it calls exec. The stack state that the parent
requires in order to return from fork will have been overwritten.

> 
> 
> 
> : 
> : The scheme I am using at the moment, that of copying the bottom 100 bytes
> : of the stack for the child to use, works, but does not really offer any
> : kind of safety net. Is it fair to just accept that if a process vfork()s,
> : and does not exec or exit, but instead carries on, it may well crash?
> : 
> 
>   It's not acceptable to carry on and crash.

I can't yet work out a way to make sure this can't happen. We could set up
some kind of timer that kills the child if it does not exec, or watch it to
see if it shrinks its stack to much and kill it then.

Al



RE: Implementing vfork

1999-07-28 Thread Greg Haerr

: This is essentially what I have done. The only problem with doing this is
: that when fork() returns to the child, and the child calls exec(), the stack
: will be modified, so when the parent comes to return from fork(), it will
: crash, or at best do something odd.

Ok.  When vfork() is called, save the stack pointer in the kernel.
Then, when the child calls exec(), more items are stored on the stack. (at
least the return address from the exec call)
When the parent is rescheduled, the system can reset the stack pointer
to the value it was when vfork was called, providing that it the child
never returned from the procedure that called fork, but instead called exec from 
there...



: 
: The scheme I am using at the moment, that of copying the bottom 100 bytes
: of the stack for the child to use, works, but does not really offer any
: kind of safety net. Is it fair to just accept that if a process vfork()s,
: and does not exec or exit, but instead carries on, it may well crash?
: 

It's not acceptable to carry on and crash.
Greg




Re: Implementing vfork

1999-07-28 Thread Alistair Riddoch

Eric J. Korpela writes:
> 
> > 
> >  
> > : The parent process's data segment is not copied, just re-alloced, and
> > : rather than returning to the parrent process, fork sleeps on the parents
> > : child_wait wait queue.
> > 
> > Let me try to remind myself of the vfork semantics  Basically,
> > rather than copying the data segment on real computers the OS sets
> > the data segment page tables to copy-on-write, so that the data segment
> > copy time and space is saved, since it's all going to be replaced by the
> > following exec, right?
> 
> No, that's what fork() does on modern machines.  vfork() was developed back
> in the days when fork() actually did do a fully copy of the data segment,
> the way it's done in elks.  This is a waste when you're forking for the
> purpose of calling exec and waiting until the child exits.  From the (SunOS 4)
> man page:
> 
>  vfork()  differs
>  from  fork()  in  that the child borrows the parent's memory
>  and thread of control until a call to execve(2V), or an exit
>  (either  by  a  call  to exit(2V) or abnormally.) The parent
>  process is suspended while the child is using its resources.
> 
> 
> which basically means that the parent gets suspended, the child uses
> the parent's data segment until the execve() call at which point a new data
> segment is allocated for the child process and the parent is allowed to
> resume.
> 

This is essentially what I have done. The only problem with doing this is
that when fork() returns to the child, and the child calls exec(), the stack
will be modified, so when the parent comes to return from fork(), it will
crash, or at best do something odd.

The scheme I am using at the moment, that of copying the bottom 100 bytes
of the stack for the child to use, works, but does not really offer any
kind of safety net. Is it fair to just accept that if a process vfork()s,
and does not exec or exit, but instead carries on, it may well crash?

Also 100 bytes is quite alot of stack. Any opinions on what would be a more
sensible, but still safe ammount?

Al



Re: Implementing vfork

1999-07-27 Thread Eric J. Korpela

> 
>  
> : The parent process's data segment is not copied, just re-alloced, and
> : rather than returning to the parrent process, fork sleeps on the parents
> : child_wait wait queue.
> 
>   Let me try to remind myself of the vfork semantics  Basically,
> rather than copying the data segment on real computers the OS sets
> the data segment page tables to copy-on-write, so that the data segment
> copy time and space is saved, since it's all going to be replaced by the
> following exec, right?

No, that's what fork() does on modern machines.  vfork() was developed back
in the days when fork() actually did do a fully copy of the data segment,
the way it's done in elks.  This is a waste when you're forking for the
purpose of calling exec and waiting until the child exits.  From the (SunOS 4)
man page:

 vfork()  differs
 from  fork()  in  that the child borrows the parent's memory
 and thread of control until a call to execve(2V), or an exit
 (either  by  a  call  to exit(2V) or abnormally.) The parent
 process is suspended while the child is using its resources.


which basically means that the parent gets suspended, the child uses
the parent's data segment until the execve() call at which point a new data
segment is allocated for the child process and the parent is allowed to
resume.

Eric



RE: Implementing vfork

1999-07-27 Thread Matthew Kirkwood

On Tue, 27 Jul 1999, Greg Haerr wrote:

> : The parent process's data segment is not copied, just re-alloced, and
> : rather than returning to the parrent process, fork sleeps on the parents
> : child_wait wait queue.
> 
> Let me try to remind myself of the vfork semantics  Basically,
> rather than copying the data segment on real computers the OS sets the
> data segment page tables to copy-on-write, so that the data segment
> copy time and space is saved, since it's all going to be replaced by
> the following exec, right?

Not quite.

It was, I think, initially implemented for machines which couldn't
do any form of COW for memory accesses.  The semantics are somewhat
ugly, but the intention (and where it is most useful) is that the
parent sleeps until the child exec()s, and that no copying takes
place.

Data changes which the child makes may or may not be available to
the parent when it wakes up, but usually are.

> : All this seems to work just fine, but to make sure the parents stack
> : does not get corrupted by the child before it execs, I make a copy
> : of the bottom 100 bytes on the stack, below the stack, for the child to
> : use.

I believe that this should be unnecessary.  It should suffice
to fix vfork() and exec() so that they don't trash the stack
(if that's what's actually happening).

Matthew.



RE: Implementing vfork

1999-07-27 Thread Greg Haerr

 
: The parent process's data segment is not copied, just re-alloced, and
: rather than returning to the parrent process, fork sleeps on the parents
: child_wait wait queue.

Let me try to remind myself of the vfork semantics  Basically,
rather than copying the data segment on real computers the OS sets
the data segment page tables to copy-on-write, so that the data segment
copy time and space is saved, since it's all going to be replaced by the
following exec, right?

What do you mean when you just re-alloc the parent data segment?
You mean you increment the use count?

: All this seems to work just fine, but to make sure the parents stack
: does not get corrupted by the child before it execs, I make a copy
: of the bottom 100 bytes on the stack, below the stack, for the child to
: use.
: 
Some programs are compiled with -H to use a very small data/stack
segment.  There is the chance that there won't be the stack available
for this.  Are you concerned that if the child executes a return it will
pop too much off the stack, or is it actual corruption you're worried about?

Greg