在GCC和Clang中堆叠粉碎(可能由于金丝雀)

问题描述 投票:0回答:2

我试图了解GCC中"stack smashing"错误的可能来源,但不是Clang。

具体来说,当我用一些调试符号编译一段代码时

set(CMAKE_CXX_FLAGS_DEBUG "-g")

并使用GCC C ++编译器(GNU 5.4.0),应用程序崩溃

*** stack smashing detected ***: ./testprogram terminated
Aborted (core dumped)

但是,当我使用Clang 3.8.0时,程序完成且没有错误。

我的第一个想法是,海湾合作委员会的金丝雀可能正在抓住克朗没有的缓冲区溢出。所以我添加了额外的调试标志

set(CMAKE_CXX_FLAGS_DEBUG "-g -fstack-protector-all")

但Clang仍编译一个没有错误运行的程序。对我来说,这表明问题可能不是缓冲区溢出(正如您经常看到的堆栈粉碎错误),而是分配问题。

在任何情况下,当我添加ASAN标志时:

set(CMAKE_CXX_FLAGS_DEBUG "-g -fsanitize=address")

两个编译器都会产生一个程序崩溃并出现相同的错误。特别,

GCC 5.4.0:

==1143==ERROR: AddressSanitizer failed to allocate 0xdfff0001000 (15392894357504) bytes at address 2008fff7000 (errno: 12)
==1143==ReserveShadowMemoryRange failed while trying to map 0xdfff0001000 bytes. Perhaps you're using ulimit -v
Aborted (core dumped)

Clang 3.8.0:

==1387==ERROR: AddressSanitizer failed to allocate 0xdfff0001000 (15392894357504) bytes at address 2008fff7000 (errno: 12)
==1387==ReserveShadowMemoryRange failed while trying to map 0xdfff0001000 bytes. Perhaps you're using ulimit -v
Aborted (core dumped)

有人可以给我一些关于这个错误可能来源的提示吗?我正在艰难地追踪发生这种情况的路线,因为它是在一个非常大的代码库中。


编辑

该问题尚未解决,但与以下功能隔离:

void get_sparsity(Data & data) {
    T x[n_vars] = {};
    T g[n_constraints] = {}; 
    for (Index j = 0; j < n_vars; j++) {

        const T x_j = x[j];
        x[j] = NAN;
        eval_g(n_vars, x, TRUE, n_constraints, g, &data);
        x[j] = x_j;

        std::vector<Index> nonzero_entries;
        for (Index i = 0; i < n_constraints; i++) {
            if (isnan(g[i])) {
                data.flattened_nonzero_rows.push_back(i);
                data.flattened_nonzero_cols.push_back(j);
                nonzero_entries.push_back(i);
            }
        }
        data.nonzeros.push_back(nonzero_entries);
    }
    int internal_debug_point = 5;
}

这被称为:

get_sparsity(data);
int external_debug_point= 6;

但是,当我在get_sparsity函数internal_debug_point = 5的最后一行放置调试点时,它会毫无问题地到达该行。但是,在退出该函数时,在它到达外部调试点external_debug_point = 6之前,它会因错误而崩溃

received signal SIGABRT, Aborted.
0x00007ffffe315428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54

我的猜测是GCC只在退出该函数时检查金丝雀,因此错误实际上发生在函数内部。这听起来合理吗?如果是这样,那么有没有办法让GCC或clang更频繁地进行金丝雀检查?

c++ gcc clang
2个回答
1
投票

我怀疑ASan的内存不足。

我不认为ASan错误意味着你的程序正试图分配那个内存,这意味着ASan正试图为它自己分配它(它说“影子内存”这是ASan用来跟踪你的程序分配的内存) 。

如果迭代次数(和数组的大小)n_vars很大,那么该函数将在每个循环中为新的std::vector使用额外的内存,迫使ASan跟踪越来越多的内存。

您可以尝试将局部向量移出循环(这可能会提高函数的性能):

std::vector<Index> nonzero_entries;
for (Index j = 0; j < n_vars; j++) {

    // ...

    for (Index i = 0; i < n_constraints; i++) {
        if (isnan(g[i])) {
            data.flattened_nonzero_rows.push_back(i);
            data.flattened_nonzero_cols.push_back(j);
            nonzero_entries.push_back(i);
        }
    }
    data.nonzeros.push_back(nonzero_entries);
    nonzero_entries.clear();
}

这将为nonzero_entries重用相同的内存,而不是每次迭代为新向量分配和释放内存。


0
投票

试图找出堆栈问题的根源是无处可去的。所以我尝试了不同的方法。通过调试,我缩小了上面的函数get_sparsity作为罪魁祸首。调试器没有给我任何关于问题发生的确切提示,但是它位于该函数内部。有了这些信息,我将该函数xg中仅有的两个堆栈变量切换为堆变量,以便valgrind可以帮助我找到错误(sgcheck即将出现空白)。具体来说,我将上面的代码修改为

void get_sparsity(Data & data) {

    std::vector<T> x(n_vars, 0);
    std::vector<T> g(n_constraints, 0);

    /* However, for our purposes, it is easier to make an std::vector of Eigen
     * vectors, where the ith entry of "nonzero_entries" contains a vector of
     * indices in g for which g(indices) are nonzero when perturbing x(i).
     * If that sounds complicated, just look at the code and compare to
     * the code where we use the sparsity structure.
     */
    for (Index j = 0; j < n_vars; j++) {

        const T x_j = x[j];
        x[j] = NAN;
        Bool val = eval_g(n_vars, x.data(), TRUE, n_constraints, g.data(), &data);
        x[j] = x_j;

        std::vector<Index> nonzero_entries;
        for (Index i = 0; i < n_constraints; i++) {
            if (isnan(g[i])) {
                data.flattened_nonzero_rows.push_back(i);
                data.flattened_nonzero_cols.push_back(j);
                nonzero_entries.push_back(i);
            }
        }
        data.nonzeros.push_back(nonzero_entries);
    }
    int bob = 5;
    return;
}

然后valgrinded它找到违规行。现在我知道问题出在哪里,我可以解决问题。

© www.soinside.com 2019 - 2024. All rights reserved.