fiz*_*gal 10 c++ clang virtual-inheritance llvm-clang
更新:我创造了更多M,但仍然是CVE再现崩溃.摘要:删除了类中所有Bool* bools_
字段的使用Base
(但仍然必须定义或不发生崩溃).还删除Base::Initialize()
了Rule
来自Base
及其后代的虚方法.附加了新的MCVE.
我已经设法为此代码创建了一个MCVE并在下面发布了它.
一些描述性细节:代码使用虚拟基类和派生类,并且实例化的某些派生类具有构造函数,这些构造函数调用从"基础"类继承的非虚方法(实际上是派生类,但在继承层次结构中高于我称之为"派生"类来初始化"基础"类数据.该方法调用在派生类中重写的虚方法.我意识到这是一件危险的事情,但是从我(可能是有限的)对C++的理解来看,它似乎应该有效,因为派生类构造函数的主体在设置"基"类虚拟表之前不会执行.在任何情况下,在调用"base"类的初始化方法期间不会发生段错误.
段错误发生在"base"类构造函数中,并且仅在构造函数的主体为空时发生.如果我向构造函数添加一个调试行,以便在到达该点时打印出来,则打印出调试行并且代码正常运行.我的猜测是,由于某种原因,编译器正在优化在"base"类的构造函数的主体执行之前应该发生的初始化,包括设置vtable.
正如主题所说,这个代码在没有使用Apple的g ++或g ++ 7.2.0进行优化编译时运行良好,并且在使用g ++ 7.2.0编译甚至-O3时运行正常.它只在编译时-O2
或-O3
使用Apple的LLVM实现g ++时出现段错误.该g++ --version
编译器的输出是:
% /usr/bin/g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin17.3.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Content
Run Code Online (Sandbox Code Playgroud)
MCVE如下.
#include <iostream>
using namespace std;
class OriginalBaseClass {
public:
OriginalBaseClass(long double data1 = 1, long int data2 = 1) : data1_(data1), data2_(data2) { cout << "In OriginalBaseClass constructor\n"; }
private:
long double data1_;
long int data2_;
};
class Base : public virtual OriginalBaseClass {
public:
Base(long int data1 = 0, long int data2 = 0) : data1_(data1), data2_(data2) { cout << "In Base constructor\n"; }
virtual ~Base();
private:
bool* bools_;
long int data1_;
long int data2_;
};
Base::~Base()
{
cout << "In Base destructor\n";
}
class Derived_A : public virtual Base {
public:
Derived_A() { cout << "In Derived_A constructor\n"; }
};
class Derived_B : public Derived_A {
public:
Derived_B() : OriginalBaseClass(), Base(4, 1), Derived_A() { cout << "In Derived_B constructor\n"; }
};
int main()
{
Derived_B Derb;
}
Run Code Online (Sandbox Code Playgroud)
链接到错误报告:https://bugreport.apple.com/web/
参考编号36382481
Sta*_*irl 14
这看起来像是由于无效生成未对齐的SSE存储而导致的Clang中的错误.以下是基于您的代码的最小示例:
struct alignas(16) Base1 { };
struct Base2 : virtual Base1 {
__attribute__((noinline)) Base2() : data1_(0), data2_(0) { }
long dummy_, data1_, data2_;
};
struct Base3 : virtual Base2 { };
int main() { Base3 obj; }
Run Code Online (Sandbox Code Playgroud)
这是由Clang生成的布局(GCC使用相同的布局):
*** Dumping AST Record Layout
0 | struct Base1 (empty)
| [sizeof=16, dsize=16, align=16,
| nvsize=16, nvalign=16]
*** Dumping AST Record Layout
0 | struct Base2
0 | (Base2 vtable pointer)
8 | long dummy_
16 | long data1_
24 | long data2_
0 | struct Base1 (virtual base) (empty)
| [sizeof=32, dsize=32, align=16,
| nvsize=32, nvalign=8]
*** Dumping AST Record Layout
0 | struct Base3
0 | (Base3 vtable pointer)
0 | struct Base1 (virtual base) (empty)
8 | struct Base2 (virtual base)
8 | (Base2 vtable pointer)
16 | long dummy_
24 | long data1_
32 | long data2_
| [sizeof=48, dsize=40, align=16,
| nvsize=8, nvalign=8]
Run Code Online (Sandbox Code Playgroud)
我们可以看到它Base3
与之合并Base1
,因此它们共享地址.Base2
通过实例化Base3
,并与随后放置8字节偏移,对齐Base2
在实例8个字节,即使alignof(Base2)
是16.这是仍然正确行为,因为这是在所有成员字段中的最大对齐Base2
.从虚拟基类继承的对齐Base1
不需要保留,Base1
由派生类实例化,Base3
负责Base1
正确对齐.
问题在于Clang生成的代码:
mov rbx,rdi ; rdi contains this pointer
...
xorps xmm0,xmm0
movaps XMMWORD PTR [rbx+0x10],xmm0
Run Code Online (Sandbox Code Playgroud)
Clang决定初始化两者data1_
并data2_
使用一条movaps
需要16字节对齐的指令,但Base2
实例只有8字节对齐,导致段错误.
看起来Clang认为它可以使用16字节对齐的存储,因为它alignof(Base2)
是16,但对于具有虚拟基础的类,这种假设是错误的.
如果需要临时解决方案,可以使用-mno-sse
标志禁用SSE指令的使用.请注意,这可能会对性能产生影响.
可以在此处找到Itanium ABI文档:https://refspecs.linuxfoundation.org/cxxabi-1.75.html
它明确提到nvalign:
nvalign(O):对象的非虚拟对齐,即没有虚拟基础的O的对齐.
然后是关于如何完成分配的解释:
除虚拟基础之外的成员分配
如果D不是空基类或D是数据成员:从offset dsize(C)开始,如果需要,则增加以对齐基类的nvalign(D)或对齐数据成员(D).将D置于此偏移处,除非这样做会导致相同类型的两个组件(直接或间接)具有相同的偏移量.如果发生这样的组件类型冲突,则通过nvalign(D)为基类增加候选偏移量,或者通过对齐(D)增加数据成员并再次尝试,重复直到成功发生(这将不会晚于sizeof(C)向上舍入到所需的对齐方式).
看起来Clang和GCC都尊重Itanium ABI,Base2
使用非虚拟对齐方式正确对齐.我们还可以在上面的记录布局转储中看到.
您可以使用-fsanitize=undefined
(GCC和Clang)编译程序,以便在运行时获得此误报警告消息:
main.cpp:29:5: runtime error: constructor call on misaligned address 0x7ffd3b895dd8 for type 'Base2', which requires 16 byte alignment
0x7ffd3b895dd8: note: pointer points here
e9 55 00 00 ea c6 2e 02 9b 7f 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 f8 97 95 34
Run Code Online (Sandbox Code Playgroud)
所以目前有三个漏洞.我已经报告了所有这些: