Query Regarding Stack-Out-of-Bounds Error

Muni Sekhar munisekharrms at gmail.com
Sat Aug 31 13:19:15 EDT 2024


On Tue, Aug 27, 2024 at 1:16 AM Valdis Klētnieks
<valdis.kletnieks at vt.edu> wrote:
>
> On Mon, 26 Aug 2024 18:04:39 +0530, Muni Sekhar said:
>
> > static struct cmd_info *find_cmd_entry_any_ring(struct intel_gvt *gvt,
> >                unsigned int opcode, int rings)
> > {
> >         struct cmd_info *info = NULL;
> >         unsigned int ring;
> >         ...
> >         for_each_set_bit(ring, (unsigned long *)&rings, I915_NUM_ENGINES) {
> >
> > In the above code, a 32-bit integer pointer (rings) is being cast to a
> > 64-bit unsigned long pointer, which leads to an extra 4 bytes being
> > accessed. This raises a concern regarding a stack-out-of-bounds bug.
> >
> > My specific query is: While it is logically understandable that a
> > write operation involving these extra 4 bytes could cause a kernel
> > crash, in this case, it is a read operation that is occurring.
>
> Note that 'ring' is located in the stack frame for the current function. So to
> complete the analysis - is there any way that the stack frame can be located in
> such a way that 'ring' is the *very last* 4 bytes on a page, and the next page
> *isn't* allocated, *and* I915_NUM_ENGINES is big enough to cause the loop to walk
> off the end?
>
> For bonus points, part 1:  Does the answer depend on whether the architecture
> has stacks that grow up, or grow down in address?

Here’s an example stack frame for context:

|---------------------------|
| Return Address |
|---------------------------|
| Saved Frame Pointer |
|---------------------------|
| Parameter: gvt |
|---------------------------|
| Parameter: opcode |
|---------------------------|
| Parameter: rings |
|---------------------------|
| Local Variable: info |
|---------------------------|
| Local Variable: ring |
|---------------------------|

Stack Growth Downwards:
If the stack grows downward in address space, is there any scenario
where the stack frame could be positioned such that the return address
is located at the very last 4 bytes of a page, the next page isn't
allocated, and I915_NUM_ENGINES is large enough to cause the loop to
walk off the end? If this happens, would it result in a
stack-out-of-bounds error leading to a kernel crash?

Stack Growth Upwards:
Conversely, if the stack grows upward, could the stack frame be
positioned in such a way that the 'ring' variable is at the very last
4 bytes of a page, and the next page isn’t allocated, with
I915_NUM_ENGINES being large enough to cause the loop to walk off the
end? Would this also result in a stack-out-of-bounds error causing a
kernel crash?

I'm curious as to why the likelihood of this error seems to depend on
whether the architecture has stacks that grow up or down in address
space. In both scenarios stack-out-of-bounds error behaves the same,
right?

>
> For bonus points, part 2: can this function be called quickly enough, and
> enough times, that it can be abused to do something interesting to L1/L2 cache
> and speculative execution, because some systems will fetch not only the bytes
> needed, but as much as 64 or 128 bytes of cache line?  Can you name 3 security
> bugs that abused this sort of thing?
Where should I look to find more details about these security bugs?
I appreciate your insights on these questions.
>
> Free hint:  There's a bit of interesting code in kernel/exit.c that tells you if
> your system has gotten close to running out of kernel stack.
>
> [/usr/src/linux-next] dmesg | grep 'greatest stack'
> [    1.093400] [     T40] pgdatinit0 (40) used greatest stack depth: 13920 bytes left
> [    3.832907] [     T82] modprobe (82) used greatest stack depth: 8 bytes left
>
> Hmm... wonder how that modprobe managed *that* :)
>
>
>


-- 
Thanks,
Sekhar



More information about the Kernelnewbies mailing list