5 ms·
Author here. Firmware, for better and worse, is stuck in C/C++ land, so many of the topics here are essential to keep a complex, multi-MCU, multi-board setup in
by tyhoff 6y ago
Author here. Firmware, for better and worse, is stuck in C/C++ land, so many of the topics here are essential to keep a complex, multi-MCU, multi-board setup in working order.
I'm actually really curious what you all do in C/C++ to prevent bad operations from ever being performed in the first place.
For example, should we just change our internal malloc / free to use double pointers instead?
bool malloc(size_t n, void **ptr) {
*ptr = <memory>
}
void free(void **ptr) {
... <free>
*ptr = NULL
}
This way, if anyone actually tries to use that pointer, it will crash the system (hardfault), instead of potentially using corrupted memory, which IMO is much worse.
- ffffjfjjfj 6y agoThis gives a false sense of security, since the most common use-after-free bugs are if you kept a pointer around. Your method doesn't stop things like this: void *p1, *p2; malloc(100, &p1); p2 = p1; ... free(&p1); *p2; // oops! Obviously its trivial in an example like this, but when p2 leaves the lexical scope then there's a problem.
- deleted 6y ago[deleted]
- btrask 6y agoIf you are transferring ownership, you would do p2 = p1; p1 = NULL; However if you are intentionally doing multiple ownership, then yes you can still have problems.
- dragontamer 6y ago> I'm actually really curious what you all do in C/C++ to prevent bad operations from ever being performed in the first place. 1. Go as far as you can with static memory. Don't call the allocator: its slow, sometimes requires inter-thread communications. 2. Dynamic uses often can use the stack: almost always in L1 of your local core. Stack allocations are self-cleaning, just don't pass those pointers to anyone else. 3. unique_ptr<blah> covers most of the true dynamic issues. 4. shared_ptr<blah> covers the rest of them. --------- The above advice is just general-purpose / low-performance code. If you're entering high performance coding (aka: you're actually worried about fragmentation, memory sizes, and such), then things get more complicated. Embedded is often RAM-restricted, so you end up in this complicated place where you need to think of highly-efficient techniques. That's almost always a complexity / resource tradeoff. Efficient techniques are just innately more complex than general purpose techniques. Try not to reach for efficient coding unless you know its something you need to do
- saagarjha 6y ago> Dynamic uses often can use the stack But they should also be very careful when deciding when to use the stack. Uncontrolled input + alloca = a bad time.
- mkoubaa 6y agoOne thing I try to do in c++ is pass things by reference in lower level libraries so that client code has to be defensive but library code assumes non null.
- _0ffh 6y agoThere is at least the option to use a more modern language if it translates to C. Nim for example does that, and has compiler options for embedded systems. https://nim-lang.org/docs/nimc.html#nim-for-embedded-systems https://nim-lang.org/docs/nimc.html#nim-for-embedded-systems
- lmm 6y ago> This way, if anyone actually tries to use that pointer, it will crash the system (hardfault), instead of potentially using corrupted memory, which IMO is much worse. Oh you sweet summer child. Null pointer deference is undefined behaviour, the compiler will happily turn it into corrupting memory under the right circumstances.
- tyhoff 6y ago> Oh you sweet summer child Should have clarified. On the embedded systems I work on, generally Cortex-M4, you set an MPU region on address 0x0 and it turns into a hardfault, so it's pretty easy to catch.
- saagarjha 6y agoA good optimizing compiler can remove the dereference entirely if it notices, unfortunately.
- sigjuice 6y agoHow big is this MPU region? Pointers to large structs can easily allow access past the protected region.
- lmm 6y agoIf you were writing assembly that would be reliable, but in C it isn't. The compiler knows that NULL dereferences are undefined behaviour and so won't necessarily compile them into memory accesses.
- btrask 6y agoYeah, in C you need to use assertions (or simple checks) for things that might be null. That said the compiler isn't infinitely smart (thank god) and complex null derefs will "safely" make it to runtime. (What a sad world we're in.)
- elygre 6y agoIt is undefined by the standard, a compiler is thus free to make null pointer references into hard faults. You need to know your tool chain and your environment.
- sgtnoodle 6y agoFor MCU code, it's best practice to statically allocate all memory. If you do need dynamic allocation, it's better to use either fixed size pool allocators, or allocate only pools. For performance critical code running on a more powerful system with an OS, it's still good to stick with static allocation, or malloc and free only during initialization time. Over the last decade I've written firmware for space ships, self driving cars, and autonomous aircraft, and I've never needed more than an allocate only pool or an O(n) LRU cache data structure. I've written plenty of software that was hard on the heap, but rarely does memory need to be freed during runtime, and it's rare to need raw pointers in C++ when writing such code. Ideally, your "firmware" is written in a way that abstracts away the target hardware enough that it's basically just weird software. Then, you can unit test it, and even run linters and memory sanitizers on it.
- tutfbhuf 6y agoWhy is it stuck in the C/C++ land? I think if you can write a firmware in C++, then it should be possible to write it in rust, too. Am I wrong?
- steveklabnik 6y agoIt is very possible to write firmware in Rust, as long as the device is using a chip that’s supported.