C++ 在没有ASLR的情况下搜寻一只(内存、GC相关)消失的海森堡
操作系统:Linux/Debian/Sid/x86_64(和Linux/Debian/Testing/x86_64);我用于编译的系统GCC是6.1.1(和带有Debian/Testing的5.3)。Gnu-libc为2.22;Linux内核是4.5;GDB是system 7.10或我自己的,由FSF源代码7.11构建 我在C++(GCC)的实验分支(Survivand and Read < <强> >中搜索内存和相关的<强> >(GLUE是一个类似于LISP的领域特定语言来定制GCC编译器;熔体方言被用熔体本身翻译成C++),可以用检索。C++ 在没有ASLR的情况下搜寻一只(内存、GC相关)消失的海森堡,c++,linux,debugging,C++,Linux,Debugging,操作系统:Linux/Debian/Sid/x86_64(和Linux/Debian/Testing/x86_64);我用于编译的系统GCC是6.1.1(和带有Debian/Testing的5.3)。Gnu-libc为2.22;Linux内核是4.5;GDB是system 7.10或我自己的,由FSF源代码7.11构建 我在C++(GCC)的实验分支(Survivand and Read < >中搜索内存和相关的 >(GLUE是一个类似于LISP的领域特定语言来定制GCC编译器;熔体方言被用熔
svn co -r236207 svn://gcc.gnu.org/svn/gcc/branches/melt-branch gcc-melt
然后(对于每个GCC变体或分支)在外部树中构建它,例如
mkdir _ObjMelt
cd _ObjMelt
../gcc-melt/configure --disable-bootstrap --enable-checks=gc \
--enable-plugins --disable-multilib --enable-languages=c,c++,lto
(如果愿意,您可以将其他选项传递给。/gcc melt/configure
,例如cxflags='-g3-O0-DMELT\u HAVE\u RUNTIME\u DEBUG=1'
;您可以删除--enable checks=gc
选项)
当然还有make
(或者make-j4
);该构建可能需要半个多小时(并且可能会因ASLR而失败,见下文)
MELT有一个分代的复制垃圾收集器(我怀疑这个bug是其中的一个特例),并且使用了很多(特别是,复制GC的大部分扫描和转发代码都是由MELT生成的)
(valgrind
在这里帮不上忙:我们正在实现一个复制GC,而GCC本身就是——即使没有熔化——内存泄漏)
熔化是自举的。通常的生成过程是从源代码中再生两次发出的C++代码。通常的方法是发出一些C++代码,叉一些<代码>使< /代码>获得共享对象,以及<代码> DLOpen< /Cord>共享对象,然后再次。
如果没有,构建总是成功的(并且它正在运行一个重要的测试:MELT的引导,以及通过MELT扩展的编译分析MELT运行时)。我甚至可以用makeupgrade-warmelt
重新生成运行时代码
但是在启用ASLR的情况下,构建失败,崩溃总是以相同的方式(请注意,cc1plus
是熔化的一个):
我正在禁用ASLR,例如使用exec setarch$(uname-m)-R/bin/bash
;当然,当运行udergdb
ASLR时,默认情况下是禁用的(除非我将禁用随机化0设置为gdb命令)
我的同事Franck Védrine建议我使用gdb的设施;原则上,它应该像在我的GC中设置断点一样简单(在melt\u fatal\u error
和melt\u fatal\u info
宏调用的melt\u fatal\u error
中,达到GC\35; 11
状态,做记录
,以便后续反向执行,运行故障案例(使用set disable randomization 0
禁用ASLR)直到“崩溃”,然后reverse cont
直到GC中的断点,并明智地使用watch
。不幸的是,这触发了一个广为人知的GDB bug(,…)-最近的GDB快照(如GDB-7.11.50.20160514
)没有纠正-
(我现在试图避免GDB bug,可能是因为我有自己的memset
&memcpy
例程,前面有#pragma GCC optimize(“-Og”)
;但这看起来太过分了)
值得一提的是,崩溃消息由以下代码给出(在mymelt runtime.h的第900行附近):
我的猜测是,这个bug可能是围绕“discriminant”(每个MELT值中都有一种“type”或“class”或“metadata”字段)的转发的一个困难的GC bug,在这种罕见的情况下,当这个discriminant还处于年轻一代时……添加一些代码来避免它确实会使bug在以后发生,但我一点也不确定
欢迎提供与实际虚拟地址相关的调试海森堡的任何线索或建议(因此对ASLR来说是合理的!)。
我甚至添加了一些初始化代码,可以选择mmap
或sbrk
几个无用的兆字节,希望“重现”由mmap
给出的随机地址(由MELT及其GC使用的calloc
调用)。这还没有帮助!我在Smalltalk垃圾收集器中使用的方法是在每次GC之前复制堆,并在副本中执行GC,然后在副本崩溃时重复调试。如果系统(如我的系统)是用高级oo语言开发的,那么这样做相对简单;复制堆只是复制组成VM模拟的对象图(在模拟中,堆位于单个大字节数组中)
在您的环境中应用此技术可能会具有相当大的挑战性,但也不是不可能的。让我在这里简述一下
我将调用您尝试调试的进程“master”和那些被克隆的进程来尝试GC for it子进程
在主节点中的GC之前,执行fork并让子节点执行GC,在子节点中运行泄漏检查程序,并以退出状态退出,以反映GC是否成功。如果子节点成功,则主节点继续执行其自己的GC。否则,主节点将循环,生成重复失败GC的子节点。然后调试子节点
子级需要在两种状态下启动。每个GC中的初始启动只运行GC并以成功状态退出。我们现在知道将失败的后续分叉可以进入等待状态,以便您可以将gdb连接到子级
我称之为“旅鼠调试”,因为在调试崩溃之前,可以根据需要让任意多的克隆人跳过悬崖。如果你能成功,请告诉我。我不能说巴兹尔是否能很好地使用这项技术,但我可以说这是一项聪明的技术。
cc1plus: note: MELT got fatal failure from ../../gcc-melt/gcc/melt-runtime.h:900
cc1plus: fatal error: corrupted memory heap with null magic discriminant
in 0x2bab6a8; GC#11
compilation terminated.
MELT BUILD SCRIPT FAILURE:
melt-build-script.tpl:382/307-melt-build-script.tpl:459/382 failed
with arguments @meltbuild-stage2/warmelt-normatch.args
static inline int
melt_magic_discr (melt_ptr_t p)
{
if (!p)
return 0;
#if MELT_HAVE_DEBUG > 0 || MELT_HAVE_RUNTIME_DEBUG > 0
if (MELT_UNLIKELY(!p->u_discr))
{
/* This should never happen, we are asking the discriminant of a
not yet filled, since cleared, memory zone. */
melt_fatal_error
("corrupted memory heap with null discriminant in %p; GC#%ld",
(void*) p, melt_nb_garbcoll);
}
#endif /*MELT_HAVE_DEBUG or MELT_HAVE_RUNTIME_DEBUG */
gcc_assert (p->u_discr != NULL);
return p->u_discr->meltobj_magic;
}