[转] 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/
[转] Dangers of using dlsym() with RTLD_NEXT的更多相关文章
- Android so 文件进阶<二> 从dlsym()源码看android 动态链接过程
0x00 前言 这篇文章其实是我之前学习elf文件关于符号表的学习笔记,网上也有很多关于符号表的文章,怎么说呢,感觉像是在翻译elf文件格式的文档一样,千篇一律,因此把自己的学习笔记分享出来.dls ...
- 加载动态链接库——dlopen dlsym dlclose
DLOPEN DLMOPEN DLCLOSE NAME dlclose, dlopen, dlmopen - 打开/关闭共享对象 SYNOPSIS #include <dlfcn.h&g ...
- 采用dlopen、dlsym、dlclose加载动态链接库【总结】(转)
1.前言 为了使程序方便扩展,具备通用性,可以采用插件形式.采用异步事件驱动模型,保证主程序逻辑不变,将各个业务已动态链接库的形式加载进来,这就是所谓的插件.linux提供了加载和处理动态链接库的系统 ...
- dlopen、dlsym和dlclose的使用
在dlopen()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给dlsym()的调用进程.使用dlclose()来卸载打开的库. dlopen: dlopen() The function ...
- 采用dlopen、dlsym、dlclose加载动态链接库【总结】
摘自http://www.cnblogs.com/Anker/p/3746802.html 采用dlopen.dlsym.dlclose加载动态链接库[总结] 1.前言 为了使程序方便扩展,具备通 ...
- gcc编译出现dlopen、dlerror、dlsym、dlcolse的解决方法
➜ test_sqlite3 gcc *.c -I . -o xixi -pthread /tmp/cckGKTrr.o: In function `unixDlOpen': sqli ...
- 【转】采用dlopen、dlsym、dlclose加载动态链接库
1.前言 为了使程序方便扩展,具备通用性,可以采用插件形式.采用异步事件驱动模型,保证主控制逻辑不变,将各个业务以动态链接库的形式加载进来,这就是所谓的插件.linux提供了加载和处理动态链接库的系统 ...
- 用dlopen,dlsym加载动态链接库.so中函数
代码如下 static void *findSymbol(const char *path, const char *symbol) { void *handle = dlopen(path, RTL ...
- the example of dlsym
void *handle; int i, (*fptr)(int); /* open the needed object */ handle = dlopen("/usr/home/me/l ...
随机推荐
- Redis设置Key/value的规则定义和注意事项(附工具类)
对于redis的存储key/value键值对,经过多次踩坑之后,我们总结了一套规则:这篇文章主要讲解定义key/value键值对时的定义规则和注意事项. 前面一篇文章讲了如何定义Redis的客户端和D ...
- C#中decimal ,double,float的区别
浮点型 Name CTS Type Description Significant Figures Range (approximate) float System.Single 32-bit sin ...
- PDA智能程序访问WebService,报告“未能建立与网络的连接”
其实就是你没又连接上网络.首先下个第三方软件关于vs模拟器连接的.然后根据以下说明操作就可以连接了在确保主机已连上互联网的情况下,按以下步骤设置: 1.打开ActiveSync ,点击“文件”——&g ...
- 解决python中遇到的乱码问题
1. 解决中文乱码的一种可行方法 # -*- coding:utf-8 -*- from __future__ import unicode_literals import chardet def s ...
- Python列表split方法
转载自:https://www.cnblogs.com/douzi2/p/5579651.html 1.split翻译为分裂. split()就是将一个字符串分裂成多个字符串组成的列表. 2.spl ...
- Spark学习入门
Spark 是一种“One Stack to rule them all”通用的大数据计算框架,期望使用一个技术栈就完美地 解决大数据领域的各种计算任务. Spark特点:速度快.容易上手开发.超强的 ...
- 033 关于YARN的HA
一:准备 1.规划 namenode namenode ZKFC ZKFC journalnode journalnode jou ...
- 深入理解类成员函数的调用规则(理解成员函数的内存为什么不会反映在sizeof运算符上、类的静态绑定与动态绑定、虚函数表)
本文转载自:http://blog.51cto.com/9291927/2148695 总结: 一.成员函数的内存为什么不会反映在sizeof运算符上? 成员函数可以被看作是类 ...
- Burp Suite之爬网模块(二)
Spider功能 Burp Spider爬网介绍 Burp Spider 是一个映射 web 应用程序的工具.它使用多种智能技术对一个应用程序的内容和功能进行全面的清查. 通过跟踪 HTML 和 Ja ...
- Selenium3 + Python3自动化测试系列二——selenium元素定位
一.selenium元素定位 Selenium对网页的控制是基于各种前端元素的,在使用过程中,对于元素的定位是基础,只有准去抓取到对应元素 才能进行后续的自动化控制,我在这里将对selenium8种元 ...