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

调用mprotect将栈上对象设为只读时进程崩溃,求助原因

问题

我希望将栈上对象的访问属性设置为只读,但调用mprotect后进程立即崩溃。大致代码如下:

constexpr int kPageSize = 4096;
CHECK_EQ(kPageSize, sysconf(_SC_PAGE_SIZE));
volatile char padding1[kPageSize] = {0};
NetDataHead head;
volatile char padding2[kPageSize] = {0};

// ...
auto align_down_4k_ptr = steel::AlignDownPointer(&head, kPageSize);
CHECK_EQ(reinterpret_cast<std::uint64_t>(align_down_4k_ptr) % kPageSize, 0);
if (auto rc = mprotect(align_down_4k_ptr, kPageSize, PROT_READ); rc != 0) {  // A
  LOG_F("Failed to mprotect ptr: %p, rc: %d", align_down_4k_ptr, rc);
}

// Read-only codes ...

if (auto rc = mprotect(align_down_4k_ptr, kPageSize, PROT_READ | PROT_WRITE); rc != 0) {
  LOG_F("Failed to mprotect ptr: %p, rc: %d", align_down_4k_ptr, rc);
}

程序直接在第一个mprotect调用的A点崩溃。查阅man手册后发现,Linux下这不应导致崩溃,请问可能的原因是什么?我想要用mprotect保护栈上的head结构体。

分析与解决
  • 栈页默认包含执行权限,修改后破坏函数返回流程
    多数Linux系统中,栈页默认权限是PROT_READ | PROT_WRITE | PROT_EXEC。当你将目标页改为仅PROT_READ时,会移除执行权限。而当前函数的返回地址就存在这个栈页里,mprotect执行完成后需要从栈中读取返回地址并跳转,此时栈页无执行权限,直接触发SIGSEGV崩溃。

  • 目标页包含栈帧关键控制数据
    head所在的栈页同时存放了当前函数的栈帧信息,比如基址指针(rbp/ebp)、返回地址。将页设为只读后,后续函数执行时(比如mprotect返回时)需要修改这些栈帧数据,会触发写权限错误,导致崩溃。

  • padding无法保证head独占一页
    栈是从高地址向低地址增长的,padding1的地址比head高,padding2比head低。AlignDownPointer(&head, kPageSize)得到的页会包含padding1的部分数据和head,而这个页属于当前函数的栈帧核心区域,修改权限必然干扰栈的正常运作。

可行方案

  • 改用堆内存分配:用mmap申请一个页大小的内存,初始化NetDataHead后设置为只读,操作完成后恢复权限再释放。这种方式能精准控制内存页权限,不会影响栈的正常运行。
  • 避免直接修改栈页权限:如果必须用栈,需要将head放在完全独立于当前栈帧控制数据的页中,但这种方式严重依赖栈布局,稳定性极差,不推荐使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 19:40:00