Use of sizeof() on a Pointer Type
Description
Use of sizeof() on a Pointer Type occurs when a programmer mistakenly applies the sizeof operator to a pointer variable instead of the data it points to or the intended array. This typically happens when arrays decay to pointers when passed to functions, or when programmers confuse pointer size with data size. On 64-bit systems, sizeof(pointer) returns 8, while sizeof(struct) might return hundreds of bytes—using the wrong size leads to buffer underallocation, incomplete copies, and memory corruption.
Risk
Incorrect sizeof usage causes buffer overflows, information disclosure, and memory corruption. When allocating memory using sizeof on a pointer (typically 4 or 8 bytes) instead of the structure size, subsequent writes overflow the undersized buffer. Memory copy operations using pointer sizeof copy only partial data, potentially leaking or corrupting information. This weakness is particularly dangerous because code may appear correct and even work in some cases before catastrophic failure.
Solution
Always apply sizeof to the dereferenced pointer or the actual type, not the pointer itself: use sizeof(*ptr) instead of sizeof(ptr). When allocating arrays, use sizeof(element) * count. Use static analysis tools that detect sizeof misuse. In modern C++, prefer std::array or std::vector which track their own size. Define allocation macros or templates that compute size correctly. Code review should specifically check sizeof arguments.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Buffer Overflow Undersized allocations lead to heap buffer overflows. |
| Availability | Scope: Memory Corruption Incorrect copies corrupt adjacent memory structures. |
| Confidentiality | Scope: Information Disclosure Partial copies may expose uninitialized or adjacent memory. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: sizeof pointer instead of structure
struct UserRecord {
char name[64];
char email[128];
int privileges;
time_t last_login;
};
struct UserRecord* create_user_vulnerable(const char* name) {
// Wrong! sizeof(struct UserRecord*) is 8 bytes on 64-bit
// But struct UserRecord is ~208 bytes!
struct UserRecord* user = malloc(sizeof(user));
// Buffer overflow when writing to undersized allocation!
strcpy(user->name, name); // Heap corruption!
return user;
}
// VULNERABLE: Array decay to pointer
void process_array_vulnerable(int arr[]) {
// Wrong! arr is actually a pointer here
// sizeof(arr) is sizeof(int*), not array size!
int count = sizeof(arr) / sizeof(arr[0]);
printf("Processing %d elements\n", count); // Wrong count!
for (int i = 0; i < count; i++) {
process(arr[i]); // Only processes 1-2 elements!
}
}
// VULNERABLE: memcpy with pointer sizeof
void copy_record_vulnerable(struct UserRecord* dest, struct UserRecord* src) {
// Wrong! Copies only 8 bytes instead of full structure
memcpy(dest, src, sizeof(src));
// Most of the structure not copied!
}
// VULNERABLE: Allocating array of structures
struct UserRecord* create_users_vulnerable(int count) {
// Wrong! Allocates count * 8 bytes instead of count * 208 bytes
struct UserRecord* users = malloc(count * sizeof(users));
return users; // Massively undersized buffer!
}
// VULNERABLE: String buffer with pointer sizeof
char* duplicate_string_vulnerable(const char* str) {
// Wrong! Allocates only 8 bytes regardless of string length
char* copy = malloc(sizeof(str));
strcpy(copy, str); // Buffer overflow!
return copy;
}
// VULNERABLE: memset with wrong size
void clear_record_vulnerable(struct UserRecord* record) {
// Wrong! Only clears first 8 bytes
memset(record, 0, sizeof(record));
// Most of structure still contains old data!
}
// VULNERABLE: Array parameter in function
void init_buffer_vulnerable(char buffer[1024]) {
// Wrong! buffer is a pointer, sizeof returns 8
memset(buffer, 0, sizeof(buffer));
// Only first 8 bytes zeroed, rest uninitialized!
}
// VULNERABLE: Flexible array member miscalculation
struct Message {
int type;
int length;
char data[]; // Flexible array member
};
struct Message* create_message_vulnerable(const char* content, int len) {
// Wrong! Doesn't account for flexible array
struct Message* msg = malloc(sizeof(msg) + len);
msg->length = len;
memcpy(msg->data, content, len); // Writes beyond allocation!
return msg;
}
// VULNERABLE: C++ with same issues
class Record {
std::string name;
std::string data;
int value;
};
// VULNERABLE: Using sizeof on pointer
Record* createRecordVulnerable() {
// sizeof(Record*) is 8, sizeof(Record) is much larger
Record* rec = (Record*)malloc(sizeof(rec));
// Object not properly constructed!
new (rec) Record(); // Placement new on undersized buffer!
return rec;
}
// VULNERABLE: Template with pointer
template<typename T>
T* allocate_vulnerable(T* example) {
// Wrong! sizeof(example) is pointer size
return (T*)malloc(sizeof(example));
}
// VULNERABLE: Array size detection in template
template<typename T>
void processArray_vulnerable(T* arr) {
// Wrong! Can't determine array size from pointer
constexpr size_t count = sizeof(arr) / sizeof(T); // Always 1
for (size_t i = 0; i < count; i++) {
process(arr[i]);
}
}
// VULNERABLE: Smart pointer misuse
void processSmartPointer_vulnerable() {
auto ptr = std::make_unique<Record[]>(10);
// Wrong! sizeof(ptr) is size of unique_ptr object
// Not the array it manages!
size_t size = sizeof(ptr);
}
// VULNERABLE: Double pointer confusion
struct Node {
int value;
struct Node* next;
};
void allocate_nodes_vulnerable(struct Node** nodes, int count) {
// First allocation - correct for array of pointers
*nodes = malloc(count * sizeof(struct Node*));
for (int i = 0; i < count; i++) {
// Wrong! Allocates sizeof(struct Node**) not sizeof(struct Node)
(*nodes)[i].next = malloc(sizeof(nodes)); // Way too small!
}
}
// VULNERABLE: Struct containing pointer
struct Container {
int count;
int* values; // Pointer to array
};
struct Container* create_container_vulnerable(int count) {
struct Container* c = malloc(sizeof(struct Container));
// Wrong! sizeof(c->values) is pointer size
// Should be count * sizeof(int)
c->values = malloc(sizeof(c->values));
c->count = count;
return c;
}
Fixed Code
// SAFE: sizeof on dereferenced pointer
struct UserRecord* create_user_safe(const char* name) {
// Correct! sizeof(*user) is sizeof(struct UserRecord)
struct UserRecord* user = malloc(sizeof(*user));
if (user == NULL) return NULL;
strncpy(user->name, name, sizeof(user->name) - 1);
user->name[sizeof(user->name) - 1] = '\0';
return user;
}
// SAFE: Pass array size explicitly
void process_array_safe(int* arr, size_t count) {
printf("Processing %zu elements\n", count);
for (size_t i = 0; i < count; i++) {
process(arr[i]);
}
}
// Macro to get array size (only works on actual arrays, not pointers)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
// Safe usage with actual array
void caller_safe() {
int numbers[100];
process_array_safe(numbers, ARRAY_SIZE(numbers));
}
// SAFE: memcpy with structure sizeof
void copy_record_safe(struct UserRecord* dest, struct UserRecord* src) {
// Correct! sizeof(*src) is structure size
memcpy(dest, src, sizeof(*src));
}
// Or use the type name
void copy_record_safe_v2(struct UserRecord* dest, struct UserRecord* src) {
memcpy(dest, src, sizeof(struct UserRecord));
}
// SAFE: Array allocation
struct UserRecord* create_users_safe(int count) {
// Correct! sizeof(*users) is structure size
struct UserRecord* users = malloc(count * sizeof(*users));
return users;
}
// SAFE: String duplication
char* duplicate_string_safe(const char* str) {
size_t len = strlen(str) + 1;
char* copy = malloc(len);
if (copy == NULL) return NULL;
memcpy(copy, str, len);
return copy;
}
// Or use strdup if available
char* duplicate_string_safe_v2(const char* str) {
return strdup(str);
}
// SAFE: memset with correct size
void clear_record_safe(struct UserRecord* record) {
// Correct! sizeof(*record) is structure size
memset(record, 0, sizeof(*record));
}
// SAFE: Buffer with explicit size
void init_buffer_safe(char* buffer, size_t buffer_size) {
memset(buffer, 0, buffer_size);
}
// Caller provides size
void caller_buffer_safe() {
char buffer[1024];
init_buffer_safe(buffer, sizeof(buffer));
}
// SAFE: Flexible array member
struct Message* create_message_safe(const char* content, int len) {
// Correct! sizeof(struct Message) plus array size
struct Message* msg = malloc(sizeof(struct Message) + len);
if (msg == NULL) return NULL;
msg->type = 0;
msg->length = len;
memcpy(msg->data, content, len);
return msg;
}
// SAFE: Type-safe allocation macro
#define ALLOC(type) ((type*)malloc(sizeof(type)))
#define ALLOC_ARRAY(type, count) ((type*)malloc(sizeof(type) * (count)))
// Usage
void use_macros() {
struct UserRecord* user = ALLOC(struct UserRecord);
struct UserRecord* users = ALLOC_ARRAY(struct UserRecord, 10);
}
// SAFE: C++ with proper sizing
// SAFE: Use new instead of malloc
Record* createRecordSafe() {
return new Record(); // Automatically correct size
}
// SAFE: Use sizeof on type or dereferenced pointer
Record* createRecordManualSafe() {
Record* rec = (Record*)malloc(sizeof(Record));
// Or: malloc(sizeof(*rec))
if (rec) {
new (rec) Record();
}
return rec;
}
// SAFE: Template with proper sizing
template<typename T>
T* allocate_safe() {
return (T*)malloc(sizeof(T));
}
// SAFE: Array with explicit count
template<typename T>
T* allocate_array_safe(size_t count) {
return new T[count];
}
// SAFE: Use std::array for compile-time size
template<typename T, size_t N>
void processArray_safe(std::array<T, N>& arr) {
for (size_t i = 0; i < N; i++) {
process(arr[i]);
}
}
// SAFE: Use std::vector for runtime size
template<typename T>
void processVector_safe(std::vector<T>& vec) {
for (size_t i = 0; i < vec.size(); i++) {
process(vec[i]);
}
}
// SAFE: Template array size deduction
template<typename T, size_t N>
constexpr size_t array_size(T (&)[N]) {
return N;
}
void use_array_size() {
int numbers[100];
size_t count = array_size(numbers); // Returns 100
}
// SAFE: Smart pointers with proper allocation
void processSmartPointer_safe() {
auto ptr = std::make_unique<Record[]>(10);
// Track size separately or use vector
size_t size = 10;
// Better: use vector
std::vector<Record> records(10);
size_t actualSize = records.size();
}
// SAFE: Double pointer with correct sizes
void allocate_nodes_safe(struct Node** nodes, int count) {
// Array of Node pointers (if that's the intent)
*nodes = malloc(count * sizeof(struct Node));
// Initialize each node
for (int i = 0; i < count; i++) {
(*nodes)[i].value = 0;
(*nodes)[i].next = NULL;
}
}
// SAFE: Struct with pointer - allocate separately
struct Container* create_container_safe(int count) {
struct Container* c = malloc(sizeof(*c));
if (c == NULL) return NULL;
// Correct! Allocate count integers
c->values = malloc(count * sizeof(*c->values));
if (c->values == NULL) {
free(c);
return NULL;
}
c->count = count;
return c;
}
// SAFE: Using sizeof consistently
typedef struct {
int id;
char name[50];
double balance;
} Account;
Account* clone_account(const Account* src) {
// All these are equivalent and correct:
Account* copy = malloc(sizeof(Account));
// Account* copy = malloc(sizeof(*copy));
// Account* copy = malloc(sizeof *copy); // Parens optional for vars
if (copy) {
memcpy(copy, src, sizeof(*copy));
}
return copy;
}
Exploited in the Wild
OpenSSL Buffer Overflows
Historical OpenSSL vulnerabilities have involved incorrect sizeof usage leading to buffer overflows in cryptographic operations.
Linux Kernel Heap Overflows
Linux kernel CVEs have resulted from sizeof misuse when allocating kernel structures, enabling privilege escalation.
Embedded Systems Corruption
Resource-constrained embedded systems have experienced memory corruption from sizeof errors where limited testing didn't expose the bugs.
Tools to test/exploit
-
Coverity — detects sizeof on pointer type.
-
PVS-Studio — warns about suspicious sizeof usage.
-
Clang Static Analyzer — catches sizeof pointer issues.
-
cppcheck — open-source static analysis.
CVE Examples
-
CVE-2017-9445 — systemd-resolved heap buffer overflow.
-
CVE-2016-7117 — Linux kernel recvmmsg sizeof issue.
-
Multiple buffer overflow CVEs traced to sizeof misuse.
References
-
MITRE. "CWE-467: Use of sizeof() on a Pointer Type." https://cwe.mitre.org/data/definitions/467.html
-
CERT C. "ARR01-C: Do not apply the sizeof operator to a pointer when taking the size of an array." https://wiki.sei.cmu.edu/confluence/display/c/