Почему gmp cra sh с "недопустимым следующим размером" в reallo c здесь? - PullRequest
0 голосов
/ 10 января 2020

У меня есть простая функция, использующая привязки 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 должен выделить массив fre sh и освободить его, когда функция вернется, поскольку уникальный указатель, которому она принадлежит, имеет свой конец жизни. Предположительно, возвращаемый объект 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;). Вэлгринд говорит, что в этой строке есть недопустимое чтение размера 8, но перечисляет записи стека, соответствующие остальной части трассы после него, а затем говорит, что в следующей инструкции есть недопустимая запись. Недопустимое чтение составляет 8 байт после «блока размером 24 allo c 'd [by make_unique]», в то время как недопустимая запись равна нулю. Очевидно, что эта строка также не должна вызывать, поскольку она должна только читать указатель и затем записывать в часть буфера, на который он указывает, который определенно не имеет адреса 0x0. Я решил использовать привязки C ++, хотя я всегда использую gmp из C, потому что думал, что писать будет быстрее, но эта ошибка гарантировала, что это не так. Это проблема с gmp или я неправильно распределяю массив? Я получаю похожие ошибки, если я использовал new и delete напрямую или если я вручную встроил вызов функции. Я чувствую, что проблема может быть связана с mpz_class, на самом деле хранящим шаблон выражения, а не с конкретным значением.

Я использую G CC 9.2.0 с g++ -std=c++17 -O2 -g -Wall ... и GMP 6.1. 2-3. Ни Clang, ни G CC не сообщают об ошибках.

1 Ответ

2 голосов
/ 10 января 2020

Если мы запускаем в Valgrind, мы видим:

==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], что выходит за пределы. Это неопределенное поведение, поэтому может произойти все что угодно. К счастью, у вас есть cra * sh, а не что-то более тонкое и др c.

...