I have found a strange behavior while I was trying to use a library that I wrote a long time ago. The main problem is, when the program is executed on Fedora 25 and linked to my library by using LD_PRELOAD, the system raises a Segmentation Fault. I've made a small sample of my old library to easily understand the problem.
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
extern void *__libc_malloc(size_t size);
void *malloc(size_t size)
{
void *ptr = __libc_malloc(size);
fprintf(stdout, "Malloc(%d)->(%p)\n", (int) size, ptr);
return ptr;
}
This code was compiled by using these parameters:
gcc -c -fPIC -O3 -Wall -o libtest.o libtest.c
gcc -shared -o libtest.so libtest.o
The program was executed as follows:
LD_PRELOAD=./libtest.so ./progtest
I found out that the "fprintf" line was causing the Segfault problem. So I've changed the "stdout" file descriptor to "stderr", and the problem was gone.
Then I tested the same code using the "stdout" file descriptor as output for "fprintf" on another machine running CentOS 7, and it worked fine in both cases using "stdout" and "stderr".
By observing these results, I wonder what I am missing and why this is happening.
GLibc and GCC versions installed on Fedora 25:
GNU ld version 2.26.1-1.fc25
gcc (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1)
GLibc and GCC versions installed on CentOS 7:
GNU ld version 2.25.1-22.base.el7
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11)
fprintf
itself may need to use malloc
under some circumstances. One possible reason is that stderr
is unbuffered while stdout
is line buffered. fprintf(stdout)
might have a buffer already, or it may try to allocate one, ending up calling your malloc
, which calls into fprintf
again, but it is not re-entrant on the same FILE*
.
You can prevent re-entrancy with a flag, such as (C11):
#include <stdbool.h>
#include <threads.h>
thread_local bool inside_malloc = false;
void *malloc(size_t size) {
void *ptr = __libc_malloc(size);
if (!inside_malloc) {
inside_malloc = true;
fprintf(stdout, "Malloc(%zd)->(%p)\n", size, ptr);
inside_malloc = false;
}
return ptr;
}