Subject: Re: signal delivering and stack frame
To: Emmanuel Dreyfus <p99dreyf@criens.u-psud.fr>
From: Nathan J. Williams <nathanw@MIT.EDU>
List: tech-kern
Date: 01/12/2001 11:36:38
p99dreyf@criens.u-psud.fr (Emmanuel Dreyfus) writes:

> The process makes a system call. It creates a trapframe on the stack,
> and enters the kernel.

See below, but that's not quite right. The process makes a system
call, and the CPU and kernel construct the trap frame.

> The kernel sees that there is a pending signal, so prior to process
> return to user mode, it modify the trapframe on the stack, so that the
> process will jump to the signal handler on return to userland. It also
> sets up a signal frame, which contains the trampoline code (used to do
> the actual jump to the signal handler), and other information.

Pretty close. The trampoline code is already present in the program's
address space (copied there by exec()); the trapframe is set to jump
to that, which then jumps to the signal handler. 

> First question: what is the signal stack? I assume this must be an
> alternate stack used to do the signal deleivering, but why it is used?
> Is it a way of avoiding stack execution (and therefore avoid a class of
> buffer overflow problems by setting stack pages as unexecutables)?

A program can tell the kernel that it wants to use a separate signal
stack for a handler by setting the SA_ONSTACK bit when it calls
sigaction(2). The stack itself is registered with
sigaltstack(2). There are a variety of uses for the separate signal
stack; one is when dealing with multithreaded programs than have a lot
of odd stack management anyway.

> Before returning, as far as I understood, the stack looks like this:
> (upper addresses are on bottom)
> 
>                    lower addresses
>                   | nothing      |
> stack pointer --->|--------------|
>                   | signal frame |
> frame ----------->|--------------|   (X)
>                   | trap frame   |
>                   |--------------|  
>                   higher addresses
> 
> (The frame pointer is used in the sendsig function)

The trap frame isn't present in userlevel code. It's generated at
trap/system call time, and disappears (or is consumed by the CPU) when
the boundary is crossed back into user-level code. 

> Another question about LINUX_COMPAT: In the alpha and i386 versions, the
> trapframe is assumed to be BSD style. I expected it to be Linux style,
> since it is the Linux executable that did set it up. Why is it BSD
> style?

The trap frame is set up by the CPU and by the kernel at the
trap-entry code. See, for example, src/sys/arch/i386/i386/locore.s,
line 2359, where the syscall entry code uses the INTRENTRY macro to
dump registers onto the stack.

Also note that the trap frame is constructed on the kernel's stack,
not the user program's stack.

> The signal frame has to be Linux style for Linux binaries, since it is
> used by the binary.

Yes. The signal frame is set up by calling a process's
p->p_emul->e_sendsig function; that is, it goes trough a function
pointer in the emulation structure. For a normal BSD binary, the emul
structure is the standard netbsd emul structure and the e_sendsig
would be the sendsig() found in (still using the i386 as an example)
arch/i386/i386/machdep.c. For a Linux binary, the emul pointer points
to a structure with Linux emulation information, such as a pointer to
the linux_sendsig() function and the Linux signal trampoline code. 

> When the signal handler returns, it returns to the trampoline code,
> which in turns calls the sig_return system call. This system call
> restores registers according to what was saved in the signal frame, and
> it reset the stack pointer to the (X) on the above picture, which is
> where the frame pointer points.

More specifically, is uses the data in the struct sigcontext in the
signal frame, but yes. The stack pointer is reset to that location
because that value was saved into the sigcontext. A program can
manipulate that sigcontext and cause the program to return to another
stack (and another instruction, by modifiying the saved PC). 

> Last question: in Linux/PowerPC, the signal frame is setup like this
> (upper addresses on the bottom)
> 
> - one or more blocks of (struct sigcontext, a gap of 64 bytes)
> - a sigcontext structure.

Well, it's possible for a signal handler to be interrupted by another
signal (if the signal masks are set up to permit it), which is passed
to another signal handler, which would cause multiple signal frames to
exist on the stack. This doesn't particularly have anything to do with
interrupted system calls.

I hope this helps.

        - Nathan