Given a function declaration like this:
int base_address(zval *object, int add_prefix, char **base_address TSRMLS_DC) {
int result;
char *host;
long port;
char *prefix;
host = ... get host from object ...;
port = ... get port from object ...;
prefix = ... get prefix from object ...;
result = SUCCESS;
if (asprintf(base_address, "%s:%ld/%s", host, port, prefix) < 0) {
result = FAILURE;
}
return result;
}
void my_func() {
char *base_address;
char *ping_url;
if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) {
MALLOC_ERROR();
}
if (asprintf(&ping_url, "%s/ping", base_address) < 0) {
MALLOC_ERROR();
}
... do some stuff with base address ...
// release both, as everything worked
free(base_address);
free(ping_url);
}
If the first call to base_address succeeded and the second call to asprintf() failed, how do I cleanly skip to the end of the function in order to safely release allocated memory?
Is there some standard pattern how to avoid memory leaks in these situations where memory is allocated one after another (and each allocation might fail) without too much code duplication or goto statements?
If you attempt to read or write to memory that was previously freed, the result will be a conflict and the program will generate a memory error. For example, if a program calls the free() function for a particular block and then continues to use that block, it will create a reuse problem when a malloc() call is made.
Marks the chunk of memory previously used by the object as free. If possible, informs the operating system that a chunk of memory is free for other programs to use.
free() just declares, to the language implementation or operating system, that the memory is no longer required. When it is written over is not defined behavior.
Double Free A simple technique to avoid this type of vulnerability is to always assign NULL to a pointer after it has been freed. Subsequent attempts to free a null pointer will be ignored by most heap managers.
Don't be afraid of goto
. It's the simplest, cleanest, and most legible way of handling exceptions in C:
You don't repeat yourself. Duplicated code is error-prone.
You don't create deeply nested code. Deep nesting is illegible.
You don't hide behind do {...} while (0)
and break
. Good code says what it means.
Here's a basic example:
int operation() {
int result = SUCCESS;
if ((result = may_fail_first()) == FAILURE) {
goto failed_first;
}
if ((result = may_fail_second()) == FAILURE) {
goto failed_second;
}
// If your cleanup code doesn't ordinarily need to run.
goto end;
failed_second:
cleanup_second();
// If you don't need to clean up everything.
goto end;
failed_first:
cleanup_first();
end:
return result;
}
This is one of the sane usages of goto
for error handling:
if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) {
goto end;
}
if (asprintf(&ping_url, "%s/ping", base_address) < 0) {
goto release_address;
}
// do stuff
release_address:
free(base_address);
end:
This way you don't have to repeat the same releasing code in case you have many allocating calls which depend on each other.
You may want to refer to another of my answers here, which talks about the general case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With