CertC-EXP42

Do not compare padding data

Required inputs: IR

The C Standard, 6.7.2.1 [ ISO/IEC 9899:2011], states

There may be unnamed padding within a structure object, but not at its beginning. . . . There may be unnamed padding at the end of a structure or union.

Subclause 6.7.9, paragraph 9, states that

unnamed members of objects of structure and union type do not participate in initialization. Unnamed members of structure objects have indeterminate value even after initialization.

The only exception is that padding bits are set to zero when a static or thread-local object is implicitly initialized (paragraph10):

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static or thread storage duration is not initialized explicitly, then:

- if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;

- if it is a union, the first named member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;

Because these padding values are unspecified, attempting a byte-by-byte comparison between structures can lead to incorrect results [ Summit 1995]. 

Noncompliant Code Example

In this noncompliant code example,  memcmp() is used to compare the contents of two structures, including any padding bytes:

#include <string.h>
 
struct s {
  char c;
  int i;
  char buffer[13];
};
 
void compare(const struct s *left, const struct s *right) {
  if ((left && right) &&
      (0 == memcmp(left, right, sizeof(struct s)))) {
    /* ... */
  }
}
Compliant Solution

In this compliant solution, all of the fields are compared manually to avoid comparing any padding bytes:

#include <string.h>
 
struct s {
  char c;
  int i;
  char buffer[13];
};

void compare(const struct s *left, const struct s *right) {
  if ((left && right) &&
      (left->c == right->c) &&
      (left->i == right->i) &&
      (0 == memcmp(left->buffer, right->buffer, 13))) {
    /* ... */
  }
}
Exceptions

EXP42-C-EX1: A structure can be defined such that the members are aligned properly or the structure is packed using implementation-specific packing instructions. This is true only when the members' data types have no padding bits of their own and when their object representations are the same as their value representations. This frequently is not true for the _Bool type or floating-point types and need not be true for pointers. In such cases, the compiler does not insert padding, and use of functions such as memcmp() is acceptable.

This compliant example uses the  #pragma pack compiler extension from Microsoft Visual Studio to ensure the structure members are packed as tightly as possible:

#include <string.h>
 
#pragma pack(push, 1)
struct s {
  char c;
  int i;
  char buffer[13];
};
#pragma pack(pop)

void compare(const struct s *left, const struct s *right) {
  if ((left && right) &&
      (0 == memcmp(left, right, sizeof(struct s)))) {
    /* ... */
  }
}
Risk Assessment

Comparing padding bytes, when present, can lead to unexpected program behavior.

Rule Severity Likelihood Remediation Cost Priority Level
EXP42-C Medium Probable Medium P8 L2
Related Guidelines
Taxonomy Taxonomy item Relationship
ISO/IEC TS 17961 Comparison of padding data [padcomp] Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C EXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representation Prior to 2018-01-12: CERT: Unspecified Relationship
Bibliography
[ ISO/IEC 9899:2011] 6.7.2.1, "Structure and Union Specifiers"
6.7.9, "Initialization"
[ Summit 1995] Question 2.8
Question 2.12
Excerpt from SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) and SEI CERT C Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/expressions-exp/exp42-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

disallowed_memcmp_pointer_arg

Disallowed type of pointer argument.

None

False

memcmp_char_pointer_arg

memcmp shall not be used with char pointer argument, use strncmp instead.

None

False

memcmp_float

memcmp shall not be used to compare floats as the same value may be stored using different representations.

None

False

memcmp_padding

memcmp shall not be used to compare structs with padding.

None

False

memcmp_struct_pointer_arg

memcmp shall not be used with struct pointer argument as it would compare padding as well.

None

False

memcmp_union_pointer_arg

memcmp shall not be used with union pointer argument as it would compare padding and different kinds of representation.

None

False

Options

allow_char

allow_char : bool = True

Whether to allow memcmp on char type.
 

allow_composites_without_padding

allow_composites_without_padding : bool = True

Whether to allow using memcmp on structs and unions that have no padding bytes.
 

allow_float

allow_float : bool = True

Whether to allow memcmp on floating point types.
 

functions

functions : set[bauhaus.analysis.config.QualifiedName] = {'memcmp'}

Names of functions being relevant as call targets for this check.
 

ignore_calls_in_functions

ignore_calls_in_functions : set[bauhaus.analysis.config.QualifiedName] = set()

Qualified names of function definitions in which calls to relevant functions are ignored for this check.