The C Standard, 6.7.3.2.1, discusses the layout of structure fields. It 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, 6.2.6.1, paragraph 6 [ISO/IEC 9899:20112024], 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 (e.g. structure and union assignment may or may not copy any padding bits).
Additionally, the storage units in which a bit-field resides may also have padding bits. For an object with automatic storage duration, these padding bits do not take on specific values and can contribute to leaking sensitive information.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#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 a conforming compiler is free to implement arg.b = 2 by setting the low byte -order bits of a 32-bit register to 2, leaving the high bytes -order bits unchanged and storing all 32 bits of the register into memory. This implementation could containing sensitive information. Then the platform copies all register bits into memory, leaving sensitive information in the padding bits. Consequently, this implementation could leak the high-order bytes resident in bits from the register to a user.
Compliant Solution
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stddef.h>
#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 = {.a = 1, .b = 2, .c = 3};
/* May be larger than strictly needed */
unsigned char buf[sizeof(arg)];
size_t offset = 0;
memcpy(buf + offset, &arg.a, sizeof(arg.a));
offset += sizeof(arg.a);
memcpy(buf + offset, &arg.b, sizeof(arg.b));
offset += sizeof(arg.b);
memcpy(buf + offset, &arg.c, sizeof(arg.c));
offset += sizeof(arg.c);
copy_to_user(usr_buf, buf, offset /* size of info copied */);
} /* Set all remaining bytes to zero */
memset(buf + offset, 0, sizeof(arg) - offset);
copy_to_user(usr_buf, buf, offset /* size of info copied */);
} |
This code This code ensures that no uninitialized padding bytes are copied to unprivileged users. Important: The structure copied to user space is now a packed structure and the copy_to_user() function (or other eventual user) would need to unpack it to recreate the original padded structure.
...
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 by the _Alignas alignment specifier, and it will attempt to place fields at adjacent memory offsets when possibleunless an explicit alignment specifier for a structure member requires the introduction of padding bytes.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#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));
}
|
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stddef.h>
struct test {
unsigned a : 1;
unsigned : 0;
unsigned b : 4;
};
/* 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 = 10 };
copy_to_user(usr_buf, &arg, sizeof(arg));
} |
However, compilers are free to implement the initialization of arg.a and arg.b by setting the low byte of a 32-bit register to the value specified, leaving the high bytes unchanged and storing all 32 bits of the register into memory. This implementation could leak the high-order bytes resident in the register to a user.
Compliant Solution
Compliant Solution
Padding bits can be explicitly declared, allowing the programmer to specify the value of those bits. When explicitly declaring all of the padding bits, any unnamed bit-fields of length 0 must be removed from the structure because the explicit padding bits Padding bits can be explicitly declared, allowing the programmer to specify the value of those bits. When explicitly declaring all of the padding bits, any unnamed bit-fields of length 0 must be removed from the structure because the explicit padding bits ensure that no further bit-fields will be packed into the same storage unit.
...
Padding units might contain sensitive data because the C Standard allows any padding to take unspecified values. A pointer to such a structure could be passed to other functions, causing information leakage.passed to other functions, causing information leakage.
Rule | Severity | Likelihood | Detectable | Repairable | Priority | Level |
|---|---|---|---|---|---|---|
DCL39-C | Low | Unlikely | No | Yes | P2 | L3 |
Automated Detection
Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Astrée |
| function-argument-with-padding | Partially checked | ||||||
| Axivion Bauhaus Suite |
| CertC-DCL39 | Detects composite structures with padding, in particular those passed to trust boundary routines. | ||||||
| CodeSonar |
| MISC.PADDING.POTB | Padding Passed Across a Trust Boundary | ||||||
| Cppcheck Premium |
| premium-cert-dcl39-c | |||||||
| Helix QAC |
| DF4941, DF4942, DF4943 | Fully implemented | ||||||
| Klocwork |
| PORTING.STORAGE.STRUCT | Fully implemented | ||||||
| Parasoft C/C++test |
| CERT_C-DCL39-a | A pointer to a structure should not be passed to a function that can copy data to the user space | ||||||
| CERT C: Rule DCL39-C | Checks for information leak via structure padding | |||||||
| RuleChecker |
| function-argument-with-padding | Partially checked | ||||||
| Security Reviewer - Static Reviewer |
| C20, C21,C22, C23, C25 | Fully implemented |
Rule
Severity
Likelihood
Remediation Cost
Priority
Level
DCL39-C
Low
Unlikely
High
P1
Related Vulnerabilities
Numerous vulnerabilities in the Linux Kernel have resulted from violations of this rule. CVE-2010-4083 describes a vulnerability in which the semctl() system call allows unprivileged users to read uninitialized kernel stack memory because various fields of a semid_ds struct declared on the stack are not altered or zeroed before being copied back to the user.
...
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 |
|---|---|---|
| CERT C Secure Coding Standard | DCL03-C. Use a static assertion to test the value of a constant expression | Prior to 2018-01-12: CERT: Unspecified Relationship |
Bibliography
| [ISO/IEC 9899: |
| 2024] | 6.2.6.1, "General" 6.7.3.2 |
| , "Structure and Union Specifiers" |
| [Graff 2003] |
| [Sun 1993] |
...
...