1

I am trying to get rid of Rule 11.3 from my code.

Sample code:

static int32_t 
do_test(const char *cp)
{
    const char *c = cp;
    const int32_t *x;
    x = (const int32_t *)cp;

    return *x;
}

I want the value of *c and *x to be same. Even-though the code is compiling and giving the correct value, "x = (int32_t *)cp;" causing violation of 11.3 and 11.8

Rule 11.3 violation: An object with pointer type shall not be converted to a pointer to a different object type.

I have tried with void pointer, but the result was not same as what I expected and also it resulted in additional violation.

Is there anyway to remove these violations ?

From MISRA C 2012 Document they are mentioning like there is an exception for this rule as it is permitted to convert a pointer to object type into a pointer to one of the object types char, signed char or unsigned char. The Standard guarantees that pointers to these types can be used to access the individual bytes of an object.

Ignore Dir 4.6 due to char type.

Salim
  • 373
  • 1
  • 9
  • 19
  • 1
    Well you can fix 11.8 simply by making it a cast to `const int32_t *`. – Oliver Charlesworth Dec 03 '17 at 11:01
  • 1
    Perhaps you could write as `static int32_t do_test(const void *cp) { const int32_t *x = cp; return *x; }`? – P.P Dec 03 '17 at 11:03
  • I tried with void pointer. static int32_t do_test(const char *cp) { const char *c = cp; const int32_t *x; const void *vp = c; x =vp; return *x; } Code is performing as expected and not violating 11.3 but it is violating rule 11.5 now for "x = vp; " . Rule 11.5 violation: An pointer to void type "vp" shall not be converted to pointer to object. Anyway to get rid of this violation ? – Salim Dec 03 '17 at 11:36
  • From MISRA C 2012 Document they are mentioning like there is an exception for this rule as it is permitted to convert a pointer to object type into a pointer to one of the object types char, signed char or unsigned char. The Standard guarantees that pointers to these types can be used to access the individual bytes of an object. – Salim Dec 03 '17 at 18:57
  • When you say "I want the value of `*c` and `*x` to be same" what do you want to happen if `c` points to something like: `"\x01\x02\x03\x04"`? – Michael Burr Dec 03 '17 at 19:05
  • For eg let *c is 'a'. Then *x should be 97. Note here we typecast-ed the pointer location from char to int – Salim Dec 03 '17 at 19:08
  • Just change the parameter type to int32_t*. That solves the problem with this function, the compiler will now tell you *actual* code you need to work on. – Hans Passant Dec 03 '17 at 19:09
  • @HansPassant That will work fine for the sample code. But if I have a typecasting the pointer to int64_t later of the code, then again violation will arise. I need a generic solution for this. – Salim Dec 03 '17 at 19:21
  • If you just want the single byte pointed to `*c` to be an `int` then simply `return *c`. You don't need any pointer aliasing. – Michael Burr Dec 03 '17 at 19:34
  • @MichaelBurr That's what I mentioned like the pointer should be typecasted from char to int and the value should be retained. That is the primary intention of the sample code. – Salim Dec 03 '17 at 19:40
  • @Salim: what the code you posted will do is return the value of the 4 bytes pointed to by `c` as an `int`. That's a very different thing than returning the value of only the single byte pointed to by `c`. you need to clarify what you want. Given the example string in my first comment, so you want the function to return `1` or `0x04030201`? – Michael Burr Dec 03 '17 at 20:02
  • Almost duplicate of [What is the strict aliasing rule?](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule), although it doesn't address the MISRA-C aspect. – Lundin Dec 04 '17 at 10:07

3 Answers3

8

You are lucky you are using MISRA-C, because this code is full of bugs. You cannot make the bugs go away with a cast.

  • Bug 1. The character pointer is not necessarily aligned, in which case your code invokes undefined behavior as per the C standard 6.3.2.3/7:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

  • Bug 2. The code contains a blatant strict aliasing violation. This is always undefined behavior as per the C standard 6.5/7.

Your assumption "The Standard guarantees that pointers to these types can be used to access the individual bytes of an object." is correct: as a special exception C allows you to convert from a pointer-to-x to pointer to char and then access the data through the char poiner. But not the other way around.

Your code is not accessing individual bytes; you are going the other way around, from an array of characters to a 32 bit type. This is not allowed. See What is the strict aliasing rule?.


Correct code, that should be ok with both the C language and MISRA-C:

static int32_t do_test(const char *cp)
{
  return (int32_t) ( ((uint32_t)cp[0] << 24u) |
                     ((uint32_t)cp[1] << 16u) |
                     ((uint32_t)cp[2] <<  8u) |
                     ((uint32_t)cp[3])        );
}

This shift version is always preferred, as it is endianess independent and therefore portable. The casts to uint32_t are necessary to prevent implicit promotions on 8/16 bit systems, plus you should never do bit shift on signed types.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thanks Lundin!! While doing like this, it is resulting in violation of 18.1 where we cannot pointerr arithmetic like (cp + 3) – Salim Dec 04 '17 at 18:01
  • @Salim Sounds like a tool bug. Rule 18.1 is about not going out of bounds of an array. You can probably make the tool happy by changing the function to `const char cp[4]` although that is not required by MISRA-C:2012. Older versions of MISRA had a weird rule that would have applied, but it has been removed. – Lundin Dec 05 '17 at 07:38
  • And one more thing here anyway finally we are not able to store the int value to the char address right ? – Salim Dec 05 '17 at 09:19
  • @Salim I don't understand what you mean. – Lundin Dec 05 '17 at 10:00
  • @Salim Lundin's code is correct with regard to MISRA, but it can be both incorrect in logic and not necessarily fixing the problem, as there may be no problem at all. First, the code given only works for big-endian machines. On an Intel you will get the wrong value. Second, the function will only invoke undefined behaviour if the value you access wasn't defined with the type `int32_t`. Remember that MISRA is warning on a higher abstraction level than your implementation, therefore flagging constructs which are perfectly legal regarding the C standard. – Vroomfondel Dec 07 '17 at 16:59
  • "The character pointer is not necessarily aligned", what do you mean ? C clearly say any pointer can be convert into character type. What could be the alignement problem with something of size one ? "The code contains a blatant strict aliasing violation. This is always undefined behavior as per the C standard 6.5/7.", there can't be strict aliasing violation with character type unless there have restrict modifier. – Stargateur Dec 07 '17 at 19:23
  • @Stargateur the character pointer is surely correctly aligned, but what Lundin meant was that for it being converted to an `int32_t` the alignment could be illegal. – Vroomfondel Dec 07 '17 at 22:32
  • @Stargateur The character pointer may point at an address where an `int32_t` would be misaligned. Strict aliasing allows us to convert _from_ pointer-to-type to pointer-to-character-type but _not the other way around_. `int32_t` _is not_ a character type, it does not match any of the allowed exceptions in 6.5/7, which you should actually go and read. If you are going to down vote, then get your facts straight first. Further study: [What is the strict aliasing rule?](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule). – Lundin Dec 08 '17 at 08:35
  • @Lundin I didn't downvote, I just wanted to know why you say that it's undefined behavior. We do not know what is the true type of `cp` or maybe I miss this information. If `cp` is aligned to an `int32_t` the cast is not undefined behavior and you wrong about strict aliasing rule this can't happen between a pointer and a character type pointer, quotation from your link "You can use char* for aliasing instead of your system's word. The rules allow an exception for char* (including signed char and unsigned char). It's always assumed that char* aliases other types.". – Stargateur Dec 08 '17 at 08:40
  • @Stargateur No, you are wrong. Strict aliasing has to do with _lvalue object access_ and not just the pointer types. If you have a character pointer and access the contents at that address as if it was an `int32_t`, then: Is it a (qualified) type compatible with the effective type of the object? No. Is it the signed/unsigned (qualified) equivalent or a character type? No. Is it an aggregate or union? No, it is a scalar. Is it a character type? No it is an `int32_t`. **None of the exceptions apply** thus a normative "shall" in the standard is violated and the code invokes UB. – Lundin Dec 08 '17 at 08:47
  • @Lundin Well, we disagree then, but for me http://rextester.com/LIR39418 is clearly not undefined behavior. – Stargateur Dec 08 '17 at 08:55
  • @Stargateur You don't disagree with me, but disagree with the standard. That whole previous comment was taken straight from the standard. How exactly did I misquote it? Or do you know better than the standard? Yes the strict aliasing rule is very cumbersome and inconvenient, I personally think it is a poorly designed rule. But that's how the language is currently defined. – Lundin Dec 08 '17 at 12:15
  • @Lundin We disagree about the signification of the standard. For me the code snipped that I link you is not undefined behavior in the regard of C11 but I maybe wrong. – Stargateur Dec 08 '17 at 12:34
  • @Stargateur Yes, it is obvious that you don't understand the standard, since I just spelled it out word by word to you, and yet you continue to argue, without any reference to the standard. Your only argument is "for me it is not so". I'm done talking with you. – Lundin Dec 08 '17 at 12:46
  • 2
    @Lundin "Is it a (qualified) type compatible with the effective type of the object?" Yes, if the original pointer was pointing to a `int32_t` variable. You can't say that `do_test` always exhibits UB if it doesn't always do that. The assumption is that the OP is not passing a pointer to an `int32_t` variable, *but that assumption is not explicitly spelled out in the question* - there is no code in the OP's question which is calling the function. – milleniumbug Dec 08 '17 at 23:02
  • @milleniumbug No. _The whole point with pointer aliasing rules in the first place, is that a function cannot know the actual type passed by the caller_. It only has the type in the parameter list. The function is free to assume that if it takes a character pointer as parameter, access through that pointer does not for example modify a file scope variable of type `double` also used by the function. Therefore the function does not need to take this in account and the compiler can generate faster code. This is the original rationale for the aliasing rules. – Lundin Dec 11 '17 at 07:32
  • So you are claiming that `int32_t a = 42; do_test((const char*)&a);` is UB? – milleniumbug Dec 11 '17 at 12:13
  • @milleniumbug From the answer: "as a special exception C allows you to convert from a pointer-to-x to pointer to char and then access the data through the char poiner. But not the other way around." So no, that code is perfectly fine. But `void do_test (const char* ptr) { *(const int32_t*)ptr = 0; }` invokes UB. – Lundin Dec 11 '17 at 12:31
  • @Lundin I think you shouldn't ignore 6.3.2.3 where conversions of pointers are quite reasonably defined. 6.5 is about trying to cut the corner with the once traditional an-int-is-four-chars-is-a-float which should not be part of any clean C program, but to prohibit a char pointer being converted to another type surely wasn't the commitees intent as that would make dealing with any kind of binary protocol impossible. – Vroomfondel Dec 11 '17 at 12:35
  • @Vroomfondel Pointer conversions and strict aliasing are two different, unrelated matters, why this answer consists of two bullets. Indeed it was probably not the committee's intention that such type punning would be disallowed, but that's how the standard is written and therefore compilers - most notably gcc - abuse this to produce faster code. The standard needs to be fixed. I can't help you there. That being said, there are work-arounds such as unions, to dodge the strict aliasing rule. – Lundin Dec 11 '17 at 12:42
  • @Lundin I think milleniumbugs and my argument is that iff there was an `int32_t` object and the `const char *cp` was generated (on the callers side) by converting from the address of this object *then* the function does not exert UB as the only applicable restriction in this case is on the alignment. – Vroomfondel Dec 11 '17 at 12:51
  • @Vroomfondel Strict aliasing violations only occur when there is dereferencing. – Lundin Dec 11 '17 at 13:21
  • @Lundin imagine the following situation: you receive a packet into a `malloc`ed array - reading an `int32_t` from this buffer is defined under 6.5p6 (last sentence "For all other accesse to an object having no declared type...") simply by passing the `char*` address - *iff* the alignment is correct. – Vroomfondel Dec 11 '17 at 13:23
  • @Lundin Great answer! Can you please share some source where such nitty gritty of C language can be found. So that amateurs like me can learn :) – Deepthought Feb 14 '19 at 09:34
  • @Deepthought I don't understand what you mean, the answer does site the relevant parts of the C standard + a link explaining strict aliasing. What exactly are you looking for? – Lundin Feb 14 '19 at 10:38
  • @Lundin , What I meant is apart from strict aliasing, what other features of C standard has which amateur programmer are usually not aware? (Ex: I knew about sequence points, expression evaluations etc till today but not strict aliasing). In general, how do you [keep yourself updated | learn]? Does reading the C spec suffice? Thanks. – Deepthought Feb 14 '19 at 12:18
  • 1
    @Deepthought The C standard is hard to read for normal people. MISRA-C is more readable and provides a bit of rationale. You can also dig through the SO [C FAQ](https://stackoverflow.com/tags/c/info). Strict aliasing, implicit promotion, all the cases of UB and so on. There's also an old but great book called "C Traps and Pitfalls" by Andew Koenig which I would recommend. Also a harder-to-read but detailed book/scientific study "Safer C" by Les Hatton. These two books were very influential when MISRA-C was created. – Lundin Feb 14 '19 at 12:54
  • Thanks a lot @Lundin! – Deepthought Feb 14 '19 at 14:42
3

If you feel a need to avoid the explicit cast, you can always do memcpy:

#include <string.h>
#include <stdint.h>

static int32_t 
do_test(const char *cp)
{
    int32_t r;
    memcpy(&r,cp,sizeof(r));
    return r;
}

With an optimizing compiler that has a builtin mempcy, this should be just as efficient as return *(int32_t*)cp; (your code, written more succinctly).

Keep in mind that in either case, the code is only defined if the cp value you passed in points to a valid int32_t object.

If memcpy isn't OK because of the implicit char* to void* cast, you could replace it with a custom-made trivially implemented void charwise_memcpy(char *Dest, char const *Src, size_t Sz); or the equivalent for loop.

void charwise_memcpy(char *Dest, char const *Src, size_t Sz)
{
    for(size_t i=0; i<Sz; i++)
        Dest[i]=Src[i];
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • By using void instead of char is resulting in new violation of Rule 11.5 "An pointer to void type "cp" shall not be converted to pointer to object". I want the code to be completely free of violation – Salim Dec 03 '17 at 12:36
  • @Salim Use the `memcpy` solution then? – Petr Skocik Dec 03 '17 at 12:38
  • "*Rule 11.5 "An pointer to void type "cp" shall not be converted to pointer to object"*" what is the idea behind this rule? – alk Dec 03 '17 at 12:41
  • OK, so it seems `memcpy` is illegal under MISRA as well (the implicit cast isn't apparently OK either) (https://stackoverflow.com/questions/35465754/rationale-behind-misra-2012-not-allowing-cast-between-different-pointers). Then I guess, you'll have to avoid generating the cp pointer in the first place (which, under the no-cast rules you could have only obtained from a union) and deal directly in `int32_t` pointers instead. – Petr Skocik Dec 03 '17 at 12:50
  • @PSkocik Is it like we cannot do any typecast of pointer without any misra violation ? – Salim Dec 03 '17 at 13:44
  • Since malloc and calloc always return a void pointer, we have to typecast that to our required type. In this case always the usage of malloc and calloc will result in violation of 11.5 right ? – Salim Dec 03 '17 at 16:09
  • @Salim That's what I gather from the linked question. You don't have to cast void pointers in C explicitly, however, it looks like an implicit cast would still be a violation of those MISRA rules. – Petr Skocik Dec 03 '17 at 16:14
  • From MISRA C 2012 Document : Exception It is permitted to convert a pointer to object type into a pointer to one of the object types char, signed char or unsigned char. The Standard guarantees that pointers to these types can be used to access the individual bytes of an object. – Salim Dec 03 '17 at 18:57
  • @Salim Then perhaps the memcpy method could be OK under it after all. – Petr Skocik Dec 03 '17 at 19:01
  • The suggested code in this answer does not convert `void *` to other object pointer type . (It converts other object pointer type to `void *`). – M.M Dec 03 '17 at 21:18
  • @PSkocik But even if I use memcpy also the pointer address wont get typecast-ed to int from char right ? – Salim Dec 04 '17 at 04:42
  • 3
    @alk The MISRA rationale for 11.5 is to prevent void pointer conversions because they can result in misaligned access. It is a bit of a cumbersome rule - I would deviate from it. It is perfectly fine to do this and still be MISRA compliant, since the rule is only advisory. – Lundin Dec 04 '17 at 10:09
2

The original code may cause undefined behaviour:

const char *cp;
// ...
x = (const int32_t *)cp;

If int32_t has an alignment requirement on the platform, and cp is not correctly aligned for that requirement, the behaviour is undefined.

I'm not a fan of MISRA in general but this particular instance seems well justified. Even if you happen to be on a platform with no alignment requirements (unlikely even in embedded development), this code is non-portable and might start breaking if you move to a CPU that does have alignment requirements.

A good solution is to use memcpy instead which is well-defined:

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 1
    Not only UB because of potential alignment issues, but also because it is a "strict aliasing" violation. – Lundin Dec 04 '17 at 10:04