Ошибка сегментации при чтении переменных в указанной функции - PullRequest
0 голосов
/ 23 октября 2018

У меня есть следующие фрагменты кода: (Примечание: коды упрощены и сильно изменены, поскольку я не могу поделиться данными)

exercise.cpp

#include "some_api.h"

extern "C" int extension_init(func_t*, VExtension*);

int main(int argc, char** argv)
{
    VExtension ve;
    extension_init(&func, &ve);
    return 0;
} 

some_api.h

bool func(int const& a, void* const& b, VExtension* const& v)
{
    std::cout << a << b << std::endl;
}

api.h

typedef int (func_t)(int c, void* p, VExtension* v)

file.cpp

#include "api.h" // this is included implicitly

extern "C" int extension_init(func_t* F, VExtension* v)
{
    intptr_t ver = 7;
    F(1, (void*)ver, v);
}

Итак, когда F вызывается func вызывается из some_api.h, но Seg Fault появляется при попытке вывести значения a и b.Статический анализатор выдает следующее сообщение об ошибке:

ASAN:DEADLYSIGNAL
=================================================================
==15==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000001 (pc 0x55d6dbe48ca2 bp 0x7fff79dbf320 sp 0x7fff79dbf300 T0)
==15==The signal is caused by a READ memory access.
==15==Hint: address points to the zero page.
    #0 0x55d6dbe48ca1 in func(int const&, void* const&, VExtension* const&) some_api.h:279
    #1 0x55d6dbefe697 in file.cpp:809
    #2 0x55d6dbe5373a in main exercise.cpp:123
    #3 0x7f9c65393b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #4 0x55d6dbd49839 in _start (//...)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: some_api.h:279 in func(int const&, void* const&, VExtension* const&)
==15==ABORTING

1 Ответ

0 голосов
/ 23 октября 2018

Исправленный код может работать

Если func_t будет определен с использованием той же сигнатуры, что и func(), все будет в порядке, поскольку произвольное значение b никогда не разыменовывается:

typedef bool (func_t)(int const& a, void* const& b, VExtension* const& v);

См. демо .

Но ваш код не может, поэтому здесь происходит что-то подозрительное

Ваш код, кажется, компилируется, по крайней мере, судя по трассировке времени выполнения, которую вы отобразили.Но в соответствии с опубликованными вами фрагментами код вообще не должен компилироваться.Поэтому я подозреваю, что вашим различным заголовкам удалось создать опасную смесь, используя разные определения из func_t в разных единицах компиляции.Если это так, вы окажетесь в мире неопределенного поведения.

Например, тип возвращаемого значения int не соответствует типу возвращаемого значения bool.Но гораздо более пагубно, что в func_t параметры являются простыми значениями, тогда как в func() все они являются ссылками.Поэтому, если компилятор обманут, соглашение о вызовах, используемое вызывающим func(), будет несовместимо с соглашениями о вызовах, используемыми func(), что приведет к недопустимому доступу к памяти (скорее всего - но это будет зависеть от реализации -сгенерированный вызывающий код будет отправлять аргументы значений, но сгенерированный принимающий код будет ожидать, что эти параметры будут указателями на значения, и попытаться получить доступ к указанным ячейкам памяти, что будет недопустимым).

Дополнительное замечание

Теперь я не адвокат по языку, но также интересно увидеть функцию, использующую соглашение о вызовах C (extern "C"), использующую параметры и возвращаемые типы, которые делаютне существует в C (т. е. bool и ссылки).

Дополнительные сведения

Просмотр кода сборки , сгенерированного для extension_init на одном компиляторе с использованием исправленного определения func_t:

lea rdx, [rsp+8]           ; load effective address of third paramvalue
lea rsi, [rsp+24]          ; load effective address of second param value
mov QWORD PTR [rsp+24], 7  ; initialize value 
lea rdi, [rsp+20]          ; load effective address of first param value
mov DWORD PTR [rsp+20], 1  ; initilize value
call rax

Если использует исходное определение , сгенерированный код выглядит иначе:

                ; the third value was left out by the global initializer
                ;  but it was previously set with an LEA to load the effective 
                ; address of the struct. 
mov esi, 7      ; pass the second value
mov edi, 1      ; pass the first value
call rax
...