C++ 在没有ASLR的情况下搜寻一只(内存、GC相关)消失的海森堡

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编译器;熔体方言被用熔

操作系统: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++),可以用

检索。
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
;当然,当运行uder
gdb
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”)
;但这看起来太过分了)

值得一提的是,崩溃消息由以下代码给出(在my
melt 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;
}