
缓冲区溢出与下溢图解指南Coursebook 中 4 个经典内存错误剖析与预防方法【免费下载链接】coursebookOpen Source Introductory Systems Programming Textbook for the University of Illinois项目地址: https://gitcode.com/GitHub_Trending/co/coursebook缓冲区溢出与下溢Buffer Overflow / Underflow是系统编程中最经典的内存错误也是 C 语言新手最容易踩中的坑。本文基于Coursebook—— 伊利诺伊大学开源的入门级系统编程教材用书中 4 个真实场景和随书插图带你 10 分钟看懂缓冲区溢出与下溢是如何发生的、为什么能从小 Bug升级为安全事故以及 4 条可直接上手的预防检查清单零基础也能看懂。 缓冲区溢出与下溢是什么一张内存图讲清楚在 C 语言里缓冲区Buffer就是一段连续的字节内存数组是缓冲区字符串是缓冲区malloc出来的堆空间也是缓冲区。关键事实只有一条C 语言不会检查你的指针是否越界。写多一个字节不会报错、不会警告而是悄悄覆盖掉邻居的内存——这就是缓冲区溢出写过头而缓冲区下溢则是从缓冲区的前面、甚至更低地址处开始读写同样会踩坏别人。下面这张图来自 Coursebook 的内存模型章节它把一块缓冲区拆成一个个字节格子写字符串person后前 4 格存长度0,0,0,6后面 7 格依次是p e r s o n \0——你能直观看到字节就是字节C 编译器只负责预留空间不帮你画边界线。正是这种无边界保护的设计让缓冲区溢出与下溢成为 C 程序最危险的内存错误类别教材专门在 introc/common_bugs.tex 一节列出经典案例。 Coursebook 书中的 4 个经典内存错误场景场景 1循环多写一格 —— 最经典的缓冲区溢出教材给出的最小案例循环从i N写到i 0当i N时写入array[N]——越界一格#define N (10) int array[N]; for (int i N; i 0; i--) array[i] i; /* 写到了 array[10]越界 */这一格内存大概率正被别的东西使用于是内存损坏悄然发生。更隐蔽的是溢出可能发生在库函数内部你的代码本身看起来完全正确。场景 2字符串少分配一个字节 —— strlen 下溢陷阱C 字符串必须在末尾有一个 NUL 字节\0。存Hi需要 3 个字节而不是 2 个。教材用一个strdup示例连摆三种写法malloc(sizeof(char*))错只分配了指针大小、malloc(strlen(input))差一点漏了 NUL、正确写法是malloc(strlen(input) 1)。记住口诀字符串 strlen(s) 1 个字节。差这一个字节strcpy会一直越过你分配的边界去找 NUL这就是典型的缓冲区下溢式越界。场景 3踩坏邻居变量 —— aoo 惊吓实验安全章节 security/security.tex 有一个著名的小实验栈上并排放着两个缓冲区out[10]和in[10]程序从输入读字符串到in再打印out。输入短字符串时一切正常一旦输入超过 10 个字符溢出的字节顺着内存覆盖到out上打印结果变成了诡异的aoo。如果并排的不是无关变量而是密码哈希呢教材紧接着给出反例pass_hash紧邻一个不检查长度的输入缓冲区——一次缓冲区溢出就能污染敏感数据。场景 4不限长度的输入 —— gets() 这颗地雷教材把gets(array)称为老朋友它根本不接收缓冲区大小参数输入多长就写多长。希望输入比我的数组短是编程界最昂贵的希望。现代开发中应使用带长度限制的fgets(array, sizeof(array), stdin)。️ 为什么缓冲区溢出会成为安全事故缓冲区溢出与下溢不只是程序崩溃。教材的事后复盘章节记录了史上最著名的案例HeartbleedSSL 心跳包允许请求方谎报长度——只发 3 个字节却要求服务器回传 500 字节于是服务器把缓冲区后面不属于你的内存也读了出来密码、密钥随之泄露。根因就一句话没有对缓冲区做边界检查。更狠的情况是溢出发生在栈上。每个线程都有自己的栈区栈帧彼此紧邻返回地址、栈保护值就放在缓冲区旁边现代操作系统已布下多道防线见 security/security.tex栈保护Stack Canary编译器在栈上埋一个哨兵值函数返回前校验它是否被改一旦栈被捣毁立即中止程序——这就是为什么教材里的溢出实验要加-fno-stack-protector才能复现ASLR每次运行随机化栈、堆、库的地址让攻击者猜不到目标位置DEP数据执行保护内存页要么可写、要么可执行不能两者兼得阻止把恶意代码写进缓冲区再执行。✅ 4 步预防检查写代码前过一遍算准字节数分配内存时想清楚我要存多少个字节包括结尾的 NUL。堆内存是一块块连续的块少分配哪怕一个字节写入时就会撕裂到相邻块里——这块空闲块切分图出自 malloc/malloc.tex提醒我们堆上每一块都有明确的尺寸边界。每次访问都查边界任何索引都要先确认0 i N。教材同步章节的环形缓冲区就是一个优秀示范读写指针在 16 个槽位内顺时针移动、取模回绕buffer[in]、buffer[out]永远不会写出圈外——拒绝无长度限制的输入函数gets一律换成fgets、scanf用%10s这类带宽度的格式串。编译时开保护别随手关默认启用栈保护、ASLR调试复现时可以临时关闭但生产环境永远不要关。 延伸阅读Coursebook 相关章节内容文件路径缓冲区溢出/下溢、字符串少一字节等常见 Bugintroc/common_bugs.tex字节级内存模型与 strcpy 图解introc/c_memory_model.tex缓冲区溢出实验、栈保护、ASLR 与 DEPsecurity/security.texHeartbleed 等真实事故复盘post_mortems/post_mortems.tex堆内存分配与块管理malloc/malloc.tex线程栈布局threads/threads.tex一句话总结C 语言给了你自由也收回了护栏——只要把算准字节数、检查边界、限制输入这三件事变成肌肉记忆缓冲区溢出与下溢这两个经典内存错误就再也难不住你。【免费下载链接】coursebookOpen Source Introductory Systems Programming Textbook for the University of Illinois项目地址: https://gitcode.com/GitHub_Trending/co/coursebook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考