...
In this noncompliant example, exit code is written for every instance in which the function can terminate prematurely. Notice how failing to allocate obj3
obj2
produces a memory leak and fails to close the opened file.
Code Block | ||
---|---|---|
| ||
typedef struct object { // A generic struct -- The contents don't matter int propertyA, propertyB, propertyC; } object_t; int do_something(void){ FILE *fin; object_t *obj1, *obj2, *obj3; fin = fopen("some_file", "r"); if (fin == NULL){ return -1; } obj1 = malloc(sizeof(object_t)); if (obj1 == NULL){ fclose(fin); return -1; } obj2 = malloc(sizeof(object_t)); if (obj2 == NULL){ fclose(fin); free(obj1); return -1; } obj3 = malloc(sizeof(object_t)); if (obj3 == NULL){ fclose(fin); free(obj2); return -1; // Forgot to free obj1 -- Memory leak!! } // ... more code ... } |
This is also just a small example; in much larger examples, errors like this would be even harder to detect.
...
Code Block | ||
---|---|---|
| ||
// ... assume the same struct as above ... int do_something(void){ FILE *fin; object_t *obj1, *obj2, *obj3; fin = fopen("some_file", "r"); if (fin == NULL){ goto FAIL_FIN; } obj1 = malloc(sizeof(object_t)); if (obj1 == NULL){ goto FAIL_OBJ1; } obj2 = malloc(sizeof(object_t)); if (obj2 == NULL){ goto FAIL_OBJ2; } obj3 = malloc(sizeof(object_t)); if (obj3 == NULL){ goto FAIL_OBJ3; } // ... more code ... SUCCESS: // Return normally Free everything and return normally fclose(fin); free(obj1); free(obj2); return 0; FAIL_OBJ3OBJ2: // Otherwise, free objects in order free(obj2); FAIL_OBJ2: free(obj1); FAIL_OBJ1: fclose(fin); FAIL_FIN: return -1; } |
...
Wiki Markup |
---|
\[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.20.3, "Memory management functions" \[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.19.5, "File access functions" \[[Seacord 05|AA. C References#Seacord 05]\] Chapter 4, "Dynamic Memory Management" |
Related Vulnerabilities
In C++, one could use this same strategy when constructing and destructing objects.