You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下C语言free函数如何检测内存越界?代码现象解析

Linux下malloc/free的越界访问行为与free实现解析

问题现象

在Ubuntu 22.04 x86-64环境下运行两段C代码,出现不同的越界访问结果:

第一段(写越界)

#include <malloc.h>
#include <unistd.h>

int main() {
  char* s = malloc(8 * 1024 * sizeof(char));
  for (int i = 0; i < 10 * 1024; ++i) {  // 越界访问
    s[i] = i; // 写入s
  }
  usleep(1);  // 避免编译器优化掉s
  free(s);    // 触发崩溃
}

程序并未在写越界赋值时崩溃,反而在调用free(s)时触发错误:double free or corruption (!prev)。

第二段(读越界)

#include <malloc.h>
#include <stdio.h>
#include <unistd.h>

int main() {
  char* s = malloc(8 * 1024 * sizeof(char));
  for (int i = 0; i < 10 * 1024; ++i) {
    printf("%d\n", (int)s[i]); // 读取s
  }
  usleep(1);
  free(s);    // 无错误
}

改为读越界操作后,调用free(s)未出现任何错误。

对比Windows平台:写越界操作会直接触发页错误崩溃。

Linux下free函数的实现与内部操作

Ubuntu 22.04使用的是glibc的ptmalloc2内存分配器,free的核心逻辑如下:

  • 空指针快速返回:如果传入的指针是NULL,直接终止操作。
  • 定位元数据:ptmalloc2给用户返回的内存指针,实际是内存块的起始地址向后偏移了一段——这段偏移区域存储着该内存块的元数据,包括块大小、分配状态、前后块链表指针等。free会通过用户指针反向定位到对应的元数据块。
  • 完整性校验:检查元数据中的魔术值、块大小对齐标记是否合法,同时验证相邻内存块的状态,确保内存未被非法篡改。
  • 合并空闲块:如果当前释放块的前/后相邻块处于空闲状态,会将它们合并成一个大的空闲块,减少内存碎片。
  • 内存归还与复用:若合并后的空闲块足够大(超过阈值,如128KB),则通过munmap系统调用将内存归还给操作系统;否则将空闲块加入对应大小分类的空闲链表,供后续malloc调用复用。

现象原因解释

写越界触发free崩溃的原因

你申请了8KB内存,但写入了10KB数据,越界的2KB直接覆盖了当前内存块的后续元数据(或相邻内存块的元数据)。当free执行时,读取到被篡改的元数据,触发了ptmalloc2的内存完整性保护机制,因此抛出错误。

写越界时未立即崩溃,是因为越界区域仍属于进程的地址空间——ptmalloc2会提前向操作系统申请更大的内存页,在用户空间自行管理,未触及进程地址空间边界,因此不会触发页错误。

读越界无错误的原因

读越界仅读取了不属于当前块的内存数据,未篡改元信息。free执行时校验的元数据保持完整,因此不会触发错误。但这种行为属于未定义行为,可能在其他场景下导致不可预测的问题。

与Windows的差异

Windows默认的内存分配器(如CRT的malloc)会在内存块前后设置保护页或更严格的边界检查,写越界触及保护页时会立即触发页错误崩溃;而glibc的ptmalloc2更侧重内存复用效率,边界检查主要在free阶段执行,因此不会立即崩溃。

内容的提问来源于stack exchange,提问作者Timothy Liu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 11:35:22