CERT
Skip to end of metadata
Go to start of metadata

Do not convert a pointer value to a pointer type that is more strictly aligned than the referenced type. Different alignments are possible for different types of objects. If the type-checking system is overridden by an explicit cast or the pointer is converted to a void pointer (void *) and then to a different type, the alignment of an object may be changed.

The C Standard, 6.3.2.3, paragraph 7 [ISO/IEC 9899:2011], states

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

See undefined behavior 25.

If the misaligned pointer is dereferenced, the program may terminate abnormally. On some architectures, the cast alone may cause a loss of information even if the value is not dereferenced if the types involved have differing alignment requirements.

Noncompliant Code Example

In this noncompliant example, the char pointer &c is converted to the more strictly aligned int pointer ip. On some implementations, cp will not match &c. As a result, if a pointer to one object type is converted to a pointer to a different object type, the second object type must not require stricter alignment than the first.

Compliant Solution (Intermediate Object)

In this compliant solution, the char value is stored into an object of type int so that the pointer's value will be properly aligned:

Noncompliant Code Example

The C Standard allows any object pointer to be cast to and from void *. As a result, it is possible to silently convert from one pointer type to another without the compiler diagnosing the problem by storing or casting a pointer to void * and then storing or casting it to the final type. In this noncompliant code example, loop_function() is passed the char pointer loop_ptr but returns an object of type int pointer:

This example compiles without warning using GCC 4.8 on Ubuntu Linux 14.04. However, v_pointer can be more strictly aligned than an object of type int *.

Compliant Solution

Because the input parameter directly influences the return value, and loop_function() returns an object of type int *, the formal parameter v_pointer is redeclared to accept only an object of type int *:

Noncompliant Code Example

Some architectures require that pointers are correctly aligned when accessing objects larger than a byte. However, it is common in system code that unaligned data (for example, the network stacks) must be copied to a properly aligned memory location, such as in this noncompliant code example:

Assigning an unaligned value to a pointer that references a type that needs to be aligned is undefined behavior. An implementation may notice, for example, that tmp and header must be aligned and use an inline memcpy() that uses instructions that assume aligned data.

Compliant Solution

This compliant solution avoids the use of the foo_header pointer:

Exceptions

EXP36-C-EX1: Some hardware architectures have relaxed requirements with regard to pointer alignment. Using a pointer that is not properly aligned is correctly handled by the architecture, although there might be a performance penalty. On such an architecture, improper pointer alignment is permitted but remains an efficiency problem.

EXP36-C-EX2: If a pointer is known to be correctly aligned to the target type, then a cast to that type is permitted. There are several cases where a pointer is known to be correctly aligned to the target type. The pointer could point to an object declared with a suitable alignment specifier. It could point to an object returned by aligned_alloc(), calloc(), malloc(), or realloc(), as per the C standard, section 7.22.3, paragraph 1  [ISO/IEC 9899:2011].

This compliant solution uses the alignment specifier, which is new to C11, to declare the char object c with the same alignment as that of an object of type int. As a result, the two pointers reference equally aligned pointer types:

Risk Assessment

Accessing a pointer or an object that is not properly aligned can cause a program to crash or give erroneous information, or it can cause slow pointer accesses (if the architecture allows misaligned accesses).

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

EXP36-C

Low

Probable

Medium

P4

L3

Automated Detection

Tool

Version

Checker

Description

Astrée17.04i Supported, but no explicit checker
Compass/ROSE  

Can detect violations of this rule. However, it does not flag explicit casts to void * and then back to another pointer type

Coverity2017.07

MISRA C 2004 Rule 11.4

MISRA C 2012 Rule 11.1

MISRA C 2012 Rule 11.2

MISRA C 2012 Rule 11.5

MISRA C 2012 Rule 11.7

Implemented

ECLAIR

1.2

CC2.EXP36

Fully implemented
EDG   
GCC4.3.5 

Can detect some violations of this rule when the -Wcast-align flag is used

Klocwork 2017

MISRA.CAST.PTR.UNRELATED
MISRA.CAST.PTR_TO_INT
PORTING.CAST.PTR.FLTPNT

PORTING.CAST.PTR
PORTING.CAST.PTR.SIZE
PORTING.CAST.SIZE

 
LDRA tool suite9.5.6

94 S, 606 S

Partially implemented
Parasoft C/C++test9.5MISRA2004-11_4Fully implemented
Polyspace Bug FinderR2016aUnreliable cast of pointerPointer implicitly cast to different data type
PRQA QA-C9.33305Fully implemented
    

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

SEI CERT C++ Coding StandardVOID EXP56-CPP. Do not cast pointers into more strictly aligned pointer types
ISO/IEC TR 24772:2013Pointer Casting and Pointer Type Changes [HFC]
ISO/IEC TS 17961Converting pointer values to more strictly aligned pointer types [alignconv]
MISRA C:2012Rule 11.1 (required)
Rule 11.2 (required)
Rule 11.5 (advisory)
Rule 11.7 (required)

Bibliography

 


13 Comments

  1. I am not sure if it can be tabulated as shown in "Implementation details" and especially the phrase "Common alignments" .

    Unlike ARM and MIPS - IA-32 is forgiving when it comes to data alignment. i.e if a 32bit data  is not aligned on 4 byte boundary it can cause an exception -undefined behavior in MIPS and ARM based architecture; whereas in IA-32 world the 32bit data will be fetched (but the performance would not be the best). For the best performance align the data such that  32bit data is at an address that is a multiple of 4, 16bit data 's address is a multiple of 2 and so on ... 8bit data can go at any address.

    When the compiler flag for alignment is used - each structure member after the first is stored on either the size of the member type or n-byte boundaries (where n is 1, 2, 4, 8, or 16), whichever is smaller.

    The "Implementation detail"  has to cover the compiler flag for alignment and the *natural* alignment.

     The "Risk Assessment" is OK, whereas the section "Implementation Details" has to be revisited.

    1. It may make sense here to just eliminate the section on "Implementation details".

      The goal of this document is to provide appropriate guidance for all C99-compliant implementations. The implementation details are mainly of value, I think, to highlight specific implementations that manifest the problem, sort of as "proof" the problem is real.

  2. Is the subject line too stringent, or the body not qualified enough?  There are two places (related) where the real issue is 'be cautious about converting a pointer to a loosely aligned type into a more strictly aligned type (and then dereferencing it)'.

    Both involve memory allocation - the first case is 'void *' returned by memory allocators such as malloc(), for which of course it is OK to convert from the ... non-aligned? ... void * to any other type since the pointer returned is guaranteed to be sufficiently well aligned for any normal use.

    The second case deals with legacy code (APIs) that pre-date C89 which use(d) 'char *' as the universal pointer instead of 'void *'.  When working with such code, you may be compelled to convert the 'char *' into (say) a 'long *'; as long as the caller provided a pointer that was correctly aligned in the first place, converting that to 'char *' and then back to 'long *' is safe.

    I also learned to program in C on a machine where the 'char *' address for a given memory location was not the same bit value as the 'anything else pointer' address to the same memory location - a word-oriented architecture.  It was crucial to get all the functions (malloc() et al - this was the pre-ISO C days, early 80s) declared because if the compiler got to think that the function returned an 'int', it would be incorrectly interpreted.  Damn good for discipline.  (The machine was an ICL Perq - and it was a micro-programmable machine with good graphics.)

  3. Is there any good reason to convert between different pointer types in general? (never mind alignment). Most pointer casts I have seen are either to/from void*, or to/from char* (for programmers that predate void*) Maybe I'm just dense, but I'll not be the first or last to ask this question...we should at least have a compliant code example that illustrates proper pointer typecasting (that doesn't need void*).

    1. this page has a neat example for when this would be necessary:

      qsort(), which takes a void * and a int(*compar)(const void *, const void *) compare function... the compare function must cast from void * to some data * in order to carry out the comparison

      our rule applies since you should not mix and match say a compare on int with a sort on char

      1. Sorry, qsort() is not what I'm looking for. qsort() takes void* arguments, as well as a comparison function that takes void* arguments. You feed qsort an array of void*s and a comparison fn that compares them and it sorts 'em for you. qsort does not pointer casts of its own; it expects you to do all that.

        What I'm saying is that the void* is C's attempt at generic programming, with qsort being a c-generic quicksort algo. To use qsort you have to feed it void*s, and then cast them back to their original type.

        But I'm wondering if there's any case where you WANT to convert a FOO* to a BAR* where neither FOO nor BAR are void. The void* lets you do this, we think its dangerous, but is it ever actually necessary?

        1. One example void non-void* -> non-void* casts: struct sockaddr_whatever* -> struct sockaddr*, since the socket functions use struct sockaddr* as the generic socket address pointer type.

          That is a special case of using struct pointers as generic pointers instead of void* to emulate Pascal-like variant records: You have several structs like

          If Foo and Bar are two such structs, C allows you to access a Foo's type element as ((Bar*)foo)->type. You then typically cast your Bar* pointer to the correct struct pointer type depending on the value of the type field.

          Another example is hash functions that access data as e.g. unsigned int* for speed. Of course the callers must then ensure the data is properly aligned, and the hash function needs a general case to cater to hosts where an unsigned int can have padding bits. Also I suspect this is dubious anyway due to C's type aliasing rules, but the implications of those rules aren't really clear to me.

          A third is casts from pointers to union Maximally_aligned { all the types you might need }; which is a trick to e.g. put properly aligned opaque objects in stack variables. Though I think this trick also runs into type aliasing rules.

          However regarding Alex's example, as he said it's the compare function to qsort which has to break this rule, in that it must cast from void*. And I'm not sure what's special about non-void* examples, when the main text itself uses casting from void* as a non-conformant example. As you say, void* is kind of C's generic pointer. So you may need to cast away from it when you use generic pointers.

          Another example of casting from void* is when you work with dynamic pointers pointer types implemented by hand:

          Sometimes you can use a union of pointers instead of the void* for that, at other times that's impractical. E.g. in C89 only the first union member can have an initializer.

          You'll find examples of char* -> other ptr* casts if you seach some packages for use of offsetof().

          Another example of casting between various pointer types arises when you need to build an object in memory in ways not directly supported by the C standard. For example if you want to avoid malloc() calls, either due to speed or to combat memory fragmentation: If a conceptual object consists of several C objects of varying length, you can't just put them in a struct and malloc that. But if you are careful about alignment you can call malloc just once, and place the objects at various offsets from the start of the malloced memory.

  4. "Once it is cast to an int *, some architectures will require that the object is aligned on a four-byte boundary. If int_ptr is later dereferenced, the program may terminate abnormally."

    It's even worse than that, as the mere cast to int* may cause loss of information. No dereference is even required to cause this. The following code won't work on all conforming C99 implementations, even though it never dereferences anything:

    On some implementations p2 will not match &c.

    1. i restructured the rule a bit and added this information to the introductory material. do you know of any specific implementations for which this assertion fails?

  5. Should we add a compliant solution or NCE/CS that uses the new C1X max_align_t?

    As usual, Dan Saks has a nice article on this complete with example:

    http://embedded.com/design/220900317?pgno=1

    1. I think it might be worth mentioning although I don't think using max_align_t solves the problem this rule cautions against.

      Incidentally, if/where we do mention a C1X feature I think we should make it clear that the feature may not be available for some time to come and, if possible, present a portable alternative.

  6. The Automated Detection section may want to mention -Wcast-align for Clang. It does not catch the second NCCE (that requires interprocedural analysis), but it does catch the other two NCCEs.

  7. This rule should also extend to taking the address of a member of a packed struct, which can also be improperly aligned. Normally, the compiler catches this and inserts proper get/set code, but when faced with the address, the compiler doesn't know to insert such code. More info here:

    https://stackoverflow.com/questions/8568432/is-gccs-attribute-packed-pragma-pack-unsafe