通过“LD_PRELOAD”替换“malloc”、“calloc”、“realloc”和“free”时出现分段错误
背景
我尝试更换malloc(3)/ calloc(3)/ realloc(3)/free(3)通过LD_PRELOAD环境变量。我尝试使用静态链接的自定义函数,它们工作得很好。
但是,当我将它作为共享库附加到 时LD_PRELOAD,它总是会导致段错误。
关于功能的简短技术说明
- 我使用 Linux x86-64
mmap(2)和munmap(2)syscall formalloc(3)andfree(3)。 - 这
calloc(3)只是对malloc(3)乘法溢出检查的调用。 - 该
realloc(3)电话malloc(3),然后复制旧的数据到新分配的内存和取消映射旧内存。
问题
- 我的方法有什么问题,以至于它总是导致段错误?
- 我该如何调试它(gdb 和 valgrind 也有段错误)?
- 我在这里错过了什么?
笔记
我完全意识到mmap每次malloc调用时总是使用是一个坏主意,尤其是对于性能而言。我只想知道为什么我的方法不起作用。
输出
ammarfaizi2@integral:~$ gcc -shared mem.c -O3 -o my_mem.so
ammarfaizi2@integral:~$ LD_PRELOAD=$(pwd)/my_mem.so ls
Segmentation fault (core dumped)
ammarfaizi2@integral:~$ LD_PRELOAD=$(pwd)/my_mem.so cat
Segmentation fault (core dumped)
ammarfaizi2@integral:~$ LD_PRELOAD=$(pwd)/my_mem.so w
Segmentation fault (core dumped)
ammarfaizi2@integral:~$ LD_PRELOAD=$(pwd)/my_mem.so gdb ls
Segmentation fault (core dumped)
ammarfaizi2@integral:~$ LD_PRELOAD=$(pwd)/my_mem.so valgrind ls
Segmentation fault (core dumped)
ammarfaizi2@integral:~$
glibc 版本
ammarfaizi2@integral:~$ /lib/x86_64-linux-gnu/libc.so.6
GNU C Library (Ubuntu GLIBC 2.33-0ubuntu2) release release version 2.33.
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 10.2.1 20210130.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
ammarfaizi2@integral:~$
代码mem.c
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <string.h>
static inline void *my_mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset)
{
void *ret;
register int _flags asm("r10") = flags;
register int _fd asm("r8") = fd;
register off_t _offset asm("r9") = offset;
asm volatile(
"syscall"
: "=a"(ret)
: "a"(9), "D"(addr), "S"(length), "d"(prot),
"r"(_flags), "r"(_fd), "r"(_offset)
: "memory", "r11", "rcx"
);
return ret;
}
static inline int my_munmap(void *addr, size_t length)
{
int ret;
asm volatile(
"syscall"
: "=a"(ret)
: "a"(11), "D"(addr), "S"(length)
: "memory", "r11", "rcx"
);
return ret;
}
#define unlikely(EXPR) __builtin_expect(EXPR, 0)
void * __attribute__((noinline)) malloc(size_t len)
{
void *start_map;
uintptr_t user_ptr, cmperr;
size_t add_req = 0;
add_req += sizeof(size_t);
add_req += sizeof(uint8_t);
add_req += 0x1full;
start_map = my_mmap(NULL, add_req + len, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
cmperr = 0xffffffffffffff00ull;
if (unlikely(((uintptr_t)start_map & cmperr) == cmperr)) {
errno = ENOMEM;
return NULL;
}
/* Align 32-byte and take space to save the length and diff */
user_ptr = ((uintptr_t)start_map + add_req) & ~0x1full;
*(size_t *)(user_ptr - 8) = len;
*(uint8_t *)(user_ptr - 9) = (uint8_t)(user_ptr - (uintptr_t)start_map);
return (void *)user_ptr;
}
void free(void *__user_ptr)
{
size_t len;
uint8_t diff;
uintptr_t user_ptr = (uintptr_t)__user_ptr;
len = *(size_t *)(user_ptr - 8);
diff = *(uint8_t *)(user_ptr - 9);
my_munmap((void *)(user_ptr - diff), len);
}
void *realloc(void *__user_ptr, size_t new_len)
{
void *new_mem;
size_t len;
uint8_t diff;
uintptr_t user_ptr = (uintptr_t)__user_ptr;
len = *(size_t *)(user_ptr - 8);
diff = *(uint8_t *)(user_ptr - 9);
new_mem = malloc(new_len);
if (unlikely(new_mem == NULL))
return NULL;
memcpy(new_mem, __user_ptr, (new_len < len) ? new_len : len);
my_munmap((void *)(user_ptr - diff), len);
return new_mem;
}
void *calloc(size_t nmemb, size_t len)
{
size_t x = nmemb * len;
if (unlikely(nmemb != 0 && x / nmemb != len)) {
errno = EOVERFLOW;
return NULL;
}
return malloc(x);
}
// #include <stdio.h>
// int main(void)
// {
// char *test = malloc(1);
// for (size_t i = 2; i <= (1024 * 1024); i++) {
// test = realloc(test, i);
// memset(test, 'q', i);
// }
// free(test);
// }
重新编译-Wall -Wextra -ggdb3并strace输出
ammarfaizi2@integral:~$
ammarfaizi2@integral:~$ gcc -Wall -Wextra -ggdb3 -shared mem.c -O3 -o my_mem.so
ammarfaizi2@integral:~$ strace -tf /usr/bin/env LD_PRELOAD=$(pwd)/my_mem.so ls
12:59:15 execve("/usr/bin/env", ["/usr/bin/env", "LD_PRELOAD=/home/ammarfaizi2/my_"..., "ls"], 0x7ffd2d6ee188 /* 34 vars */) = 0
12:59:15 brk(NULL) = 0x565552193000
12:59:15 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd13017120) = -1 EINVAL (Invalid argument)
12:59:15 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
12:59:15 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
12:59:15 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
12:59:15 newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=74118, ...}, AT_EMPTY_PATH) = 0
12:59:15 mmap(NULL, 74118, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7facba2ba000
12:59:15 close(3) = 0
12:59:15 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
12:59:15 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
12:59:15 read(3, "177ELF2113 3 >