vector::at()越界抛std::out_of_range异常,operator[]越界导致未定义行为;越界读可能泄露数据,越界写会破坏内存;未触发SIGSEGV不等于安全,因越界可能落在可访问内存页内。

vector::at() 和 operator[] 的越界行为差异
C++ 标准库 std::vector 对越界访问不做强制检查,但提供两种不同策略:operator[] 完全不检查,at() 会抛出 std::out_of_range 异常。
这导致同一段越界代码在不同写法下表现截然不同:
-
v[i](i ≥v.size())→ 未定义行为(UB),可能读到垃圾值、崩溃,也可能“恰好”没出事 -
v.at(i)(i ≥v.size())→ 立即抛异常,便于定位问题
实际开发中,operator[] 常用于性能敏感路径(如内层循环),但一旦索引逻辑有误,错误会隐藏得很深;而 at() 更适合调试期或用户输入驱动的索引场景。
越界读 vs 越界写:危害等级完全不同
越界访问不是“都一样危险”,读和写的后果差异极大:
- 越界读(如
v[i]取值,i 超出范围):可能泄露栈上相邻变量、返回随机整数、触发段错误,但一般不破坏程序控制流 - 越界写(如
v[i] = x,i 超出范围):直接覆写内存,可能覆盖:- 同一栈帧的其他局部变量(改掉标志位、指针地址)
- 函数返回地址(常见于原始数组,vector 内部缓冲区若在栈上分配则同理)
-
vector自身的元数据(如size或capacity字段,若内存布局紧凑且编译器未 padding)
尤其注意:即使 vector 数据在堆上分配,其内部指针(_M_start 等)仍在栈/对象内,越界写可能先破坏这些指针,再引发二次崩溃。
为什么 vector 越界不总触发 SIGSEGV?
SIGSEGV 不是“越界就来”,而是“访问了操作系统标记为不可读/不可写的页”。关键点在于:
立即学习“C++免费学习笔记(深入)”;
-
vector的底层内存通常由new分配,连续一页或多页堆内存,相邻地址大概率可读可写 - 越界偏移较小时(如 +1~+10 个元素),大概率落在同一内存页内 → 不触发信号,只造成静默数据污染
- 只有当越界落到:
- 堆边界外的保留区(如 glibc 的
top chunk后空隙) - 其他已映射页的保护区域(如
mprotect设为PROT_NONE) - 栈溢出撞到栈 guard page(vector 在栈上时更易触发)
- 堆边界外的保留区(如 glibc 的
换句话说:没崩溃 ≠ 没问题,反而更危险——你无法靠 crash 发现它。
缓冲区溢出攻击如何利用 vector 越界?
真实攻击不依赖 vector,但原理相通:攻击者通过可控输入诱导越界写,篡改关键内存。例如:
- 若某服务将用户 ID 存入
vector,又用未经校验的索引执行ids ids[user_input] = new_val - 攻击者传入极大
user_input(如0x7fffffffffff),使指针计算后指向返回地址附近 - 若该
vector对象与函数返回地址在栈上紧邻(取决于编译器布局、优化等级),一次越界写就可劫持控制流
现代防护(ASLR、stack canary、W^X)大幅增加难度,但 vector 越界仍是内存破坏类漏洞的常见入口点——尤其在禁用异常、关闭 sanitizer 的生产构建中,它几乎不留痕迹。








