[转] Dangers of using dlsym() with RTLD_NEXT
There are times when you want to wrap a library function in order to provide some additional functionality. A common example of this is wrapping the standard library’s malloc()
and free()
so that you can easily track memory allocations in your program. While there are several techniques for wrapping library functions, one well-known method is using dlsym()
with RTLD_NEXT
to locate the wrapped function’s address so that you can correctly forward calls to it.
Problem
So what can go wrong? Let’s look at an example:
LibWrap.h
void* memAlloc(size_t s); |
LibWrap.c
#define _GNU_SOURCE |
Main.c
#include <malloc.h> |
First compile LibWrap.c
into a shared library:
$ gcc -Wall -Werror -fPIC -shared -o libWrap.so LibWrap.c
Next compile Main.c
and link it against the libWrap.so
that we just created:
$ gcc -Wall -Werror -o Main Main.c ./libWrap.so -ldl
Time to run the program!
$ ./Main
Bytes allocated before malloc: 0
Bytes allocated after malloc: 80
Bytes allocated after free: 0
So far, so good. No surprises. We allocated a bunch of memory and then freed it. The statistics returned by mallinfo()
confirm this.
Out of curiosity, let’s look at ldd
output for the application binary we created.
$ ldd Main
linux-vdso.so.1 => (0x00007fff1b1fe000)
./libWrap.so (0x00007fe7d2755000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe7d2542000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe7d217c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe7d2959000)
Take note of the relative placement of libWrap.so
with respect to libc.so.6
: libWrap.so
comes before libc.so.6
. Remember this. It will be important later.
Now for fun, let’s re-compile Main.c
with libc.so.6
explicitly specified on the command-line and coming before libWrap.so
:
$ gcc -Wall -Werror -o Main Main.c /lib/x86_64-linux-gnu/libc.so.6 ./libWrap.so -ldl
Re-run:
$ ./Main
Bytes allocated before malloc: 0
Bytes allocated after malloc: 80
Bytes allocated after free: 80
Uh oh, why are we leaking memory all of a sudden? We de-allocate everything we allocate, so why the memory leak?
It turns out that the leak is occurring because we are not actually forwarding malloc()
and free()
calls to libc.so.6
‘s implementations. Instead, we are forwarding them to malloc()
and free()
inside ld-linux-x86-64.so.2
!
“What are you talking about?!” you might be asking.
Well, it just so happens that ld-linux-x86-64.so.2
, which is the dynamic linker/loader, has its own copy of malloc()
and free()
. Why? Because ld-linux
has to allocate memory from the heap before it loads libc.so.6
. But the version of malloc/free
that ld-linux
has does not actually free memory!
[RTLD_NEXT] will find the next occurrence of a function in the search order after the current library. This allows one to provide a wrapper around a function in another shared library.But why does libWrap.so
forward calls to ld-linux
instead of libc
? The answer comes down to how dlsym()
searches for symbols when RTLD_NEXT
is specified. Here’s the relevant excerpt from the dlsym(3)
man page:— dlsym(3)
To understand this better, take a look at ldd
output for the new Main
binary:
$ ldd Main
linux-vdso.so.1 => (0x00007fffe1da0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f32c2e91000)
./libWrap.so (0x00007f32c2c8f000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f32c2a8a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f32c3267000)
Unlike earlier, libWrap.so
comes after libc.so.6
. So when dlsym()
is called inside libWrap.so
to search for functions, it skips libc.so.6
since it precedes libWrap.so
in the search order list. That means the searches continue through to ld-linux-x86-64.so.2
where they find linker/loader’s malloc/free
and return pointers to those functions. And so, libWrap.so
ends up forwading calls to ld-linux
instead of libc
!
The answer is unfortunately no. At OptumSoft, we recently encountered this very same memory leak with a binary compiled using the standard ./configure && make
on x86-64 Ubuntu 14.04.1 LTS. For reasons we don’t understand, the linking order for the binary was such that using dlsym()
with RTLD_NEXT
to lookup malloc/free
resulted in pointers to implementations inside ld-linux
. It took a ton of effort and invaluable help from Mozilla’s rr tool to root-cause the issue. After the whole ordeal, we decided to write a blog post about this strange behavior in case someone else encounters it in the future.At this point you might be wondering: We ran a somewhat funky command to build our application and then encountered a memory leak due to weird library linking order caused by said command. Isn’t this whole thing a silly contrived scenario?
Solution
If you find dlsym()
with RTLD_NEXT
returning pointers to malloc/free
inside ld-linux
, what can you do?
For starters, you need to detect that a function address indeed does belong to ld-linux
using dladdr()
:
void* func = dlsym(RTLD_NEXT,"malloc"); |
Once you have figured out that a function is inside ld-linux
, you need to decide what to do next. Unfortunately, there is no straightforward way to continue searching for the same function name in all other libraries. But if you know the name of a specific library in which the function exists (e.g. libc), you can use dlopen()
and dlsym()
to fetch the desired pointer:
void* handle = dlopen("libc.so.6",RTLD_LAZY); |
Summary
- One can use
dlsym()
withRTLD_NEXT
to implement wrappers aroundmalloc()
andfree()
. - Due to unexpected linking behavior,
dlsym()
when usingRTLD_NEXT
can return pointers tomalloc/free
implementations insideld-linux
(dynamic linker/loader). Usingld-linux
‘smalloc/free
for general heap allocations leads to memory leaks because that particular version offree()
doesn’t actually release memory. - You can check if an address returned by
dlsym()
belongs told-linux
viadladdr()
. You can also lookup a function in a specific library usingdlopen()
anddlsym()
.
From: http://optumsoft.com/dangers-of-using-dlsym-with-rtld_next/
最新文章
- <;head>;<;/head>;
- 组合使用css选择器
- 【BZOJ-4456】旅行者 分治 + 最短路
- 获取SQLSERVER所有库 所有表 所有列 所有字段信息
- [Js/Jquery]立即执行匿名函数
- java用代理访问
- MySQL数据库优化技术之SQL语句慢查询定位
- 问题.NETSystem.Data.OleDb.OleDbException 操作必须使用一个可更新的查询
- Windows下的多线程
- 【转】 学习ios(必看经典)牛人40天精通iOS开发的学习方法【2015.12.2
- python小游戏实现代码
- search_word
- outlook gmail setting
- IntelliJ IDEA 2018.3.3配置 Tomcat 9,控制台出现中文乱码 “淇℃伅”(2019/01/25)
- spoj mpoint
- mysql 多实例安装
- 微擎框架下拉分页(使用js模板引擎)
- 算法之DP
- P2651 添加括号III
- Java Web项目中使用Freemarker生成Word文档
热门文章
- hdu 1072 有炸弹的迷宫 (DFS)
- POJ 2976 3111(二分-最大化平均值)
- CS224d 单隐层全连接网络处理英文命名实体识别tensorflow
- 解决Linux下Qt编译出现";cannot find -lGL";以及无法安装libgl1-mesa-dev的问题
- Codeforces Round #317 (div 2)
- 044 HIVE中的几种排序
- pyqt5 &#39;QWidget&#39; object has no attribute &#39;setCentralWidget&#39;(转)
- Linux- Showdown 命令详解
- P1862输油管道问题
- DB2<;RedHed Linux>; 创建数据库