cpointersundefined-behaviorc99misra

Undefined behavior with pointer casts in C99 and MISRA C:2012


I'm working on a C99 project that requires us to follow the MISRA C:2012 standard.

Rule 11.3 essentially prevents from casting a pointer between different object types without declaring a justified exception an approved deviation (in accordance with MISRA Compliance).

The reasoning behind this seems to be to prevent undefined behavior in case of

  1. misaligned pointers
  2. accessing an object with a "wrong" type (refers to C99 6.5 paragraph 7)

It looks to me like point 2 is just about the strict aliasing rule (at least the footnote on the referenced C99 paragraph indicates this).

I would argue that both do not matter in this particular project because...

  1. the underlying processor (Cortex-M7) can handle unaligned access
  2. type-based alias analysis and related strict alias optimization is disabled

Am I missing some potentially undefined behavior or is this a valid justification to violate this rule?


edit:

I agree with you that it's code smell and it breaks portability. Moreover, I see that depending on how and where the memory access is done, alignment issues could still be possible.

But just to add a bit of context: I'm dealing with code that already exists and just has a very poor justification for this deviation. Changing it because of code smell isn't really an option due to the process overhead involved. But if there's a real problem, e.g. due to undefined behavior, that would be a different thing. If I were completely free to rewrite the code, I would definitely want to write it in a way without breaking this rule.


Solution

  • The main reasons not to allow such casts are misalignment and "strict aliasing" indeed, both invoking undefined behavior. We can tell since on top of rule 11.3, there's the cryptic note

    C11 [Undefined 25, 37]

    This is a strange, somewhat fishy MISRA thing where you are supposed to open up Annex J of ISO C11 and count in the summary list of UB there.

    (Which is questionable since Annex J is informative and also known to be filled with mistakes that never get corrected, many sitting there since C99. A lot of Annex J states that something in is UB and then points to completely irrelevant normative parts that do not justify or even mention the supposed UB at all. Annex J is not to be trusted - I can dig up a bunch of various mistakes in it if prompted.)

    "Undefined 25" being misalignment UB, and "Undefined 37" being effective type/strict aliasing UB.

    Notably rule 11.3 has an exception for a conversion to (but not from) a pointer to character type, because then neither alignment nor strict aliasing apply.


    Is it valid to deviate?

    In embedded systems there are some rare oddball situations where wild & crazy pointer casts may be motivated. Flash/eeprom drivers for example might require that you write C which boils down to 16 bit or 32 bit access, based on an incoming byte stream.

    Another valid reason to deviate from the rule would be object pointer conversions from the first member of a struct to/from a pointer to that struct, which is well-defined by C and a common use-case, yet not listed as an exception in 11.3.

    Other situations could be when implementing stuff like memcpy or memory pools in C and you need to type pun from character type to a larger type.


    the underlying processor (Cortex-M7) can handle unaligned access

    Yes, no, maybe? I'm not an ARM assembler expert but as noted in comments, M7 only allows unaligned access in some situations and we have no control over which assembly instructions that get generated from C. So if you mean to take advantage of the instructions in the link, you need to write inline assembler in which case C language rules and MISRA C are out of scope.

    type-based alias analysis and related strict alias optimization is disabled

    The amount of possible alias analysis that you can disable varies a lot depending on compiler. It might be hard to make a case stating "yes I have definitely disabled all of it". Sounds like mission impossible if using gcc, for example.

    Am I missing some potentially undefined behavior or is this a valid justification to violate this rule?