空指针访问的陷阱

简介: # 背景 之前用AddressSanitizer工具验证阿里云短视频SDK的稳定性时出现了一个崩溃问题,报错堆栈在一个空指针对象访问其成员函数处,但是从整体代码执行流程分析发现其对象指针一直是空的,诡异的是不使用工具运行时无论如何都不会崩溃。 ASAN工具报错发生在如下行,此处```mAudioRenderServicePtr```为NULL。 ```c this->OnStop(fa

背景

之前用AddressSanitizer工具验证阿里云短视频SDK的稳定性时出现了一个崩溃问题,报错堆栈在一个空指针对象访问其成员函数处,但是从整体代码执行流程分析发现其对象指针一直是空的,诡异的是不使用工具运行时无论如何都不会崩溃。

ASAN工具报错发生在如下行,此处mAudioRenderServicePtr为NULL。

this->OnStop(false, mAudioRenderServicePtr->GetAddr());

下面对正常运行和工具调试出现的结果一致进行分析,以及延伸的讨论一下关于空指针访问的一些陷阱。

分析

空指针即未指向任何对象,于是从语言层面来讲在访问空指针时应该一定发生崩溃,但在多函数调用及参数传递过程中,以及C++语言的封装特性、编译器是否优化的情况下,则不一定发生崩溃或者崩溃延迟。

先看看GetAddr这个成员函数的声明:

const MdfAddr& GetAddr(){ return mAddr; }

这里发现返回值为成员变量mAddr的常量引用,如下是反汇编对应的指令

    0xad0b9880 <+512>: ldr.w  r0, [r5, #0x124]
    0xad0b9884 <+516>: movs   r1, #0x0
    0xad0b9886 <+518>: add.w  r2, r0, #0x3c

从汇编指令看到,最后一行直接将this指针加上成员变量的偏移值0x3c并返回,也即返回的是mAddr的地址0x3c。

以及成员函数OnStop的函数声明

void OnStop(bool isAsync, const MdfAddr &srcAddr);

第二个参数实际使用的是MdfAddr的常量引用,因此参数会直接传递其对象地址0x3c,而不会访问0x3c指向的内存值,后面函数执行过程中也并未访问mAddr的成员变量,所以无工具运行时不会发生崩溃。

至于有工具时会发生崩溃,是因为ASAN会对所有地址访问进行监控,当出现异常时就会中断崩溃。

其它情况

  • 如上述的情况,如果我们在OnStop里实际访问了第二个参数里的成员变量,即实际访问了0x3c指针指向的内存,就会立即产生崩溃,比如OnStop的声明是这样

void OnStop(bool isAsync, MdfAddr srcAddr);

第二个参数会直接传递MdfAddr的值,因此会访问0x3c指向的实际内存,从而会立即导致崩溃,输出类似如下的信息

Build fingerprint: 'Xiaomi/cancro_wc_lte/cancro:6.0.1/MMB29M/8.9.13:user/release-keys'
02-21 16:43:27.589 290-290/? A/DEBUG: Revision: '0'
02-21 16:43:27.589 290-290/? A/DEBUG: ABI: 'arm'
02-21 16:43:27.589 290-290/? A/DEBUG: pid: 19854, tid: 19933, name: Thread-3246  >>> com.aliyun.aliyunvideosdkpro <<<
02-21 16:43:27.589 290-290/? A/DEBUG: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
02-21 16:43:27.610 290-290/? A/DEBUG:     r0 00000000  r1 00004ddd  r2 00000006  r3 99c11978
02-21 16:43:27.610 290-290/? A/DEBUG:     r4 99c11980  r5 99c11930  r6 00000019  r7 0000010c
02-21 16:43:27.610 290-290/? A/DEBUG:     r8 0000000b  r9 b3472bd0  sl 00000000  fp 133822cc
02-21 16:43:27.610 290-290/? A/DEBUG:     ip 00000006  sp a8260188  lr b6816c69  pc b6819058  cpsr 400d0010
02-21 16:43:27.646 290-290/? A/DEBUG: backtrace:
02-21 16:43:27.646 290-290/? A/DEBUG:     #00 pc 00042058  /system/lib/libc.so (tgkill+12)
02-21 16:43:27.646 290-290/? A/DEBUG:     #01 pc 0003fc65  /system/lib/libc.so (pthread_kill+32)
02-21 16:43:27.646 290-290/? A/DEBUG:     #02 pc 0001c403  /system/lib/libc.so (raise+10)
02-21 16:43:27.646 290-290/? A/DEBUG:     #03 pc 000195b5  /system/lib/libc.so (__libc_android_abort+34)
02-21 16:43:27.646 290-290/? A/DEBUG:     #04 pc 00017508  /system/lib/libc.so (abort+4)
02-21 16:43:27.647 290-290/? A/DEBUG:     #05 pc 000a18b3  /system/lib/libclang_rt.asan-arm-android.so (_ZN11__sanitizer5AbortEv+40)
02-21 16:43:27.647 290-290/? A/DEBUG:     #06 pc 000a6449  /system/lib/libclang_rt.asan-arm-android.so (_ZN11__sanitizer3DieEv+60)
02-21 16:43:27.647 290-290/? A/DEBUG:     #07 pc 0008ffc0  /system/lib/libclang_rt.asan-arm-android.so (_ZN6__asan19ScopedInErrorReportD2Ev+352)
02-21 16:43:27.647 290-290/? A/DEBUG:     #08 pc 000900b8  /system/lib/libclang_rt.asan-arm-android.so (_ZN6__asan18ReportDeadlySignalEiRKN11__sanitizer13SignalContextE+160)
02-21 16:43:27.647 290-290/? A/DEBUG:     #09 pc 0008f0fc  /system/lib/libclang_rt.asan-arm-android.so (_ZN6__asan18AsanOnDeadlySignalEiPvS0_+188)
02-21 16:43:27.647 290-290/? A/DEBUG:     #10 pc 0036d2e1  /system/lib/libart.so (_ZN3art12FaultManager11HandleFaultEiP7siginfoPv+208)
02-21 16:43:27.647 290-290/? A/DEBUG:     #11 pc 0001756c  /system/lib/libc.so
  • 考虑另一种场景,如何实现的代码
class MyClass
{
public:
    int calcValue(int x, int y) const
    {
        return x + y;
    }
};

如果我们将一些实际未使用类成员变量的全局函数作为类的成员函数,那么在对象指针为NULL时访问这一类函数,则不会发生崩溃,因为在执行calcValue时this指针并不会用到。

总结

  • 如果直接访问空指针指向的内存,如内建类型,则会立即发生崩溃
  • 访问空指针对象指向的成员函数时,则不一定会发生崩溃
  • 使用引用或者常量引用类型的参数时,内存访问会延迟,内存访问异常也会延迟
目录
相关文章
|
7月前
|
设计模式 运维 Java
开发中造成空指针的常见写法,如何预防!
开发中造成空指针的常见写法,如何预防!
40 0
|
2月前
|
C++
C++野指针 空指针 危险指针
C++野指针 空指针 危险指针
|
4月前
|
监控 Java Unix
日常知识点之内存泄露定位手段(c语言hook malloc相关方式)
日常知识点之内存泄露定位手段(c语言hook malloc相关方式)
40 0
|
6月前
|
存储 C语言
C语言之指针的含义,指针类型的定义及使用方法,野指针的定义,以及原因,如何避免野指针
C语言之指针的含义,指针类型的定义及使用方法,野指针的定义,以及原因,如何避免野指针
|
10月前
|
编译器 C语言 C++
C语言数组越界造成的死循环例子,当你得到了这个意想不到的结果的时候,你肯定不知道为什么,看你还敢不敢越界访问数组了
C语言数组越界造成的死循环例子,当你得到了这个意想不到的结果的时候,你肯定不知道为什么,看你还敢不敢越界访问数组了
|
11月前
|
安全 Windows
因为你安全了,所以你危险了——空指针引用
1.本文章属于系列文章《因为你安全了,所以你危险了》中的第一篇 2.本篇文章的作者是Gcow安全团队复眼小组的晏子霜,未经允许禁止转载 3.本篇文章需要你对GDI子系统有一定了解,最好阅读过部分关于Windows显示驱动,打印机驱动,以及调色板这一块的源码.并对DDI函数有一定了解,以及编写Windows Kernel Exploit的能力
|
11月前
|
编译器 C语言
《C陷阱与缺陷》之“语义”陷阱——数组越界导致的程序死循环问题
《C陷阱与缺陷》之“语义”陷阱——数组越界导致的程序死循环问题
88 0
经典的笔试题解析:内存泄漏问题忘记free与非法访问的问题
经典的笔试题解析:内存泄漏问题忘记free与非法访问的问题
71 0
经典的笔试题解析:内存泄漏问题忘记free与非法访问的问题
|
存储 Go 开发者
指针案例和使用陷阱|学习笔记
快速学习指针案例和使用陷阱。
73 0
指针案例和使用陷阱|学习笔记