通过指向错误函数类型的指针来调用函数(未知) [英] Call to function (unknown) through pointer to incorrect function type

查看:157
本文介绍了通过指向错误函数类型的指针来调用函数(未知)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个动态链接到库的程序. 该程序将一个函数指针传递到该库以执行.

I have a program that dynamically links against a library. The program passes a function pointer to that library, to execute.

但是ubsan(未定义的行为清理器)指定了指针在错误的函数类型上.而且只会发生

But the ubsan (Undefined Behavior Sanitizer) specified that the pointer is on an incorrect function type. And that occurs only

  • 如果回调函数具有一个类作为参数
  • 如果回调函数具有一个类作为参数,但仅前向声明
  • 如果我指定编译标志:-fvisibility = hidden.

我用clang编译我的项目.

I use clang to compile my project.

这是clang未定义行为消毒器中的错误吗?

Is it a bug in clang undefined behavior sanitizer?

以下代码简化为一个简单的测试用例.查看评论,看看我们可以采取行动删除一些警告

The following code is reduced to a simple test case. Check the comments to see where we can act to remove some warnings

应用程序代码:

Main.cxx

#include "Caller.h"
#include "Param.h"

static void FctVoid()
{
}
static void FctInt(int _param)
{
   static_cast<void>(&_param);
}
static void FctCaller(Caller &_caller)
{
   static_cast<void>(&_caller);
}
static void FctParam(Param const &_param)
{
   static_cast<void>(&_param);
}

int main()
{
   Param param;
   Caller::CallVoid(&FctVoid);
   Caller::CallInt(&FctInt);
   Caller::CallThis(&FctCaller);
   Caller::CallParam(&FctParam, param);
   return 0;
}

该库文件的代码为:

Caller.cxx:

Caller.cxx:

#include "Caller.h"
// To uncomment to fix one warning
//#include "Param.h"
void Caller::CallVoid(FctVoidT _fct)
{
   _fct();
}
void Caller::CallInt(FctIntT _fct)
{
   _fct(32);
}
void Caller::CallThis(FctThisT _fct)
{
   Caller caller;
   _fct(caller);
}
void Caller::CallParam(FctParamT const &_fct, Param const &_param)
{
   _fct(_param);
}

Caller.h

#ifndef __Caller_h_
#define __Caller_h_
#include "owExport.h"

class Param;
class EXPORT_Library Caller
{
public:
   typedef void(*FctVoidT)();
   static void CallVoid(FctVoidT _fct);
   typedef void(*FctIntT)(int);
   static void CallInt(FctIntT _fct);
   typedef void(*FctThisT)(Caller &);
   static void CallThis(FctThisT _fct);
   typedef void(*FctParamT)(Param const &);
   static void CallParam(FctParamT const &_fct, Param const &_param);
};
#endif

Param.h

#ifndef __Param_h_
#define __Param_h_
#include "owExport.h"
class EXPORT_Library Param
{
public:
};
#endif

owExport.h

owExport.h

#ifndef __owExport_h_
#define __owExport_h_
#define OW_EXPORT __attribute__ ((visibility("default")))
#define OW_IMPORT
// Use this one to fix one warning
#define OW_IMPORT __attribute__ ((visibility("default")))
#ifdef Library_EXPORTS
#  define EXPORT_Library OW_EXPORT
#else
#  define EXPORT_Library OW_IMPORT
#endif
#endif

用于配置项目的CMakeLists.txt:

CMakeLists.txt that configures the project:

cmake_minimum_required(VERSION 3.0.0)
project(TestFunction)
set(BUILD_SHARED_LIBS ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fsanitize=undefined ")

# Act here to for the call of function through pointer to incorrect function type
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")

add_library(Library Caller.cxx Param.cxx)
add_executable(TestWithLib Main.cxx)
target_link_libraries(TestWithLib Library)

推荐答案

第一:如果您编辑问题以添加修复程序,那就不好了.这很难回答.

First: It is not good if you edit the question to add fixes already. This makes it hard to answer.

对于您的问题:您基本上有两个问题:首先是符号Caller, second with Param`,两者基本上是相同的.

For your problem: You basically have 2 issues: First with the Ssymbol Caller, second withParam`, both are basically the same.

对于问题的根源:UBSAN将指针的typeinfo与预期的typeinfo进行比较.如果typeinfo不同,则显示错误. typeinfo比较是通过指针比较完成的.这对于提高速度非常有用,但会带来一个细微的问题:即使实际类型实际上是相同的,它们也可能不会共享相同的typeinfo.当您从共享库中抛出一个类型并想将其捕获到可执行文件中时,这一点也很重要(反之亦然):捕获是通过typeinfo比较来完成的,并且两种类型不完全相同(共享相同的typeinfo)你不会抓住它的.

For the source of the issue: UBSAN compares the typeinfo of the pointer to the expected typeinfo. If the typeinfo differs, it shows the error. typeinfo comparison is done by pointer comparison. This is great for speed but introduces a subtle issue: Even when the actual types are literally the same, they might not share the same typeinfo. This is also important when you throw a type from a shared library and want to catch it in the executable (or vice-versa): Catching is done by typeinfo comparison and if the two types are not exactly the same (share the same typeinfo) you won't catch it.

因此,您的第一个问题是class EXPORT_Library Caller:您有条件地将EXPORT_Library定义为导出"或不导出".如果它是从多个DSO导出的,则typeinfo将被合并.在您的情况下,可以将其导出到共享库中,但不能导出到可执行文件中,这会阻止它们的合并.您可以为此使用BOOST_SYMBOL_EXPORTOW_EXPORT.

So your first issue is class EXPORT_Library Caller: You conditionally define EXPORT_Library to either be "exported" or not. If it is exported from multiple DSOs then the typeinfos will be merged. In your case you export it in the shared library but not in the executable which prevents merging them. You can use BOOST_SYMBOL_EXPORT or OW_EXPORT for this.

第二个问题是反过来(假设EXPORT_Library==OW_EXPORT):当包含Param.h标头时,导出Param,这仅由可执行文件而不是共享库完成.再次,typeinfos没有合并->与RTTI系统的类型不同.

Second issue is the other way round (assuming EXPORT_Library==OW_EXPORT): Param is exported when the Param.h header is included which is only done by the executable not by the shared library. Again typeinfos not merged -> different types to the RTTI system.

底线:在DSO边界上导出要使用的所有类.

Bottom line: Export all your classes you want to use over DSO boundaries.

这篇关于通过指向错误函数类型的指针来调用函数(未知)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆