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

Automated Detection

Tool

Version

Checker

Description

Astrée

24.04

memcpy-with-paddingPartially checked
Axivion Bauhaus Suite

7.2.0

CertC-EXP42
CodeSonar
8.1p0

BADFUNC.MEMCMP

Use of memcmp

Cppcheck Premium

24.9.0

premium-cert-exp42-cFully implemented
Helix QAC

2024.2

DF4726, DF4727, DF4728, DF4729


Klocwork

2024.2

MISRA.STDLIB.MEMCMP.PTR_ARG_TYPES
LDRA tool suite
9.7.1
618 SPartially implemented
Cppcheck
 2.15
cert.py

Detected by the addon cert.py

Does not warn about global/static padding data as this is probably initialized to 0

Parasoft C/C++test
2023.1
CERT_C-EXP42-a

Don't memcpy or memcmp non-PODs

PC-lint Plus

1.4

958, 959

Assistance provided: reports structures which require padding between members or after the last member

Polyspace Bug Finder

R2024a

CERT C: Rule EXP42-C


Checks for memory comparison of padding data (rule fully covered)

PVS-Studio

7.33

V1103
RuleChecker

24.04

memcpy-with-paddingPartially checked
TrustInSoft Analyzer

1.38

comparable_char_blocks

Exhaustively verified (see the compliant and the non-compliant example).

Related Vulnerabilities

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

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

ISO/IEC TS 17961Comparison of padding data [padcomp]Prior to 2018-01-12: CERT: Unspecified Relationship
CERT CEXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representationPrior 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



8 Comments

  1. Question: Would we allow bitwise serlization of a struct, given that the padding data might contain sensitive info (eg: password from its previous use as a char string)?

     

  2. To me it seems that the "compliant" solution is dangerous and uncompliant.

    It assumes there is no padding inside the struct, for instance between c and i.

    Then it is dangerous, because if there is padding then that code will not compare the members completely.

     

    1. The compliant solution is comparing the struct members individually. The exception compliant solution is doing a memcmp() only because the structure is packed with an implementation-defined #pragma. Can you expound on what you find dangerous?

      1. sorry I misread the code. it is safe.

  3. as far as I see... if you don't want to compare padding data at all then memcmp should not be used. the struct members should be compared individually then.

  4. The Compliant Solution checks left and right for NULL while the Noncompliant Code Example and EXP42-C-EX1 do not. I don't think this difference is intended to illustrate anything about this rule so I would suggest making them all consistent for clarity. Passing NULL to memcmp is undefined behavior.

    1. Agreed, I changed the code as you suggest.