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

Swift中的UnsafeTypes

  • 指针类型的操作在Swift是不安全的类型,提供了面向对象的指针访问。 Swift UnsafePointer<Pointee> 不可变指针
    UnsafeMutablePointer<Pointee> 可变指针
    UnsafeRawBufferPointer<Pointee> 不可变数组指针
    UnsafeMutableRawBufferPointer<Pointee> 变的数组指针

参考文档

https://developer.apple.com/wwdc17/407

原文地址:https://www.cnblogs.com/wwoo/p/understanding-undefined-behavior.html