...
When passing a pointer to a class object instance across a trust boundary to a different trusted domain, the programmer must ensure that the padding bits of such an object does not contain sensitive information.
Noncompliant Code Example
This noncompliant code example runs in kernel space and copies data from arg to user space. However, padding bits may be used within the object, for example, to ensure the proper alignment of class data members. These padding bits may contain sensitive information, which may then be leaked when the data is copied to user space.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <cstddef>
struct test {
int a;
char b;
int c;
};
// Safely copy bytes to user space
extern int copy_to_user(void *dest, void *src, std::size_t size);
void do_stuff(void *usr_buf) {
test arg{1, 2, 3};
copy_to_user(usr_buf, &arg, sizeof(arg));
} |
Noncompliant Code Example
In this noncompliant code example, arg is value-initialized through direct initialization. Because test does not have a user-provided default constructor, the value-initialization is preceded by a zero-initialization which guarantees that the padding bits are initialized to 0 before any further initialization occurs. It is akin to using std::memset() to initialize all of the bits in the object to 0.
...
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 could leak the high-order bytes resident in the register to a user.
Compliant Solution
This compliant solution serializes the structure data before copying it to an untrusted context:
...
This code ensures that no uninitialized padding bits are copied to unprivileged users. Note that the structure copied to user space is now a packed structure and that the copy_to_user() function would need to unpack it to re-create the original, padded structure.
Compliant Solution (Padding Bytes)
Padding bits can be explicitly declared as fields within the structure. This solution is not portable, however, because it depends on the implementation and target memory architecture. The following solution is specific to the x86-32 architecture:
...
The static_assert() declaration 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 used as the diagnostic. 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 assertions should be true. However, it is necessary to validate these assumptions to ensure that the solution is correct for a particular implementation.
Noncompliant Code Example
In this noncompliant code example, padding bits may abound, including:
...
| Offset (bytes (bits)) | Storage Size (bytes (bits)) | Reason | Offset | Storage Size | Reason | |
|---|---|---|---|---|---|---|
| 0 | 1 (32) | vtable pointer | 56 (448) | 4 (32) | unsigned k | |
| 4 (32) | 28 (224) | data member alignment padding | 60 (480) | 0 (4) | unsigned l : 4 | |
| 32 (256) | 8 (64) | double h | 60 (484) | 0 (3) | unsigned short m : 3 | |
| 40 (320) | 1 (8) | char i | 60 (487) | 0 (1) | unused bit-field bits | |
| 41 (328) | 3 (24) | data member alignment padding | 61 (488) | 1 (8) | char n | |
| 44 (352) | 4 (32) | unsigned j : 80 | 62 (496) | 2 (16) | data member alignment padding | |
| 48 (384) | 6 (48) | extended bit-field size padding | 64 (512) | 8 (64) | double o | |
| 54 (432) | 2 (16) | alignment padding | 72 (576) | 24 (192) | class alignment padding |
Compliant Solution
Due to the complexity of the data structure, this compliant solution serializes the object data before copying it to an untrusted context instead of attempting to account for all of the padding bytes manually:
...
This code ensures that no uninitialized padding bits are copied to unprivileged users. Note that the structure copied to user space is now a packed structure and that the copy_to_user() function would need to unpack it to re-create the original, padded structure.
Risk Assessment
Padding bits might inadvertently contain sensitive data such as pointers to kernel data structures or passwords. A pointer to such a structure could be passed to other functions, causing information leakage.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
DCL60DCL59-CPP | Low | Unlikely | High | P1 | L3 |
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. CVE-2010-3881 describes a vulnerability in which structure padding and reserved fields in certain data structures in QEMU-KVM were not initialized properly before being copied to user space. A privileged host user with access to /dev/kvm could use this flaw to leak kernel stack memory to user space. CVE-2010-3477 describes a kernel information leak in act_police where incorrectly initialized structures in the traffic-control dump code may allow the disclosure of kernel memory to user space applications.
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
| SEI CERT C Coding Standard | DCL39-C. Avoid information leakage when passing a structure across a trust boundary |
Bibliography
| [ISO/IEC 14882-2014] | Subclause 8.5, "Initializers" |
...