...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> struct S { size_t len; char buf[]; /* Flexible array member */ }; const char *find(const struct S *s, int c) { const char *first = s->buf; const char *last = s->buf + s->len; while (first != last) { /* Avoid incrementing here */ if (*++first == (unsigned char)c) { return first; } } return NULL; } void g(void) { struct S *s = (struct S *)malloc(sizeof(struct S)); if (s == NULL) { /* handle error */ } s->len = 0; find(s, 'a'); } |
Noncompliant Code Example (null pointer addition)
...
The following noncompliant code example is similar to a vulnerability in Adobe Flash player that was first exploited in 2008. (See http://www.securityfocus.com/blogs/746 for more information). This code allocates a block of memory, and initializes it with some data. The data does not belong at the beginning of the block, which is left uninitialized. Instead, it is placed offset
bytes within the block. The function ensures that the data fits within the allocated block.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h>
char* init_block(size_t block_size, size_t offset,
char* data, size_t data_size) {
char* buffer = malloc(block_size);
if (data_size > block_size || block_size - data_size > offset) {
/* data won't fit in buffer, handle error */
}
memcpy( buffer + offset, data, data_size);
return buffer;
}
|
This function fails to check if the allocation succeeds. If the allocation fails, then malloc()
returns a null pointer. The call to memcpy()
does not dereference this pointer, but instead performs pointer arithmetic on it and tries to write to the resulting pointer. Because a null pointer does not point into valid memory, the results are undefined.
By not checking if malloc()
succeeds, this code also violates ERR33-C. Detect and handle standard library errors.
An attacker can use this function to write to arbitrary memory. They pass a large value to block_size
, such as SIZE_MAX
...such a large size usually causes malloc()
to fail. They then convert an address of memory they wish to overwrite to a size_t
integer, and pass it as the offset
parameter. They then set data
and data_size
to refer correctly to memory containing the data they wish to write into the memory indicated by offset
. Consequently, the call to memcpy()
happily writes the memory of their choice.
Compliant SolutionSolution
This compliant solution also checks that malloc()
succeeds.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> char* init_block(size_t block_size, size_t offset, char* data, size_t data_size) { char* buffer = malloc(block_size); if (NULL == buffer) { /* handle error */ } if (data_size > block_size || block_size - data_size > offset) { /* data won't fit in buffer, handle error */ } memcpy( buffer + offset, data, data_size); return buffer; } |
...