Understanding Undefined Behavior
- 什么是未定义的行为
- 编译器和未定义的行为之间的关系
- 未定义行为导致的安全问题
- 检查未定义行为的工具介绍
- Swift相比OC更安全一些
什么是程序的未定义行为?
- 在 ISO C++14 Standard未定义成为标准的行为,有些错误的行为没有多加检测和控制
- 即语法检查不到的错误,语法正确,运行起来却会有错误
- 比如除数未0,数组越界,数值类型溢出,空指针访问异常,更改字符串的字面量这些都是我们在项目中经常会碰到的.
- 详细见下图
编译器如何处理未定义行为
- 编译器可以帮助整段并给出错误或者警告信息
- 安装文档中的规则执行
- 产生不可预料的后果
未定义行为是一个利弊权衡的取舍过程
- 性能和安全性之间需要做一个取舍
一些未定义行为的例子
//值未初始化
int uninitialized_variable(int arg) { int value;
if (arg <= 0)
value = 42;
return arg + value;
}
//使用未初始化的值
int uninitialized_variable(int arg) { int value;
if (arg <= 0)
value = 42;
return arg + value;
}
//指针没有按照要求对齐,char和int混用
char *serialize_misaligned(char *buffer, int a, int b) { *(int *)buffer = a;
buffer += sizeof(a);
*(int *)buffer = b;
buffer += sizeof(b);
return buffer;
}
![-w415](media/16025922849569/16025945666340.jpg)
//访问已经释放的对象或变量,value指针得到的是局部变量`default_value`的地址,但是这个变量是一个局部变量,离开if语句就释放了
int lifetime_issue(int *value) {
if (value == NULL) {
int default_value = 42;
value = &default_value;
}
}
编译器和未定义行为之间的关系
关系见下图:
- Singed int不能溢出,通过 x<x+1判定
- 指针自然对齐,自动根据type类型偏移查找,使用向量指令检查
- 空指针不能取值,空指针没有地址
编译器是如何优化这些未定义行为的?
- 图解
- 读取源代码
- 生成中间代码
- 分析
- 优化
- 输出目标文件
- 优化无用代码
Objective-C int foo(int *P) {
int var = *P; //无用的 *P直接去掉
return 42;
} - 去除冗余的校验
在优化时可能会有不同的优化行为
消除NULL -> 去除无用代码
消除无用代码 -> ..
编译器优化分为多个等级
- 未定义的行为危害
- 不可预知的
- 后果可能会影响整个程序的执行结果
- bug会更加隐蔽
- 未定义的行为危害
- 图解
未定义行为的安全性问题
- 安全性问题和用户隐私息息相关,举些例子
- 未定义行为是很多安全漏洞的核心
- 缓冲区溢出
- 使用未定义的变量
- 使用释放的变量
- 重复释放
- 多线程冲突
处理未定义行为
- 工具检测
- 编译检测
- Static Analyzer: 静态分析
- Address Sanitizer: 地址安全性
- Thread Sanitizer: 多线程问题检测
- Undefined Behavior Sanitizer: 未定义行为检测
- 充分利用编译器规则并相信它
- 注意编译器警告
- 使用release版本的Xcode
- 为工程定义化更多的验证设置信息(Editor → Validate Settings)
运行静态检测工具(Run the Static Analyzer)
- 扫描编写的代码
- 分析每次构建
- 持续集成分析
分析Runtime检测信息分析
值运算溢出
开启检查
使用更安全的语言功能
- 自动引用计数
- C++智能指针
(std::shared_ptr, std::unique_ptr)
- 数组边界检查
推荐使用Swift
- 强大的类型推导
- 可选链有效的避免空指针访问异常
- 自动引用计数
Swift是目前iOS开发中最安全的语言
- 对比C语言
- Optionals: 通过可选链
?
对空指针取消引用的应答,避免Crash,注意不要强制解包- (nullable NSView *)ancestorSharedWithView:(nonnull NSView *)aView; // Objective-C func ancestorShared(with view: NSView) -> NSView? // Swift
- Optionals: 通过可选链
Swift中的UnsafeTypes
- 指针类型的操作在Swift是不安全的类型,提供了面向对象的指针访问。
Swift UnsafePointer<Pointee> 不可变指针
UnsafeMutablePointer<Pointee> 可变指针
UnsafeRawBufferPointer<Pointee> 不可变数组指针
UnsafeMutableRawBufferPointer<Pointee> 变的数组指针