The C Standard, subclause 6.7.2.1 discusses the layout of structure fields. It specifies that non-bit-field members are aligned in an implementation defined manner, and that there may be padding within or at the end of a structure. Furthermore, initializing the members of the structure does not guarantee initialization of the padding bytes. The C Standard, subclause 6.2.6.1 paragraph 6 [ISO/IEC 9899:2011], states:
When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values.
As a result of being unspecified, padding bytes could contain random, possibly sensitive, data. A pointer to such a structure could be passed to other functions, causing information leakage.
Noncompliant Code Example
In this noncompliant code example, padding bytes may be used within the structure, depending on the architecture:
#include <stddef.h>
struct test{
int a;
char b;
int c;
};
/* Safely copy bytes to user space */
extern int copy_to_user(void *dest, void *src, size_t size);
void do_stuff(void *usr_buf) {
struct test arg = {.a = 1, .b = 2, .c = 3};
copy_to_user(usr_buf, &arg, sizeof(arg));
}
Noncompliant Code Example (memset())
The padding bytes can be explicitly initialized by calling memset():
#include <string.h>
struct test{
int a;
char b;
int c;
};
/* Safely copy bytes to user space */
extern int copy_to_user(void *dest, void *src, size_t size);
void do_stuff(void *usr_buf) {
struct test arg;
/* Set all bytes (including padding bytes) to zero */
memset(&arg, 0, sizeof(arg));
arg.a = 1;
arg.b = 2;
arg.c = 3;
copy_to_user(usr_buf, &arg, sizeof(arg));
}
However, compilers are free to implement arg.b = 2 by setting the low byte of a 32-bit register to 2, leaving the high bytes unchanged, and storing all 32-bits of the register into memory. This would then leak the high bytes resident in the register to a user.
Compliant Solution
One compliant solution is to manually serialize the structure data before copying it to an untrusted context:
#include <stddef.h>
struct test{
int a;
char b;
int c;
};
/* Safely copy bytes to user space */
extern int copy_to_user(void *dest, void *src, size_t size);
void do_stuff(void *usr_buf) {
struct test arg = {.a = 1, .b = 2, .c = 3};
/* May be larger than strictly needed */
unsigned char buf[sizeof(arg)];
size_t size = 0;
*((int *)(buf + size)) = arg.a;
size += sizeof(arg.a);
*((char *)(buf + size)) = arg.b;
size += sizeof(arg.b);
*((int *)(buf + size)) = arg.c;
size += sizeof(arg.c);
copy_to_user(usr_buf, buf, size);
}
This code ensures that no uninitialized padding bytes are copied to unprivileged users.
Compliant Solution (Padding Bytes)
Padding bytes can be explicitly declared as fields within the structure, however this solution is not portable because it depends on the implementation and target memory architecture. The following solution assumes it will be run on an IA-32 architecture:
#include <assert.h>
#include <stddef.h>
struct test{
int a;
char b;
char padding_1, padding_2, padding_3;
int c;
};
/* Safely copy bytes to user space */
extern int copy_to_user(void *dest, void *src, size_t size);
void do_stuff(void *usr_buf) {
/* Ensure c is the next byte after the last padding byte */
static_assert(offsetof(struct test, c) ==
offsetof(struct test, padding_3) + 1,
"Structure contains intermediate padding");
/* Ensure there is no trailing padding */
static_assert(sizeof(struct test) ==
offsetof(struct test, c) + sizeof(int),
"Structure contains trailing padding");
struct test arg = {.a = 1, .b = 2, .c = 3};
arg.padding_1 = 0;
arg.padding_2 = 0;
arg.padding_3 = 0;
copy_to_user(usr_buf, &arg, sizeof(arg));
}
The C11 static_assert() macro accepts a constant expression and an error message. The expression is evaluated at compile time, and, if false, the compilation is terminated and the error message is output. See DCL03-C. Use a static assertion to test the value of a constant expression for more details.
For example, on an IA-32 machine, the explicit insertion of the padding bytes into the struct should ensure that no additional padding bytes are added by the compiler, and consequently both static_assert expressions should be true. However, it is still necessary to assert these details to ensure the safety of the solution.
Compliant Solution (Structure Packing—GCC)
GCC allows specifying declaration attributes using the keyword __attribute__((__packed__)). When this attribute is present, the compiler will not add padding bytes for memory alignment, unless otherwise required to by the _Alignas alignment specifier, and will attempt to place fields at adjacent memory offsets when possible.
#include <stddef.h>
struct test{
int a;
char b;
int c;
} __attribute__((__packed__));
/* Safely copy bytes to user space */
extern int copy_to_user(void *dest, void *src, size_t size);
void do_stuff(void *usr_buf) {
struct test arg = {.a = 1, .b = 2, .c = 3};
copy_to_user(usr_buf, &arg, sizeof(arg));
}
Compliant Solution (Structure Packing—Microsoft Visual Studio)
Microsoft Visual Studio supports #pragma pack() instead of the __packed__ attribute to attempt to suppress padding bytes [MSDN]. The compiler will add padding bytes for memory alignment depending on the current packing mode, but still honors alignment specified by __declspec(align()). In this compliant solution, the packing mode is set to one in an attempt to ensure all fields are given adjacent offsets.
#include <stddef.h>
#pragma pack(push, 1) /* 1 byte */
struct test{
int a;
char b;
int c;
};
#pragma pack(pop)
/* Safely copy bytes to user space */
extern int copy_to_user(void *dest, void *src, size_t size);
void do_stuff(void *usr_buf) {
struct test arg = {1, 2, 3};
copy_to_user(usr_buf, &arg, sizeof(arg));
}
The pack pragma takes effect at the first struct declaration after the pragma is seen. The alignment of a member will be on a boundary that is a multiple of one byte unless otherwise specified.
Risk Assessment
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
DCL39-C | Low | Unlikely | High | P1 | L3 |
Related Vulnerabilities
In practice, this type of security flaw can expose sensitive information to unintended parties. The Sun tarball vulnerability discussed in Secure Coding Principles & Practices: Designing and Implementing Secure Applications [Graff 2003] and Sun Security Bulletin #00122 [Sun 1993] shows a violation of this recommendation and its consequential leak of sensitive data. Attackers can also leverage this defect to retrieve sensitive information using techniques such as heap inspection.
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
| CERT C Secure Coding Standard | DCL03-C. Use a static assertion to test the value of a constant expression |
Bibliography
| [ISO/IEC 9899:2011] | Subclause 6.2.6.1, "General" Subclause 6.7.2.1, "Structure and Union Specifiers" |
| [Graff 2003] | |
| [Sun 1993] |