Many functions require the allocation of multiple resources. Failing and returning somewhere in the middle of this function without freeing all of the allocated resources could produce a memory leak. It is a common error to forget to free one (or all) of the resources in this manner, so a goto-chain is the simplest and cleanest way to organize exits when order is preservedwhile preserving the order of freed resources.
Noncompliant Code Example
In this noncompliant example, exit code is written for every instance in which the function can terminate prematurely. Notice how failing to allocate obj2
close fin2
produces a memory leak and fails to close the opened fileresource leak, leaving an open file descriptor.
Wiki Markup |
---|
_Please note that these examples assume {{errno_t}} and {{NOERR}} to be defined, as requested by \[[DCL09-C. Declare functions that return an errno error code with a return type of errno_t|DCL09-C. Declare functions that return an errno error code with a return type of errno_t]\]. An equivalent compatible example would define {{errno_t}} as an {{int}} and {{NOERR}} as zero._ |
Code Block | ||
---|---|---|
| ||
typedef struct object { // A generic struct -- The contents don't matter int propertyA, propertyB, propertyC; } object_t; errno_t do_something(void){ FILE *fin1, *finfin2; object_t *obj1, *obj2obj; errno_t ret_val; finfin1 = fopen("some_file", "r"); if (finfin1 == NULL) { return errno; } obj1fin2 = malloc(sizeof(object_t)fopen("some_other_file", "r"); if (obj1fin2 == NULL) { ret_val = errno; fclose(finfin1); return ret_valerrno; } obj2obj = malloc(sizeof(object_t)); if (obj2obj == NULL) { ret_val = errno; fclose(finfin1); return ret_val; // Forgotforgot to freeclose obj1fin2 !! } // ... more code ... fclose(finfin1); freefclose(obj1fin2); free(obj2obj); return NOERR; } |
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 ... errno_t do_something(void) { FILE *fin1, *finfin2; object_t *obj1, *obj2obj; errno_t ret_val = NOERR; // Initially assume a successful return value finfin1 = fopen("some_file", "r"); if (fin == NULL) { ret_val = errno; goto FAIL_FINFIN1; } obj1fin2 = malloc(sizeof(object_t))fopen("some_other_file", "r"); if (obj1fin2 == NULL) { ret_val = errno; goto FAIL_OBJ1FIN2; } obj2obj = malloc(sizeof(object_t)); if (obj2obj == NULL) { ret_val = errno; goto FAIL_OBJ2OBJ; } // ... more code ... SUCCESS: // Clean Freeup everything free(obj2obj); FAIL_OBJ2OBJ: // Otherwise, freeclose only the objectsresources we allocatedopened freefclose(obj1fin2); FAIL_OBJ1FIN2: fclose(finfin1); FAIL_FINFIN1: return ret_val; } |
The benefits of this method are that the code is cleaner and we prevent the rewriting of similar code upon every function error.
Compliant Solution (copy_process()
from Linux kernel)
Many Some effective examples of goto-chains are quite large. The following compliant code is an excerpt from the Linux kernel. This is the copy_process
function from kernel/fork.c
from Version 2.6.29 of the kernel.
...