Some updates on the topic of hardening via CFLAGS.
The explorations reported on the 2022-12-22 entry of this phlog
turned out to be successful, in that I managed to enable stack
smashing protection (-fstack-protector and variants) under a
bare-metal build. I updated the 2022-12-22 entry with my
findings about -fstack-protector.
The -D_FORTIFY_SOURCE setting was lately discovered to be enabled
by default in the toolchain configuration, which would explain
why I could not spot any difference between a build where it was
enabled, and a build where it was not. It is still not clear if
the -D_FORTIFY_SOURCE accomplished anything on our build, given
that I couldn't appreciate any additional safety until
-fstack-protector was enabled.
My working implementation is currently using the "terminator
canary", which is a well known constant canary value. A constant
canary is not as robust as a random canary, but it provides a
certain degree of protection (plenty of details can be found
online). A constant canary has the advantage of being extremely
simple to implement.
Today a colleague suggested to use a randomised canary, so I've
spent some additional time in refining my implementation.
The newlib implementation can optionally use a randomised canary,
but it relies on features that are not available on our
bare-metal firmware.
We do have a (hardware) random number generator which we could
use for the purpose, but it needs to be initialised before we get
to use it. Unfortunately, assigning the canary after the C
runtime has been set up breaks the stack smashing detection
mechanism. Any return from a function is detected as a smashing.
This comes with no surprise: I had a clue when I noticed that
newlib assigns a random value to the canary within function
marked as '__attribute__((constructor))'
I don't believe that initialising the hardware from crt0 is a
good idea :D