-
Notifications
You must be signed in to change notification settings - Fork 6.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tests: Increase a couple of test stack sizes #58459
tests: Increase a couple of test stack sizes #58459
Conversation
mem_protect and sprintf stacks both need to be slightly larger than currently defined in order to avoid stack overflow when using picolibc. Signed-off-by: Keith Packard <[email protected]>
8d56724
to
45dbb8e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpicky whine: do we have a handle on exactly where the additional space is being used? Zephyr used to be extremely concerned about stack depth (thus the tests still in the tree expecting to run in ~512 bytes!). It's becoming less so as the years progress, but it's still sort of a design concern.
Basically: is this something we can fix, or is picolibc printf (or whatever) just inherently more stack hungry?
Yeah, I haven't spent a lot of time looking at the issues here. Back in the early days of integrating picolibc into Zephyr, I discovered a pile of stack overflow issues that only manifested because of picolibc's use of TLS. All of the stacks would be allocated in adjacent chunks of memory. When a stack overflow occurred, it would often end up writing over the upper parts of another stack segment. Before TLS started storing critical data there, this was remarkably benign as quite a bit of the upper reaches of the stack were filled with stuff that didn't matter. Makes me wonder if some of these newer overflows aren't caused by something other than the picolibc code itself. We've got several things that change when we switch from the minimal C library to picolibc:
Yes, when compiled with its full C standard conformance glory, picolibc's printf likely uses more stack space, especially when printing floating point numbers. One large consumer is likely to be the support for POSIX positional parameters ("%1$d"). That requires another copy of the va_list value so printf can rewind the argument list to go backwards if necessary. On x86, that's completely trivial. On aarch64 (and any arch using register param passing), it's a pile of stack memory. It's also not a part of any C standard, and it's optional in the picolibc build, but currently enabled by default. We could disable this for the integer-only printf version in the SDK, and maybe disable it by default for the module. Other than that, there are stack usage differences due to how various functions are implemented underneath the Zephyr API surface. picolibc adds a FILE structure to the stack when calling printk. If you call printf directly, you save that by using the shared stdout FILE instead. Similarly, the picolibc implementation of cbprintf uses a chunk of stack memory to pass the callback data through picolibc. I'd suggest disabling the positional parameter stuff and then doing some stack usage measurements to see how things compare. |
#define KERNEL_ONLY_THREAD_STACK_SIZE (ROUND_UP(1024, CONFIG_MMU_PAGE_SIZE)) | ||
#else | ||
#define KERNEL_ONLY_THREAD_STACK_SIZE (512) | ||
#define KERNEL_ONLY_THREAD_STACK_SIZE (1024) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... the thread is a very simple. It "shouldn't" need much stack space.
Ok, time for some data. The test which used more than 512 bytes of stack was on qemu_riscv32_smp. https://github.com/keith-packard/zephyr/tree/measure-stack I instrumented tests/kernel/mem_protect/userspace/src/main.c:kernel_only_thread_entry to measure stack usage by filling the unused stack with a sentinel value (0xbaadf00d) and then checking to see how much of that value remained in the stack after calling various functions. Here's how much stack was in use after each step: picolibc from SDK 0.16.2-rc1-1-gdc0ab61
picolibc module
minimal C library
Rather unexpectedly, picolibc ends up using less stack space than the minimal C library for this test, both when used from the SDK and when built as a module with the application. Looking a bit further at the printk call stack under picolibc all the way down to the bottom of the uart driver, we see:
So, the task isn't using too much stack. However, when it tries to invoke
At this point, the stack pointer is 16 bytes below the bottom of the stack, and it still hasn't finished running. It ends up writing all over a bunch more memory. I'm not sure how this test passes when running the minimal C library; it also overwrites memory, writing lots of garbage into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's good enough for me. FWIW if user_mode_enter() is a stack bomb like that we should probably file a bug to track it and maybe see if we can mitigate it somehow. This obviously is happening before memory protection is enabled for the region, and thus user stack sizing is likely to be stingy (though on MMU architectures it's generally rounded up to 4k pages) in the expectation that protection is available.
yeah, whereever it took the fault was using quite a bit of stack already. Of course, taking a fault there was an error, not a normal condition, so it might not be as much of a problem with correct code? In any case, we've exonerated picolibc's printf for overuse of the stack, which was a more positive outcome than I had expected. It might be good to see what this looks like on aarch64 where va_list is a lot heavier weight. Although, I guess it's just three pointers and two ints, so it might not be all that terrible. Somehow I had convinced myself that you'd end up copying all of the saved register values too, and that won't happen. |
mem_protect and sprintf stacks both need to be slightly larger than currently defined in order to avoid stack overflow when using picolibc.