Discussion:
[ORLinux] Initialization of the initial kernel stack pointer
Joël Porquet
2013-11-14 09:33:20 UTC
Permalink
Hi guys,

I recently asked the following question on the openrisc IRC channel,
and I was told I should post it on the ML, so here I am!

So I'm actually afraid there might be a possible (somewhat small) leak
from kernelspace to userspace.

Basically, in kernel/head.S, the kernel stack pointer is initialized
at the exact top of the kernel stack (8KiB above init_thread_union):

LOAD_SYMBOL_2_GPR(r1,init_thread_union+0x2000) // setup kernel stack

Later, in kernel/process.c:copy_thread(), when creating the first
userspace task (forked from the init_thread), there is copy of
pt_regs:

*userregs = *current_pt_regs();

Knowing that current_pt_regs() points on a pt_regs structure that is
supposed to be at the top of the kernel stack (of the thread that is
being forked), isn't there a sort of security breach?

In that case, current_pt_regs() points on (random) data that are part
of the first stackframe (ie the stackframe of
init/main.c:start_kernel()).

On other architectures (well, at least MIPS), the kernel stack pointer
is decreased right away by a stackframe and a pt_regs structure:

PTR_LA $28, init_thread_union
/* Set the SP after an empty pt_regs. */
PTR_LI sp, _THREAD_SIZE - 32 - PT_SIZE
PTR_ADDU sp, $28

I might be wrong, but from my understanding, I think that the kernel
stack pointer should also leave some empty space on top of the kernel
stack. It could look like (INT_FRAME_SIZE is already defined by
asm-offset.c):

LOAD_SYMBOL_2_GPR(r1,init_thread_union + 0x2000 - INT_FRAME_SIZE)
// setup kernel stack

Hope it helps!
Best,

Jo?l
Jonas Bonn
2013-11-14 11:27:01 UTC
Permalink
Hi Joel,

Good question! Glad to see you are putting some thought into this!
Post by Joël Porquet
Hi guys,
I recently asked the following question on the openrisc IRC channel,
and I was told I should post it on the ML, so here I am!
So I'm actually afraid there might be a possible (somewhat small) leak
from kernelspace to userspace.
Basically, in kernel/head.S, the kernel stack pointer is initialized
LOAD_SYMBOL_2_GPR(r1,init_thread_union+0x2000) // setup kernel stack
Later, in kernel/process.c:copy_thread(), when creating the first
userspace task (forked from the init_thread), there is copy of
*userregs = *current_pt_regs();
userregs is a _copy_ of the current userspace regs; it's not a pointer
into another process' kernel stack frame.
Post by Joël Porquet
Knowing that current_pt_regs() points on a pt_regs structure that is
supposed to be at the top of the kernel stack (of the thread that is
being forked), isn't there a sort of security breach?
In that case, current_pt_regs() points on (random) data that are part
of the first stackframe (ie the stackframe of
init/main.c:start_kernel()).
On other architectures (well, at least MIPS), the kernel stack pointer
PTR_LA $28, init_thread_union
/* Set the SP after an empty pt_regs. */
PTR_LI sp, _THREAD_SIZE - 32 - PT_SIZE
PTR_ADDU sp, $28
I might be wrong, but from my understanding, I think that the kernel
stack pointer should also leave some empty space on top of the kernel
stack.
Why?

For a new process you have the following on the stack:

|----------------|<- top of stack
| userspace regs |
|----------------|
| kernel regs |
|----------------|
| ~~~~~ | <- stack pointer points here

When the new process is "run", it returns to its kernel context with the
kernel regs loaded. It then returns to userspace with its userspace
regs loaded. If the new process is a kernel thread that won't ever go
to userspace then the userspace regs part of the stack may be empty.

What else do you need?

It could look like (INT_FRAME_SIZE is already defined by
Post by Joël Porquet
LOAD_SYMBOL_2_GPR(r1,init_thread_union + 0x2000 - INT_FRAME_SIZE)
// setup kernel stack
The kernel has its own stack. The redzone (INT_FRAME_SIZE) doesn't
apply when saving the userspace regs. It does, however, apply if the
kernel is preempted by an interrupt.

Best regards,
Jonas

Loading...