Linux 在Fedora 25上使用LD_预加载会导致分段错误

Linux 在Fedora 25上使用LD_预加载会导致分段错误,linux,gcc,glibc,ld-preload,Linux,Gcc,Glibc,Ld Preload,当我试图使用我很久以前写的库时,我发现了一个奇怪的行为。主要问题是,当程序在Fedora 25上执行并使用LD_PRELOAD链接到我的库时,系统会引发分段错误。我对我的旧图书馆做了一个小样本,以便于理解这个问题 #include <stdio.h> #include <stdlib.h> #include <malloc.h> extern void *__libc_malloc(size_t size); void *malloc(size_t siz

当我试图使用我很久以前写的库时,我发现了一个奇怪的行为。主要问题是,当程序在Fedora 25上执行并使用LD_PRELOAD链接到我的库时,系统会引发分段错误。我对我的旧图书馆做了一个小样本,以便于理解这个问题

#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;
}
程序执行如下:

LD_PRELOAD=./libtest.so ./progtest
我发现“fprintf”线路导致SEGFULT问题。因此,我将“stdout”文件描述符更改为“stderr”,问题就解决了

然后,我在另一台运行CentOS 7的机器上,使用“stdout”文件描述符作为“fprintf”的输出,测试了相同的代码,使用“stdout”和“stderr”在这两种情况下都运行良好

通过观察这些结果,我想知道我遗漏了什么,为什么会发生这种情况

Fedora 25上安装的GLibc和GCC版本:

GNU ld版本2.26.1-1.fc25

gcc(gcc)6.3.1 20161221(红帽6.3.1-1)

CentOS 7上安装的GLibc和GCC版本:

GNU ld版本2.25.1-22.base.el7

gcc(gcc)4.8.5 20150623(红帽4.8.5-11)


fprintf
本身在某些情况下可能需要使用
malloc
。一个可能的原因是
stderr
是无缓冲的,而
stdout
是行缓冲的
fprintf(stdout)
可能已经有一个缓冲区,或者它可能尝试分配一个缓冲区,最后调用您的
malloc
,它再次调用
fprintf
,但它不会重新进入同一
文件*

您可以使用标志(如(C11))防止重新进入:

#包括
#包括
线程\u本地布尔内部\u malloc=false;
void*malloc(大小){
void*ptr=uuu libc_malloc(大小);
如果(!in_malloc){
内部_malloc=真;
fprintf(标准值,“Malloc(%zd)->(%p)\n)”,大小,ptr;
内部_malloc=假;
}
返回ptr;
}

另一种方法是放弃使用fprintf,而是将snprintf用于本地缓冲区,并调用write()输出由snprintf@nos这可能会奏效,尽管我仍然担心
snprintf
可能涉及分配。
LD_PRELOAD=./libtest.so ./progtest
#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;
}