2

I have been trying to intercept calls to malloc and free, following our textbook (CSAPP book). I have followed their exact code, and nearly the same code that I found online and I keep getting a segmentation fault. I heard our professor saying something about printf that mallocs and frees memory so I think that this happens because I am intercepting a malloc and since I am using a printf function inside the intercepting function, it will call itself recursively. However I can't seem to find a solution to solving this problem? Our professor demonstrated that intercepting worked ( he didn't show us the code) and prints our information every time a malloc occurs, so I do know that it's possible. Can anyone suggest a working method??

Here is the code that I used and get nothing: mymalloc.c

#ifdef RUNTIME
// Run-time interposition of malloc and free based on // dynamic linker's (ld-linux.so) LD_PRELOAD mechanism #define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h> #include <dlfcn.h>
void *malloc(size_t size) {
static void *(*mallocp)(size_t size) = NULL; char *error;
void *ptr;
// get address of libc malloc
if (!mallocp) {
mallocp = dlsym(RTLD_NEXT, "malloc"); if ((error = dlerror()) != NULL) {
            fputs(error, stderr);
            exit(EXIT_FAILURE);
         }
}
ptr = mallocp(size);
printf("malloc(%d) = %p\n", (int)size, ptr); return ptr;
}
#endif

test.c

#include <stdio.h>
#include <stdlib.h>
int main(){
   printf("main\n");
   int* a = malloc(sizeof(int)*5);
   a[0] = 1;
   printf("end\n");
}

The result i'm getting:

$ gcc -o test test.c
$ gcc -DRUNTIME -shared -fPIC mymalloc.c -o mymalloc.so
$ LD_PRELOAD=./mymalloc.so ./test
Segmentation Fault

This is the code that I tried and got segmentation fault (from https://gist.github.com/iamben/4124829):

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void* malloc(size_t size)
{
        static void* (*rmalloc)(size_t) = NULL;
        void* p = NULL;

        // resolve next malloc
        if(!rmalloc) rmalloc = dlsym(RTLD_NEXT, "malloc");

        // do actual malloc
        p = rmalloc(size);

        // show statistic
        fprintf(stderr, "[MEM | malloc] Allocated: %lu bytes\n", size);

        return p;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STR_LEN 128

int main(int argc, const char *argv[])
{
        char *c;
        char *str1 = "Hello ";
        char *str2 = "World";

        //allocate an empty string
        c = malloc(STR_LEN * sizeof(char));
        c[0] = 0x0;

        //and concatenate str{1,2}
        strcat(c, str1);
        strcat(c, str2);

        printf("New str: %s\n", c);


        return 0;
}

The makefile from the git repo didn't work so I manually compiled the files and got:

$ gcc -shared -fPIC libint.c -o libint.so
$ gcc -o str str.c
$ LD_PRELOAD=./libint.so ./str
Segmentation fault

I have been doing this for hours and I still get the same incorrect result, despite the fact that I copied textbook code. I would really appreciate any help!!

juimdpp
  • 37
  • 1
  • 4
  • Try `fprintf(stderr, ...)` to avoid recursion. `stderr` is unbuffered. – chqrlie Dec 13 '20 at 11:39
  • @chqrlie I have already tried that and still get the same error... Might there be an issue with the gcc compiling statement?? – juimdpp Dec 13 '20 at 11:41
  • You could also try `sprintf(buf, ...)` to a a local array and `write(2, buf, strlen(buf))` – chqrlie Dec 13 '20 at 11:41
  • 1
    Does it work if you just write a constant string with `write()` from your `malloc`? – chqrlie Dec 13 '20 at 11:44
  • If you just want to trace malloc / free, you can use environment variables. see `man malloc` – chqrlie Dec 13 '20 at 11:47
  • @chqrlie: Buffering is not the issue, and `sprintf` with `write` will not work either. The issue is `printf` allocates some space to do its formatting work, and so does `sprintf`. That formatting is done preparing the string to be written to the stream, so it occurs before any buffering occurs. – Eric Postpischil Dec 13 '20 at 12:15
  • @chqrlie I did try using write which kind of worked! I'm getting prints of the "buf" excessively until I get Segmentation Fault, which confirms that it was the recursion that was the problem! Thank you. – juimdpp Dec 13 '20 at 13:16

1 Answers1

2

One way to deal with this is to turn off the printf when your return is called recursively:

static char ACallIsInProgress = 0;
if (!ACallIsInProgress)
{
    ACallIsInProgress = 1;
    printf("malloc(%d) = %p\n", (int)size, ptr);
    ACallIsInProgress = 0;
}
return ptr;

With this, if printf calls malloc, your routine will merely call the actual malloc (via mallocp) and return without causing another printf. You will miss printing information about a call to malloc that the printf does, but that is generally tolerable when interposing is being used to study the general program, not the C library.

If you need to support multithreading, some additional work might be needed.

The printf implementation might allocate a buffer only once, the first time it is used. In that case, you can initialize a flag that turns off the printf similar to the above, call printf once in the main routine (maybe be sure it includes a nice formatting task that causes printf to allocate a buffer, not a plain string), and then set the flag to turn on the printf call and leave it set for the rest of the program.

Another option is for your malloc routine not to use printf at all but to cache data in a buffer to be written later by some other routine or to write raw data to a file using write, with that data interpreted and formatted by a separate program later. Or the raw data could be written by a pipe to a program that formats and prints it and that is not using your interposed malloc.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Thank you for your answer! I wonder however, how the static char works. If the char is 0, it will enter the if condition and return the ptr. Then how will the following statement (where 0 is assigned to the char) execute? What is its purpose?? – juimdpp Dec 13 '20 at 13:16
  • Whilst debugging, I found out that it's not actually the printf function that was the problem, but actually the dlsym function (I tried a write() function (writing a string) before and after the dlsym, and the infinite loop of printing the string). How is that possible?? Do you have any ideas? – juimdpp Dec 13 '20 at 13:31
  • It is a shame `printf` allocates memory with `malloc` to perform such simple formatting! – chqrlie Dec 13 '20 at 13:57
  • @juimdpp: FYI, I edited the answer to change the code—I did not notice at first the `return` was on the same line as the `printf`. I have moved it outside the `if` statement. – Eric Postpischil Dec 13 '20 at 14:31
  • @juimdpp: The `static char ACallIsInProgress` serves to record whether the `malloc` routine is currently executing and calling `printf`. Declaring it with `static` creates a single `char` object that is used for all calls to the routine, rather than the default of each call having its own separate copy. After we set it to 1 and call `printf`, if `printf` then calls `malloc`, the routine will find `ACallIsInProgress` is not zero and will skip its call to `printf` and will return. Then the first call to `printf` will return, and `malloc` will reset `ACallIsInProgress` to 0 and return. – Eric Postpischil Dec 13 '20 at 14:34
  • 1
    @juimdpp: If `dlsym` is calling `malloc`, you may need to move the initialization of `mallocp` out of your `malloc` routine. – Eric Postpischil Dec 13 '20 at 14:37
  • Also make this variable thread-local. (Keyword `__thread` with some compilers.) – Zsigmond Lőrinczy Dec 13 '20 at 17:19