glibc __libc_free() 源码分析

__libc_free源码分析涉及hook函数调用、_int_free中chunk检查、快速缓存(TCACHE)处理、fast bin管理和chunk的合并操作。当释放的chunk不来自mmap且满足条件时,会进行边界合并或加入unsorted bin。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

__libc_free()

  • 检查是否存在 hook函数,如果有就执行
void
__libc_free (void *mem)
{
   
   
  mstate ar_ptr;
  mchunkptr p;                          /* chunk corresponding to mem */

  void (*hook) (void *, const void *)
    = atomic_forced_read (__free_hook);
  if (__builtin_expect (hook != NULL, 0)) //hook函数不是null,就执行__free_hook对应的函数并返回
    {
   
   
      (*hook)(mem, RETURN_ADDRESS (0));
      return;
    }

  if (mem == 0)                              /* free(0) has no effect */
    return;

  p = mem2chunk (mem); //将指向user data 的指针转化为指向 chunk的指针

  if (chunk_is_mmapped (p)) //如果chunk是mmap分配的,就用munmap_chunk()函数释放                /* release mmapped memory. */
    {
   
   
      /* See if the dynamic brk/mmap threshold needs adjusting.
	 Dumped fake mmapped chunks do not affect the threshold.  */
      if (!mp_.no_dyn_threshold
          && chunksize_nomask (p) > mp_.mmap_threshold
          && chunksize_nomask (p) <= DEFAULT_MMAP_THRESHOLD_MAX
	  && !DUMPED_MAIN_ARENA_CHUNK (p))
        {
   
   
          mp_.mmap_threshold = chunksize (p);
          mp_.trim_threshold = 2 * mp_.mmap_threshold;
          LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2,
                      mp_.mmap_threshold, mp_.trim_threshold);
        }
      munmap_chunk (p);
      return;
    }

  MAYBE_INIT_TCACHE ();

  ar_ptr = arena_for_chunk (p); //获得指向arena的指针
  _int_free (ar_ptr, p, 0); //调用init函数进行释放
    
}
  • _int_free()函数,对chunk进行检查
static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
   
   
  INTERNAL_SIZE_T size;        /* its size */
  mfastbinptr *fb;             /* associated fastbin */
  mchunkptr nextchunk;         /* next contiguous chunk */
  INTERNAL_SIZE_T nextsize;    /* its size */
  int nextinuse;               /* true if nextchunk is used */
  INTERNAL_SIZE_T prevsize;    /* size of previous contiguous chunk */
  mchunkptr bck;               /* misc temp for linking */
  mchunkptr fwd;               /* misc temp for linking */

  size = chunksize (p);

  /* Little security check which won't hurt performance: the
     allocator never wrapps around at the end of the address space.
     Therefore we can exclude some size values which might appear
     here by accident or by "design" from some intruder.  */
    
    //第一个条件筛选掉一些特别大的size,第二个条件检查chunk是否对齐
  if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
      || __builtin_expect (misaligned_chunk (p), 
<think>我们正在讨论glibc中的`__lookup_name`函数异常退出问题。首先,需要明确`__lookup_name`是glibc内部用于名称解析的函数,它被`getaddrinfo`等函数调用。异常退出可能由多种原因引起,例如内存错误、资源耗尽或内部逻辑错误。 根据引用[1]的提示,我们注意到它提到一个宏(`strdupa`)不能在函数调用的参数列表中使用,这可能导致栈溢出等问题。虽然引用没有直接提到`__lookup_name`,但我们可以推测类似的问题(如栈溢出)也可能发生在`__lookup_name`的内部实现中。 ### 可能的原因及排查步骤 1. **栈溢出(Stack Overflow)** `__lookup_name`函数在解析过程中可能会使用递归或大尺寸的栈上变量,如果调用深度过大或栈空间不足,会导致栈溢出。 **排查方法**: - 使用`ulimit -s`检查并增加栈大小(例如`ulimit -s unlimited`) - 在调试时使用`-fstack-usage`编译选项检查栈使用情况 2. **内存分配失败** 函数内部可能调用`malloc`等动态内存分配函数,若内存不足会导致失败。 **排查方法**: - 检查系统内存状态(`free -m`) - 使用`valgrind`检测内存错误 3. **同步原语问题(死锁等)** 如果`__lookup_name`内部使用互斥锁等同步机制,死锁可能导致线程挂起或异常退出。 **排查方法**: - 使用`gdb`附加到进程,检查线程堆栈 - 检查是否在信号处理函数中调用了不可重入函数 4. **DNS服务器响应问题** 当DNS服务器返回异常响应(如格式错误、超长报文)时,解析函数可能处理异常。 **排查方法**: - 使用`tcpdump`抓取DNS查询包(`tcpdump -i any port 53`) - 检查`/etc/resolv.conf`配置的DNS服务器 5. **glibc版本缺陷** 已知版本的glibc在名称解析上存在漏洞,例如[CVE-2015-7547](https://nvd.nist.gov/vuln/detail/CVE-2015-7547)。 **排查方法**: - 运行`ldd --version`检查glibc版本 - 对照[glibc漏洞列表](https://sourceware.org/glibc/wiki/Security%20Exceptions)检查是否命中已知问题 ### 调试方法 #### 核心转储分析 1. 启用核心转储: ```bash ulimit -c unlimited echo "core.%e.%p" > /proc/sys/kernel/core_pattern ``` 2. 复现崩溃后使用`gdb`分析: ```bash gdb /path/to/program core.* ``` 3. 检查崩溃时的堆栈: ```gdb bt full ``` #### 源码级调试 1. 下载对应版本的glibc源码: ```bash apt source glibc-source # Debian/Ubuntu ``` 2. 编译带调试符号的glibc(参考[官方文档](https://sourceware.org/glibc/wiki/Testing/Builds)) 3. 使用`gdb`跟踪`__lookup_name`函数: ```gdb break __lookup_name run ``` ### 修复建议 1. **升级glibc** 若确认是已知漏洞,立即升级到修复版本: ```bash sudo apt update && sudo apt install libc6 ``` 2. **替换解析函数** 使用第三方DNS解析库(如`c-ares`)绕过glibc问题: ```c #include <ares.h> void callback(void* arg, int status, int timeouts, struct hostent* host) { /* 处理解析结果 */ } ares_gethostbyname(channel, "example.com", AF_INET, callback, NULL); ``` 3. **配置超时和重试** 通过修改`/etc/resolv.conf`减少超时时间: ```conf options timeout:1 attempts:2 ``` ### 总结流程图 ```mermaid graph TD A[发生异常退出] --> B{检查核心转储} B -->|有核心转储| C[gdb分析堆栈] B -->|无核心转储| D[检查系统日志] C --> E[定位崩溃函数] D --> F[/var/log/syslog] E --> G{是否在__lookup_name} G -->|是| H[检查栈大小/内存] G -->|否| I[检查其他模块] H --> J[调整栈大小/内存] J --> K[问题解决?] K -->|是| L[结束] K -->|否| M[升级glibc/更换解析库] ``` §§相关问题§§ 1. 如何获取glibc中未导出符号(如`__lookup_name`)的调试信息? 2. glibc的DNS解析过程涉及哪些关键函数? 3. 在多线程程序中如何安全地进行域名解析? 4. 如何编写自定义的DNS解析函数替代glibc实现? 5. 哪些工具可以跟踪glibc内部的函数调用?</think>### `__lookup_name`函数异常退出问题分析 `__lookup_name`是glibc内部用于名称解析的核心函数(位于`nss/digits_dots.c`),被`getaddrinfo`等函数调用。其异常退出通常与以下问题相关: #### 1. **栈溢出问题** 引用[1]指出类似`strdupa`的宏不能在函数参数中使用,`__lookup_name`也存在类似风险: ```c /* glibc源码片段 */ static enum nss_status __lookup_name (struct resolv_context *ctx, ...) { char *tmpbuf = alloca (1024); // 栈上分配大内存存在溢出风险 // ... } ``` **风险点**: - 使用`alloca()`在栈上分配大内存(如长域名) - 递归调用导致栈耗尽 **解决方案**: - 替换`alloca()`为堆分配`malloc()` - 设置栈大小限制:`ulimit -s 65536` #### 2. **DNS响应解析错误** 当处理畸形DNS响应时可能触发异常: ```c /* 解析响应代码片段 */ if (anslen < HFIXEDSZ) { // 未检查长度直接访问 hp->id = response->id; // 可能越界访问 } ``` **典型崩溃场景**: - DNS响应长度不足(`anslen < HFIXEDSZ`) - 压缩指针解析错误(RFC1035压缩格式) - TXT记录超长(>2048字节) #### 3. **线程安全问题** 在非线程安全场景下使用: ```c static struct hostent static_hostent; // 静态变量非线程安全 __lookup_name(...) { static_hostent.h_addr_list[0] = ...; // 多线程并发写冲突 } ``` **表现**:多线程程序随机崩溃,堆栈指向`__lookup_name` #### 4. **内存管理缺陷** ```c char *buffer = __libc_malloc(MAXPACKET); // ... 解析过程中未检查空指针 memcpy(buffer, response, len); // len可能大于MAXPACKET ``` **风险**:堆溢出导致`SIGSEGV` --- ### 诊断与修复步骤 #### 1. **复现与调试** ```bash # 使用GDB捕获异常 gdb -ex "set environment RES_OPTIONS debug" \ -ex "catch signal SIGSEGV" \ -ex "run" ./your_program ``` 检查崩溃时的: - `backtrace`(是否在`__lookup_name`内部) - `frame`寄存器(检查内存操作地址) - `p *response`(DNS响应结构) #### 2. **补丁验证** 检查是否命中已知漏洞: ```bash # 查看glibc版本 ldd --version | grep libc # 检查CVE补丁 grep -r CVE-2015-7547 /usr/share/doc/glibc-*/NEWS ``` 重点关注: - CVE-2015-7547(DNS栈溢出) - CVE-2016-3075(堆栈消耗) - CVE-2023-4911(本地提权) #### 3. **替代解决方案** 使用更安全的解析方式: ```c #include <ares.h> // 使用c-ares库替代 ares_gethostbyname(channel, "example.com", AF_INET, callback, NULL); ``` #### 4. **配置加固** `/etc/resolv.conf`添加防护: ```conf options edns0 # 启用EDNS options single-request # 防止多请求竞争 options timeout:2 # 减少阻塞时间 ``` --- ### 问题排查流程图 ```mermaid graph TD A[异常退出] --> B{信号类型} B -->|SIGSEGV| C[内存错误] B -->|SIGABRT| D[断言失败] C --> E[检查alloca/memcpy] D --> F[检查响应验证] E --> G[替换堆分配] F --> H[添加长度检查] G --> I[压力测试] H --> I I --> J{问题解决?} J -->|是| K[结束] J -->|否| L[升级glibc] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值