Skip to content

Commit b22777a

Browse files
committed
fix #3764
This answer a static analysis tool, which complains that a buffer allocation is not free() just before exit(). In general, this requirement is not necessary, because invoking exit() will make the OS reclaim all buffers from the terminated process. I could articulate this new requirement in a "not too heavy" way with the use of a new macro, `CONTROL_EXIT()`. But "not too heavy" is still a form of maintenance burden: whenever the code is modified, by adding, removing or changing some of these buffers, it requires some form of coordination with exit points, which is easy to let go wrong. Besides, I wouldn't be surprised if there were some more complex scenarios left, typically across multiple levels of functions, where a call to `exit()` is made while some other buffers, inaccessible from the function, are still allocated. Tackling such issues would require a very different approach, typically forbidding the use of `exit()`, which was meant to simplify code maintenance by reducing the nb and complexity of error paths. I question the need to make the code more complex to read and maintain, just to tackle a largely theoretical problem with no practical impact on target platforms.
1 parent 1c007e6 commit b22777a

File tree

1 file changed

+12
-7
lines changed

1 file changed

+12
-7
lines changed

programs/util.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,17 @@ extern "C" {
5353
* It's designed for failures that may happen rarely,
5454
* but we don't want to maintain a specific error code path for them,
5555
* such as a malloc() returning NULL for example.
56-
* Since it's always active, this macro can trigger side effects.
5756
*/
58-
#define CONTROL(c) { \
57+
#define CONTROL(c) CONTROL_EXIT(c, {})
58+
59+
/* CONTROL_EXIT:
60+
* Same as CONTROL, but can also run a last action before exit()
61+
*/
62+
#define CONTROL_EXIT(c, _action) { \
5963
if (!(c)) { \
60-
UTIL_DISPLAYLEVEL(1, "Error : %s, %i : %s", \
64+
UTIL_DISPLAYLEVEL(1, "Error : %s, %i : %s not respected", \
6165
__FILE__, __LINE__, #c); \
66+
{ _action; } \
6267
exit(1); \
6368
} }
6469

@@ -685,7 +690,7 @@ UTIL_createFileNamesTable_fromFileName(const char* inputFileName)
685690
}
686691

687692
{ const char** filenamesTable = (const char**) malloc(nbFiles * sizeof(*filenamesTable));
688-
CONTROL(filenamesTable != NULL);
693+
CONTROL_EXIT(filenamesTable != NULL, free(buf));
689694

690695
{ size_t fnb;
691696
for (fnb = 0, pos = 0; fnb < nbFiles; fnb++) {
@@ -774,12 +779,12 @@ UTIL_mergeFileNamesTable(FileNamesTable* table1, FileNamesTable* table2)
774779
newTotalTableSize = getTotalTableSize(table1) + getTotalTableSize(table2);
775780

776781
buf = (char*) calloc(newTotalTableSize, sizeof(*buf));
777-
CONTROL ( buf != NULL );
782+
CONTROL_EXIT ( buf != NULL, UTIL_freeFileNamesTable(newTable) );
778783

779784
newTable->buf = buf;
780785
newTable->tableSize = table1->tableSize + table2->tableSize;
781786
newTable->fileNames = (const char **) calloc(newTable->tableSize, sizeof(*(newTable->fileNames)));
782-
CONTROL ( newTable->fileNames != NULL );
787+
CONTROL_EXIT ( newTable->fileNames != NULL, { free(buf); UTIL_freeFileNamesTable(newTable); } );
783788

784789
{ unsigned idx1;
785790
for( idx1=0 ; (idx1 < table1->tableSize) && table1->fileNames[idx1] && (pos < newTotalTableSize); ++idx1, ++newTableIdx) {
@@ -1266,7 +1271,7 @@ void UTIL_mirrorSourceFilesDirectories(const char** inFileNames, unsigned int nb
12661271
for (i = 0; i < nbFile; ++i) {
12671272
if (isFileNameValidForMirroredOutput(inFileNames[i])) {
12681273
char* fname = STRDUP(inFileNames[i]);
1269-
CONTROL(fname != NULL);
1274+
CONTROL_EXIT(fname != NULL, free(srcFileNames));
12701275
srcFileNames[validFilenamesNr++] = fname;
12711276
}
12721277
}

0 commit comments

Comments
 (0)