Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proving that "int *p = malloc(1); p[0]" is undefined behavior

I'm trying to convince (citing the specific parts of the C99 standard) a colleague that the following is undefined behavior:

int *p = malloc(1);
p[0] = 0;

But I cannot find the specific parts in the standard which clearly ensure that this is undefined. I'm looking specifically for the logical steps in the standard which lead from these lines to the conclusion: undefined behavior. Is it the conversion from void * to int * in the first line? The assignment in the second line?

The only relevant part I can find about malloc is that it returns a suitably aligned pointer (7.20.3):

The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object or an array of such objects in the space allocated (...)

I tried grepping for space in the norm, but there's too much noise due to white space and other lexical issues.

like image 275
anol Avatar asked Nov 18 '15 09:11

anol


4 Answers

Adding from 7.20.3.3 The malloc function to your quote:

The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate.
The malloc function returns either a null pointer or a pointer to the allocated space.

So there are 2 possible sources of undefined behavior, one is overwriting (Size of int is guaranteed to be 16 bits or more, but you are allocating just 1 byte which is 8 bit on almost all systems) the buffer, and second is possible de-referencing of null-pointer.

From 6.5.2.1 Array subscripting, p[0] = 0 is equivalent to *p = 0. Type of *p is an int so it would fill sizeof(*p) * CHAR_BIT bits with 0 which may not all belong to the allocated buffer causing the UB.

There is no undefined behavior in first line of code (assignation), UB if any would be in second line (de-referencing).

But on machines where CHAR_BIT is large and sizeof(int) is 1, this will be well defined behavior for the cases when malloc doesn't return a null pointer.

like image 86
Mohit Jain Avatar answered Nov 19 '22 12:11

Mohit Jain


int *p = malloc(1);
p[0] = 0;

This is undefined behaviour because you have allocated 1 byte and in above assignment you are trying to write four bytes (assuming int is four bytes). This holds true as long as sizeof(int) > 1.

like image 29
Giorgi Moniava Avatar answered Nov 19 '22 10:11

Giorgi Moniava


Quotes from the standard:

J.2, Undefined behavior: The behavior is undefined in the following circumstances: ... An array subscript is out of range, even if an object is apparently accessible with the given subscript

6.2.5, Types, 20: An array type describes a contiguously allocated nonempty set of objects.

As long as sizeof(int) > 1, your malloc(1) did not allocate a nonempty set of objects, so the array size as allocated is zero and with p[0] you access with a subscript that is out of range. QED.

like image 5
Paul Ogilvie Avatar answered Nov 19 '22 10:11

Paul Ogilvie


6.5.3.2 Address and indirection operators

...

Semantics

The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue. Similarly, if the operand is the result of a [] operator, neither the & operator nor the unary * that is implied by the [] is evaluated and the result is as if the & operator were removed and the [] operator were changed to a + operator. Otherwise, the result is a pointer to the object or function designated by its operand.

The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.

The [] operator is an implied * operator on the pointer. The value assigned to the pointer is invalid for an int as long as sizeof( int ) > 1.

The behavior is undefined.

And NULL is an invalid pointer, so this also covers malloc() returning NULL.

like image 5
Andrew Henle Avatar answered Nov 19 '22 12:11

Andrew Henle