深入理解Linux内存保护:mprotect函数源码解析

📅 2026/6/25 23:01:07
深入理解Linux内存保护:mprotect函数源码解析
前言在Linux系统编程中内存管理是一个核心话题。今天我们来深入分析musl libc中mprotect函数的实现看看它是如何工作的。源码分析#include sys/mman.h #include libc.h #include syscall.h int __mprotect(void *addr, size_t len, int prot) { size_t start, end; start (size_t)addr -PAGE_SIZE; end (size_t)((char *)addr len PAGE_SIZE-1) -PAGE_SIZE; return syscall(SYS_mprotect, start, end-start, prot); } weak_alias(__mprotect, mprotect);核心要点解析1️⃣ 为什么需要页对齐Linux内核的内存保护是‌以页Page为单位‌的通常是4KB4096字节。start (size_t)addr -PAGE_SIZE;这行代码的作用是‌向下取整到页边界‌。-PAGE_SIZE在补码表示下是0xFFFFF000假设PAGE_SIZE4096 -PAGE_SIZE相当于保留高位低位清零实现页对齐‌举例‌addr 0x1005PAGE_SIZE 0x1000start 0x1005 0xFFFFF000 ‌0x1000‌ ✅2️⃣ 结束地址的向上取整end (size_t)((char *)addr len PAGE_SIZE-1) -PAGE_SIZE;这里需要‌向上取整到页边界‌技巧是先加上PAGE_SIZE-1再对齐。‌举例‌addr 0x1005len 100PAGE_SIZE 0x1000addr len 0x1069加上 PAGE_SIZE-1 0x1069 0xFFF 0x2068对齐后 end 0x2068 0xFFFFF000 ‌0x2000‌ ✅这样就覆盖了 [0x1000, 0x2000) 整个页范围。3️⃣ 系统调用封装return syscall(SYS_mprotect, start, end-start, prot);调用真正的系统调用参数为‌start‌: 起始地址已对齐‌end-start‌: 实际保护的内存大小‌prot‌: 保护标志PROT_READ/WRITE/EXEC等4️⃣ weak_alias 的妙用weak_alias(__mprotect, mprotect);这创建了一个‌弱符号别名‌允许用户自定义mprotect函数来覆盖默认实现常用于调试 Hook安全沙箱内存检测工具实际应用场景表格场景用法示例️ 数据保护禁止写入代码段mprotect(code, len, PROT_READPROT_EXEC) 安全加固栈不可执行启动时设置栈为 PROT_READPROT_WRITE 调试标记内存为只读检测溢出mprotect(buf, size, PROT_READ)完整示例#include sys/mman.h #include stdio.h #include string.h int main() { char buf; // 先可写 strcpy(buf, Hello); printf(%s\n, buf); // 改为只读会保护整个页 if (mprotect(buf, 4096, PROT_READ) 0) { // buf X; // ❌ 段错误 printf(内存已保护\n); } return 0; }总结表格知识点说明页对齐内核以页为单位管理权限位运算技巧 -PAGE_SIZE快速对齐向上取整PAGE_SIZE-1再对齐weak_alias允许函数覆盖灵活性强这个看似简单的函数体现了‌系统编程的精髓‌理解硬件约束用最少的代码高效解决问题‌参考资料‌musl libc源码Linux man page:man 2 mprotect‌觉得有用就点个赞吧 欢迎评论区讨论‌#Linux #系统编程 #mprotect #musl #内存管理