为什么gmp会在这里与"invalid next size"重新定位一起崩溃?
Why does gmp crash with "invalid next size" to realloc here?
我有一个简单的函数,使用 gmp C++绑定:
#include <inttypes.h>
#include <memory>
#include <gmpxx.h>
mpz_class f(uint64_t n){
std::unique_ptr<mpz_class[]> m = std::make_unique<mpz_class[]>(n + 1);
m[0] = 0;
m[1] = 1;
for(uint64_t i = 2; i <= n; ++i){
m[i] = m[i-1] + m[i-2];
}
return m[n];
}
int main(){
mpz_class fn;
for(uint64_t n = 0;; n += 1){
fn = f(n);
}
}
据推测,make_unique
应该分配一个新数组并在函数返回时释放它,因为拥有它的唯一指针的生命周期结束了。 据推测,返回的mpz_class
对象应该是一个副本,并且不受此数组被删除的影响。 程序崩溃并显示错误:
realloc(): invalid next size
如果我查看 gdb 中的核心转储,我会得到堆栈跟踪:
#0 raise()
#1 abort()
#2 __libc_message()
#3 malloc_printerr()
#4 _int_realloc()
#5 realloc()
#6 __gmp_default_reallocate()
#7 __gmpz_realloc()
#8 __gmpz_add()
#9 __gmp_binary_plus::eval(v, w, z)
#10 __gmp_expr<...>::eval(this, this, p)
#11 __gmp_set_expr<...>(expr, z)
#12 __gmp_expr<...>::operator=<...>(expr, this)
#13 f(n)
#14 main(argc, argv)
这对我没有帮助,除了它表明问题可能来自使用表达式模板的 gmpxx(堆栈帧 9-12 表示这一点,valgrind 和堆栈帧 12 将错误之前执行的代码的最后一行放在m[1] = 1;
处(。 Valgrind 说这一行有一个大小为 8 的无效读取,但列出了与它后面的其余跟踪对应的堆栈条目,然后说在下一条指令中有一个无效的写入。 无效读取是"大小为 24 的块 [由make_unique
] 分配"之后的 8 个字节,而无效写入为 null。 显然,这一行也不应该引起,因为它应该只读取一个指针,然后写入它指向的缓冲区的一部分,而缓冲区绝对没有地址0x0。 我决定使用 C++ 绑定,即使我总是使用 C 的 gmp,因为我认为编写会更快,但此错误确保情况并非如此。 这是 gmp 的问题还是我分配了错误的阵列? 如果我直接使用new
和delete
,或者手动内联函数调用,我会收到类似的错误。 我觉得问题可能与实际存储表达式模板mpz_class
而不是适当的具体化值有关。
我正在使用带有g++ -std=c++17 -O2 -g -Wall ...
和 GMP 6.1.2-3 的 GCC 9.2.0。 Clang和GCC都没有报告任何错误。
如果我们在瓦尔格林德下运行,我们会看到:
==1948514== Invalid read of size 8
==1948514== at 0x489B0F0: __gmpz_set_si (in /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2)
==1948514== by 0x10945E: __gmp_expr<__mpz_struct [1], __mpz_struct [1]>::assign_si(long) (gmpxx.h:1453)
==1948514== by 0x1094E3: __gmp_expr<__mpz_struct [1], __mpz_struct [1]>::operator=(int) (gmpxx.h:1538)
==1948514== by 0x109248: f(unsigned long) (59678712.cpp:8)
==1948514== by 0x109351: main (59678712.cpp:18)
==1948514== Address 0x4e08ca0 is 8 bytes after a block of size 24 alloc'd
==1948514== at 0x483650F: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1948514== by 0x10953F: std::_MakeUniq<__gmp_expr<__mpz_struct [1], __mpz_struct [1]> []>::__array std::make_unique<__gmp_expr<__mpz_struct [1], __mpz_struct [1]> []>(unsigned long) (unique_ptr.h:855)
==1948514== by 0x10920C: f(unsigned long) (59678712.cpp:6)
==1948514== by 0x109351: main (59678712.cpp:18)
这表明当我们调用f(0)
时,我们写给m[1]
,这是越界的。 这是未定义的行为,所以任何事情都可能发生。 幸运的是,你遇到了崩溃,而不是更微妙的事情。
简单修复:
mpz_class f(uint64_t n) {
if (!n) return 0;
顺便说一句,更喜欢<cstdint>
而不是<inttypes.h>
,并写成std::uint64_t
等。
- 当回溯以零开始时,如何调试崩溃
- 内联映射初始化的动态atexit析构函数崩溃
- 执行函数时导致崩溃的变量
- 程序崩溃并显示"std::out_of_range"错误
- CoInitialize()在单独的线程上崩溃而不返回
- 使用调试/崩溃报告将应用程序部署到客户端
- 为什么所有C++编译器都会崩溃或挂起此代码
- 为什么lambda在clang上崩溃而不是在gcc上崩溃
- 为什么我的多线程作业队列崩溃
- ExtractIconEx:可以工作,但偶尔会崩溃
- 为什么引用传递会导致此崩溃(C++)
- 试图创建流或fopen时程序崩溃
- 重新定位图像时如何前进到下一个内存块
- 类对象数组的问题会导致崩溃
- 排序时无法执行交换操作.我做的时候它会崩溃.为什么
- 为什么要增加导致崩溃的指针
- 在虚幻引擎中删除NXOpen对象时崩溃
- 为什么它只打印双链接列表的第一个值,而我的程序却崩溃了
- 为什么gmp会在这里与"invalid next size"重新定位一起崩溃?
- 在函数内部错误定位后,从函数中释放返回的指针会导致芯片崩溃