如何摆脱C++中的非确定性,尝试了ADDR_NO_RANDOMIZE,其他东西

anauzrmj  于 11个月前  发布在  其他
关注(0)|答案(4)|浏览(74)

我有一个不确定的C++程序。我用一个输入文件运行它,它运行得很好。我用同一个输入文件运行第二次,它崩溃了。我想摆脱不确定性,以便使崩溃重现。
我加入了一些print语句来打印某些数据结构的地址,每次执行时,数据结构都在不同的地址上(在Linux下)。
malloc会返回不可预测地址的一个明显原因是ASLR。我把它关掉了。我可以验证它是关的--共享库总是在同一个地址加载,堆栈总是在同一个地址,等等。但是即使关闭了ASLR,malloc仍然是不可复制的--它在不同的连续运行中返回不同的地址。
我在绞尽脑汁寻找非决定论的可能来源:

  • 我已经用“strace”运行了这个程序。我可以“diff "两个连续执行的strace,除了打印我的数据结构的地址(它们在不同的地址)的print语句之外,没有任何diff。
  • 据我所知,它没有使用线程,除非glibc或C++在后台使用线程。我注意到ptmalloc使用了__thread变量.这可能是相关的吗?
  • 除了默认的之外,没有其他的信号处理程序。我没有故意使用信号。
  • 从理论上讲,glibc中的某些东西可能会获取一个CPU性能计数器,并将其用作非确定性的来源,但我对正在发生的事情持怀疑态度。

有人知道malloc在连续执行时返回不同地址的原因吗?
最新消息:
以下是我发现的最小的程序,即使关闭了ASLR,它也表现出非确定性:

int main(int argc, char **argv) {
    // Turn off ASLR address space layout randomization.
    const int old_personality = personality(ADDR_NO_RANDOMIZE);
    if (!(old_personality & ADDR_NO_RANDOMIZE)) {
       const int new_personality = personality(ADDR_NO_RANDOMIZE);
       if (new_personality & ADDR_NO_RANDOMIZE) {
           execv(argv[0], argv);
       }
    }
    
    // Create a lua engine, then free it.
    lua_State *L = luaL_newstate();
    lua_close(L);

    // Allocate a big block of RAM.
    malloc(4*1024*1024);
    
    // Now print the hash of some mallocs.
    int hash = 0;
    for (int i = 0; i < 100; i++) {
        int n = (int)(ptrdiff_t)malloc(1);
        hash = (hash * 17) + n;
    }
    fprintf(stderr, "%08x\n", hash);
}

字符串
以下是三次运行的输出:

$ ./foo
c75ba620
$ ./foo
0e2e5210
$ ./foo
7c38ba10


我不知道为什么lua分配是相关的,但是如果没有luaL_newstate和lua_close,它就不会这样做。如果没有中间的4兆字节的malloc,它也不会这样做。
更新2:
我发现了不确定性的来源。lua库调用time(0)来获取当前时间,然后它将其用作随机种子,影响它所做的内存分配。之所以花了这么长时间才发现这一点,是因为'strace'没有向'time(0)'报告系统调用。我曾假设所有系统调用都是由strace报告的。

yr9zkbsy

yr9zkbsy1#

在本例中,不确定性来自Lua运行时内部。Lua使用'time(0)'生成随机种子,然后影响Lua进行的malloc调用。
这个不确定性的来源对我隐藏的原因是Linux的'strace'不报告对“time(0)"的系统调用,我以为strace会显示任何在连续执行中返回不同值的系统调用。

b4qexyjb

b4qexyjb2#

如果您使用的是Linux,则需要做两件事:
1.设置ADDR_NO_RANDOMIZATION,就像你做的那样。
1.使你的malloc实现具有确定性。例如,对于dlmalloc,这就像#define LACKS_TIME_H 1和recompiling. wasi-libc一样简单。如果定义了LACKS_TIME_H,dlmalloc将简单地使用一个固定的堆栈地址来种子,而不是time(0)或/dev/urandom:https://github.com/WebAssembly/wasi-libc/blob/main/dlmalloc/src/malloc.c#L3181

tyky79it

tyky79it3#

你可能是内存泄漏问题的受害者。你的程序使用的内存不是你分配的。如果这个内存地址在你分配的内存块中,程序不会崩溃。但是如果程序访问你没有读/写权限的内存块,操作系统会杀死你的程序-你认为这是崩溃。

qkf9rpyu

qkf9rpyu4#

有人知道malloc在连续执行时返回不同地址的原因吗?
假设该过程实际上是单线程的,并且malloc没有进行块地址随机化,我猜:
您的程序可能在某个时候分配了随机数量的内存,例如,在计算要分配的某个块的大小时使用了不可靠的垃圾值。所有后续分配返回的内存可能会受到影响(因此是随机的)。
当然,这将取决于malloc的实现和实际分配大小:如果它被实现为每个线程的桶分配器,并且随机块大小不超过桶大小,那么对后续分配的影响几乎为零。

相关问题