You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 25 Next »

An object of type void * is a generic data pointer. It can point to any data object. For any incomplete or object type T, C permits implicit conversion from T * to void * or from void * to T *. The Standard C Library uses void * to declare parameters and return types of functions designed to work for objects of different types. Such is the case with the standard memory allocation functions malloc, calloc, and realloc.

For example, the Library declares malloc as:

void *malloc(size_t);

Calling malloc( n ) allocates memory for an object whose size is n and returns either a null pointer or a pointer to the allocated memory. A program can implicitly convert the pointer that malloc returns into a different pointer type.

Code that follows this recommendation will compile and execute equally well in C++.

Non-Compliant Code Example

The argument to malloc can be any value of (unsigned) type size_t. If the program uses the allocated storage to represent an object (possibly an array) whose size is greater than the requested size, the behavior is undefined. The implicit pointer conversion lets this slip by without complaint from the compiler.

For example,

#include <stdlib.h>

typedef struct gadget gadget;
struct gadget {
    int i;
	double d;
};

typedef struct widget widget;
struct widget {
	char c[10];
    int i;
	double d;
};

widget *p;

..

p = malloc(sizeof(gadget)); /* imminent problem */
if (p != NULL) {
    p->i = 0;               /* undefined behavior */
	p->d = 0.0;             /* undefined behavior */
}

An implementation may add padding to gadget or widget so that sizeof(gadget) equals sizeof(widget), but this is highly unlikely. More likely, sizeof(gadget) is less than sizeof(widget). In that case,

p = malloc(sizeof(gadget)); /* imminent problem */

quietly assigns p to point to storage too small for a widget. The subsequent assignments to p->i and p->d produce undefined behavior. They'll likely produce memory overruns.

Compliant Solution

Casting the result of malloc to the appropriate pointer type enables the compiler to catch subsequent inadvertant pointer conversions. When allocating individual objects, the "appropriate pointer type" is a pointer to the type argument in the sizeof expression passed to malloc, as in:

widget *p;

..

p = (gadget *)malloc(sizeof(gadget)); /* invalid assignment */

Here, malloc allocates space for a gadget and the cast immediately converts the returned pointer to a gadget *. This lets the compiler detect the invalid assignment, because it attempts to convert a gadget * into a widget *.

Repeating the same type in the sizeof expression and the pointer cast is easy to do, but still invites errors. Packaging the repetition in a macro, such as:

#define MALLOC(type) ((type *)malloc(sizeof(type)))

further reduces the possibility of error.

widget *p;

..

p = MALLOC(widget);     /* OK */
if (p != NULL) {
    p->i = 0;           /* OK */
	p->d = 0.0;         /* OK */
}

Here, the entire allocation expression (to the right of the assignment operator) allocates storage for a widget and returns a widget *. If p were not a widget *, the compiler would complain about the assignment.

Compliant Solution

When allocating an array with N elements of type T, the appropriate type in the cast expression is still T *, but the argument to malloc should be of the form N * sizeof(T). Again, packaging this form as a macro, such as:

#define MALLOC_ARRAY(number, type) \
    ((type *)malloc(number * sizeof(type)))

reduces the chance of error in an allocation expression:

enum { N = 16 };
widget *p;

..

p = MALLOC_ARRAY(N, widget);    /* OK */

Compliant Solution

A small collection of macros can provide secure implementations for common uses for the standard memory allocation functions.

#define MALLOC(type) ((type *)malloc(sizeof(type)))

allocates a single object using malloc.

#define MALLOC_ARRAY(number, type) \
    ((type *)malloc(number * sizeof(type)))

allocates an array of objects using malloc.

#define MALLOC_FLEX(stype, number, etype) \
    ((stype *)malloc(sizeof(stype) + number * sizeof(etype)))

allocates a single object with a flexible array member using malloc.

#define CALLOC(number, type) \
    ((type *)calloc(number, sizeof(type)))

allocates an array of objects using calloc.

#define REALLOC_ARRAY(pointer, number, type) \
    ((type *)realloc(pointer, number * sizeof(type)))

reallocates an array of objects using realloc.

#define REALLOC_FLEX(pointer, stype, number, etype) \
    ((stype *)realloc(pointer, sizeof(stype) + number * sizeof(etype)))

reallocates a single object with a flexible array member using realloc.

For example,

enum month

Unknown macro: { Jan, Feb, ... }

;
type enum month month;

typedef enum date date;
struct date {
unsigned char dd;
month mm;
unsigned yy;
};

typedef struct string string;
struct string

Unknown macro: { size_t length; char text[];}; date *d, *week, *fortnight;string *name; d = MALLOC(date);week = MALLOC_ARRAY(7, date);name = MALLOC_FLEX(string, 16, char);fortnight = CALLOC(14, date);{code}

Risk Assessment

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

MEM02-A

1 (low)

1 (unlikely)

3 (low)

P3

L3

Related Vulnerabilities

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

References

[[Summit 05]] Question 7.7, Question 7.7b


[!CERT C Secure Coding Standard^button_arrow_left.png!]      [!CERT C Secure Coding Standard^button_arrow_up.png!]       [!CERT C Secure Coding Standard^button_arrow_right.png!]

  • No labels