-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
MISRA C Avoid dynamic memory allocation #9892
Comments
@andyross This is not a trivial piece of code, can we check how much work is required to fix it ? |
So... alloca() is allocating memory "dynamically", but on the stack. It won't fail in the sense of malloc() where someone else is using the memory. The only failure case is a stack overflow, which is something that is always possible. What exactly does MISRA say? Note that there are a few other spots in our codebase where we're doing equivalent allocations via variable-sized arrays (e.g. int foo[some_runtime_value];) which are likely to need attention too. There's no good workaround. The "fix" would be to always allocate a maximally sized block (which isn't huge -- probably hundreds of bytes) I guess. Maybe we could put this under a CONFIG_MISRA_PEDANTRY flag or something so that normal apps can use the appropriate tool. But really I'd hope that stack allocation would be possible in conforming MISRA code. I mean... fundamentally it's no different than recursion in terms of expression and safety. |
yep, it's allocating on stack but still dynamic. MISRA dooms dynamic allocation and everything should be pre-allocated. Btw, variable length arrays and alloca are different, they have different scope, memory allocated by alloca is release only when the function exits while the former is when the block exits. |
sorry, VLAs are also not allowed. Dammit, we may have to define one pattern for these cases and maybe create a deviation. Anyway, between alloca() and VLAs I stand to VLAs, at least is covered by standard and we have less chance to find problems with different compilers. |
Dir 4.12 and rule 21.3 |
FWIW: everywhere I'm aware of this is being done, the memory usage isn't unbounded (in fact it's generally log-sized in whatever the relevant data structure is). It's possible to decide on a tractable maximum size upfront and simply use that, obviously at the cost of significantly larger stacks for the common case. |
MISRA-C prohibits recursion, but you could probably have guessed this already. Personal opinion: It seems silly to rely on dynamic memory to implement dynamic memory. |
FWIW, these are the places VLAs are currently used:
|
An alloca()/VLA is hundreds of cycles faster than malloc() on most architectures (often more on some workloads if you factor in long term costs from fragmentation). In general it's implementable via a single subtraction from the stack pointer. It's absolutely the right way to solve problems like this where you have a need for small-ish amounts of non-constant-size memory that will affirmatively live only for the duration of a function call. That's why it's in the language, after all. That said: the mempool and rbtree uses are mine, and those can be bounded well enough such that we can use the same code with a tolerable cost paid in stack sizing. I strongly suspect the same is true of tinycrypt (where it's likely to be a buffer sized according to algorithm and thus has some reasonable maximum). No idea about the flash thing. |
@andyross will this be done for 1.14 or should I change the milestone to future ? |
In some form, sure. Worst case we can add maximally-sized iteration arrays somewhere (on the stack, or globally with appropriate locking, etc...). (Edit: for clarity, that's talking about usage in the kernel. I don't think the tinycrypt and subsys/fs directories are in the line of fire for MISRAfiation in 1.14, right?) |
MISRA rules (see zephyrproject-rtos#9892) forbid alloca() and family, even though those features can be valuable performance and memory size optimizations useful to Zephyr. Introduce a MISRA_SANE kconfig, which when true enables a gcc error condition whenever a variable length array is used. When enabled, the mempool code will use a theoretical-maximum array size on the stack instead of one tailored to the current pool configuration. The rbtree code will do similarly, but because the theoretical maximum is quite a bit larger (236 bytes on 32 bit platforms) the array is placed into struct rbtree instead so it can live in static data (and also so I don't have to go and retune all the test stack sizes!). Current code only uses at most two of these (one in the scheduler when SCHED_SCALABLE is selected, and one for dynamic kernel objects when USERSPACE and DYNAMIC_OBJECTS are set). This tunable is false by default, but is selected in a single test (a subcase of tests/kernel/common) for coverage. Note that the I2C and SPI subsystems contain uncorrected VLAs, so a few platforms need to be blacklisted with a filter. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
MISRA rules (see #9892) forbid alloca() and family, even though those features can be valuable performance and memory size optimizations useful to Zephyr. Introduce a MISRA_SANE kconfig, which when true enables a gcc error condition whenever a variable length array is used. When enabled, the mempool code will use a theoretical-maximum array size on the stack instead of one tailored to the current pool configuration. The rbtree code will do similarly, but because the theoretical maximum is quite a bit larger (236 bytes on 32 bit platforms) the array is placed into struct rbtree instead so it can live in static data (and also so I don't have to go and retune all the test stack sizes!). Current code only uses at most two of these (one in the scheduler when SCHED_SCALABLE is selected, and one for dynamic kernel objects when USERSPACE and DYNAMIC_OBJECTS are set). This tunable is false by default, but is selected in a single test (a subcase of tests/kernel/common) for coverage. Note that the I2C and SPI subsystems contain uncorrected VLAs, so a few platforms need to be blacklisted with a filter. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
Merged the VLA patch for the kernel, which I think is the scope here. There are a few spots in the I2C and SPI drivers that can be hit (as failures -- we error on VLA usage now) by trying to build the full test suite with CONFIG_MISRA_SANE. We can address those in specific bugs as needed. |
Zephyr's kernel is using rb tree defined in rb.[ch]. This algorithm is using alloca() function to allocate memory using stack. If the allocation fails the behaviour is undefined.
part of: #9552
The text was updated successfully, but these errors were encountered: