地址清理程序在静态转换无效指针时报告错误

Posted

技术标签:

【中文标题】地址清理程序在静态转换无效指针时报告错误【英文标题】:address sanitizer reports error when statically casting an invalid pointer 【发布时间】:2018-02-05 13:12:34 【问题描述】:

当将未分配内存中的 Derived* 静态转换为 Base* 时,gcc 的 ASAN 报告:

ASAN:DEADLYSIGNAL
=================================================================
==12829==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x563da3783776 bp 0x7ffde1670e50 sp 0x7ffde166d800 T0)
==12829==The signal is caused by a READ memory access.
==12829==Hint: address points to the zero page.

对于我使用此设置的测试:

struct Base2  int dummy; ;
struct Base  int dummy2; ;
struct Derived : public Base2, public virtual Base  ;

Derived* derived = (Derived*)0x1122334455667788; /* some pointer into non-allocated memory */
Base* base = static_cast<Base*>(derived); /* ASAN fails here */

为什么 ASAN 在此处报告无效的读取访问?指针偏移量和正确的结果指针值不应该在编译时就知道吗?

那么为什么还要进行这种读取访问呢?

【问题讨论】:

首先,0x1122334455667788 不是您认为的那样......它很可能被截断为 0x55667788,因为它被视为从 int 转换而来的。其次,不需要virtual,因为您不会多次继承Base。最后,您的错误是关于您在 snipit 中没有执行的空指针访问 - 所以我认为没有人可以给您一个有意义的回复 @UKMonkey “你不会在你的 snipit 中这样做” - 他会这样做,强制转换为虚拟继承的类需要 vtable 访问。 @yugr 没有虚函数,也就没有 vtable;这给了空 ptr 访问....所以事实上虚拟继承是问题的原因。有趣的。 (我尽量避免死亡钻石的情况;所以我对虚拟继承的影响的理解有点模糊)听起来你有一个很好的答案:) @UKMonkey 我认为如果有虚拟基类(即使没有虚拟函数)你仍然需要 vtable,因为编译器直到运行时才知道它们的偏移量。有关 GCC 4.8 生成的代码,请参阅下面的答案。 【参考方案1】:

不应该指针偏移,因此正确的结果指针 值在编译时就知道了吗?

不,虚拟继承类的偏移量在编译时是未知的,因此编译器在运行时通过访问 vtable 来计算它。

这是一个简单的例子:

Base *foo(Derived *p) 
  return static_cast<Base*>(p);

编译成

movq    (%rdi), %rax     # Get vptr
addq    -24(%rax), %rdi  # Load offset of Base from vtable
movq    %rdi, %rax       # Return result
ret

ASan 抱怨,因为您尝试访问一些导致段错误的随机内存地址。

【讨论】:

以上是关于地址清理程序在静态转换无效指针时报告错误的主要内容,如果未能解决你的问题,请参考以下文章

使用地址清理程序和附加 asan 标志进行静态初始化顺序时出现 g++ 5 和 6 的错误

MSVC 中的地址消毒器:为啥它在启动时报告错误?

unordered_map 在使用 [] 运算符时报告“向量下标超出范围”断言

static_cast静态类型转换指针导致Release程序随机崩溃报错

150809112 杨磊

将Apache的.htaccess转换到nginx中