-
Notifications
You must be signed in to change notification settings - Fork 31
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
ConfigurationBlobFlash::BlobIsValid() could give the wrong result #91
Comments
I would rather suggest replacing that template <class To, class From>
std::enable_if_t<
sizeof(To) == sizeof(From) &&
std::is_trivially_copyable_v<From> &&
std::is_trivially_copyable_v<To>,
To>
// constexpr support needs compiler magic
bit_cast(const From& src) noexcept
{
static_assert(std::is_trivially_constructible_v<To>,
"This implementation additionally requires "
"destination type to be trivially constructible");
To dst;
std::memcpy(&dst, &src, sizeof(To));
return dst;
} |
I don't think converting all instances where we violate the strict aliasing rules to use std::bit_cast or memcpy gives satisfactory results. In lots of cases, we have an object of which the contents is read from flash or spi or something like that:
Using either bit_cast or memcpy in the flash driver's ReadBuffer would mean that first we need an additional buffer of sufficient size, read the contents from flash into that buffer, and then copy that into headerPrologue. That additional buffer is a real problem: its size is unknown up front, and it might need to be pretty big, depending on what kind of object we are reading from flash. It might be a bunch of 4kB certificates. If I understand the strict aliasing rules correctly, then we cannot use uint8_t* because when writing via such a pointer the compiler does not need to acknowledge the possibility that the original object is modified. However, using char, unsigned char, or std::byte seems to be allowed. I read these relevant portions of the standard:
So my expectation is that when we use std::byte instead of uint8_t for all our byte-accesses, that should solve the normal part of violating strict aliasing rules. Open question: Does this solve accesses via DMA as well? What if flash's ReadBuffer is implemented with DMA()? The DMA is then initialized with a std::byte*, and after the DMA finishes we execute a __DMB(). I sincerely hope that compilers will accept that construct. @zhmu What do you think of this? |
The compiler isn't required to do the actual I think using On a sidenote, as long as Finally, since |
I'm using gcc 9.3.1 for arm, as bundled with STM32CubeIDE 1.6.1 (gcc version 9.3.1 20200408 (release) (GNU Tools for STM32 9-2020-q2-update.20201001-1621)). When trying to build with
-O3 -Werror
, I get a warning regarding the following code:where the warning is:
I believe this warning to be correct and the code to be wrong: by using
infra::MakeByteRange
,header
will be cast to anuint8_t*
, which will be used byinfra::Copy
(which seems to callstd::copy
internally) to fill the byte range.However, due to the strict aliasing rule, the compiler is free to assume that the
uint8_t*
being written does not overlap withheader
as they have different types. I suspect it might even be legal to optimize away the copy completely as there are no observable side-effects within the rules of the language.My recommendation would be to simply do the following:
std::memcpy( &header, blob.begin(), sizeof( header ) );
which will yield the desired result with no risk of causing troubles.
(There are more constructs like these, it may be worthwhile to build with
-O3 -Werror
to locate them)The text was updated successfully, but these errors were encountered: