【问题标题】:Segmentation fault when reading variables in pointed function在指向函数中读取变量时出现分段错误
【发布时间】:2019-03-27 21:27:15
【问题描述】:

我有以下几段代码:(注意:由于我无法共享数据,代码被简化并更改了很多)

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 调用,但在尝试输出值ab 时出现Seg Fault。 静态分析器给出以下错误信息:

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

【问题讨论】:

  • 投票结束是一个错字。函数签名不匹配。注意int (func_t)(int c, void* p, VExtension* v)bool func(int const&amp; a, void* const&amp; b, VExtension* const&amp; v) 不是一回事。
  • 为什么some_api.h 也为该方法定义了一个实现?这似乎是一个错误。
  • 如果这是一个 C 方法,那么你不能使用引用。
  • @NathanOliver 是的,它的工作原理也很奇怪,但是当我适当地更改类型时,我得到了编译错误。
  • @DavidHovsepyan 你能修改代码吗?它会给你什么错误?

标签: c++ c c++11 memory-management bazel


【解决方案1】:

修正后的代码可以工作

如果 func_t 将使用与 func() 相同的签名来定义,那么一切都会好起来的,因为 b 的任意值永远不会被取消引用:

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

demo

但你的代码不能,所以这里有一些可疑的地方

至少从您显示的运行时跟踪来看,您的代码似乎可以编译。但是根据您发布的sn-ps,代码根本不应该编译。因此,我怀疑您的不同标头设法在不同的编译单元中使用来自func_t 的不同定义来创建危险的组合。如果是这种情况,您将处于未定义行为的世界中。

例如,返回类型 int 与返回类型 bool 不匹配。但更致命的是,func_t 中的参数是普通值,而func() 中它们都是引用。因此,如果编译器被欺骗,func() 的调用者使用的调用约定将与func() 使用的调用约定不兼容,从而导致无效的内存访问(很可能——但这将取决于实现——生成的调用代码会发送值参数,但生成的接收代码会期望这些参数是指向值的指针并尝试访问指向的内存位置,这将是无效的)。

补充说明

现在,我不是语言律师,但看到一个函数使用 C 调用约定 (extern "C"),使用 C 中不存在的参数和返回类型(即 bool 和引用)也很有趣。

其他见解

使用func_t 的更正定义在一个编译器上查看assembly code generatedextension_init

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

如果using your original definition,生成的代码看起来不一样:

                ; 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

【讨论】:

  • 你说得对,我发现我有不同的.h 文件和不同的函数指针声明,所以我在这里放错了声明。实际上,我找到了正确的声明,因此代码可以编译,但正如您所提到的,由于不同的翻译单元中的 func_t 声明不同,这是未定义的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-19
  • 2020-03-20
  • 2016-11-10
  • 1970-01-01
  • 2017-09-20
  • 2020-02-07
  • 2017-09-18
相关资源
最近更新 更多