1

I am reading the book "Effective C" by Robert C. Seacord. In this book, it has an exercise where you intentionally double-free a pointer so you can test using dmalloc to debug the cause. However, it doesn't fail as expected.

#include <string.h>
#include <stdlib.h>

#ifdef DMALLOC
#include "dmalloc.h"
#endif

void usage(char *msg) {
    fprintf(stderr, "%s", msg);
    free(msg);
    return;
}

int main(int argc, char *argv[]) {
    if (argc != 3 && argc !=4) {
        /* The error message won't be more than 80 chars */
        char *errmsg = (char *)malloc(80);
        sprintf(
            errmsg,
            "Sorry %s,\nUsage: caesar secret_file keys_file [output_file]\n",
            getenv("USER")
        );
        usage(errmsg);
        free(errmsg);
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

Its clear here that *errmsg should get freed twice: First by the usage function when its passed to it, and then right after in main. Why doesn't this fail when ran with no arguments? I am using linux (POP!_OS 20.04) with GCC 9.3.0.

EDIT: For more context the book suggests I should see an output like this:

% ./caesar
Sorry student,
Usage: caesar secret_file keys_file [output_file]
debug-malloc library: dumping program, fatal error
  Error: tried to free previously freed pointer (err 61)
Aborted (core dumped)

Adding more calls to free doesn't do anything either. I get the usage portion but not a core dump.

Raven King
  • 155
  • 1
  • 12
  • 1
    You don't do anything after the double free. And a double free invokes undefined behavior. "Undefined behavior" means anything can happen - and that includes appearing to work normally. – Andrew Henle Feb 10 '21 at 12:20
  • 1
    undefined behavior: behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements. Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). From [the C11 standard](https://port70.net/~nsz/c/c11/n1570.html#3.4.3) – anastaciu Feb 10 '21 at 12:24
  • So this means its simply ignoring the behavior? Does that mean that because the exercise has non-portable code it simply doesn't work in my case? – Raven King Feb 10 '21 at 12:26
  • @RavenKing, no diagnostics message is required as defined in the standard, it may or may not work, *no requirements* means exactly that. Bottom line the error is there and makes your program unreliable, so you should fix it. – anastaciu Feb 10 '21 at 12:29
  • I'd say the book is wrong, different implementations can output different results, no specific error or diagnostic message is required. – anastaciu Feb 10 '21 at 12:33
  • 2
    If you cannot notice that there is a problem in your heap after freeing twice, that does not mean that there is no problem. You could also get into some trouble later. "It seems to work" is basically the worst outcome of undefined behaviour. – Gerhardh Feb 10 '21 at 12:33
  • @Gerhardh from what I just learned, it looks like the purpose of the dmalloc library is to prevent that very scenario thankfully. – Raven King Feb 10 '21 at 12:39

1 Answers1

1

I am sorry for taking up people's time with this. I figured it out. That crash behavior is supposed to be provided by dmalloc, however its usage has changed a little since the writing of the book I am reading. I needed to add -DDMALLOC_FUNC_CHECK to the compiler options in order for it to produce the expected result.

I learned its dmalloc, not the OS that causes the program to crash when you double-free the pointer.

Raven King
  • 155
  • 1
  • 12