Clang 的 UBSan 和函数指针:这是非法的吗?
Clang's UBSan & Function Pointer: Is this illegal?
我正在尝试通过函数指针调用一些 C++ 函数 table,该函数指针作为 C 符号从共享对象中导出。该代码实际上可以正常工作,但 Clang 的未定义行为消毒剂(= UBSan)认为我进行的调用是非法的,如下所示:
==11410==WARNING: Trying to symbolize code, but external symbolizer is not initialized!
path/to/HelloWorld.cpp:25:13: runtime error: call to function (unknown) through pointer to incorrect function type 'foo::CBar &(*)()'
(./libFoo.so+0x20af0): note: (unknown) defined here
由于 Clang 的未定义行为净化器,通过函数指针间接调用 returns C++ 标准 class 对象引用的函数是合法的 但这是非法的对于用户定义的 class。有人能告诉我它有什么问题吗?
我一直在尝试在 Ubuntu 14.04 上使用 Clang-llvm 3.4-1ubuntu3 和 CMake 2.8.12.2。要重现该现象,请将以下 5 个文件 放在同一目录中并调用 build.sh。它将创建一个 makefile 并构建项目,并且 运行 executable.
Foo.h
#ifndef FOO_H
#define FOO_H
#include <string>
//
#define EXPORT __attribute__ ((visibility ("default")))
namespace foo {
class CBar
{
// empty
};
class CFoo
{
public:
static CBar& GetUdClass();
static std::string& GetStdString();
};
// function pointer table.
typedef struct
{
CBar& (*GetUdClass)();
std::string& (*GetStdString)();
} fptr_t;
//! function pointer table which is exported.
extern "C" EXPORT const fptr_t FptrInFoo;
}
#endif
Foo.cpp
#include "Foo.h"
#include <iostream>
using namespace std;
namespace foo
{
// returns reference of a static user-defined class object.
CBar& CFoo::GetUdClass()
{
cout << "CFoo::GetUdClass" << endl;
return *(new CBar);
}
// returns reference of a static C++ standard class object.
std::string& CFoo::GetStdString()
{
cout << "CFoo::GetStdString" << endl;
return *(new string("Hello"));
}
// function pointer table which is to be dynamically loaded.
const fptr_t FptrInFoo = {
CFoo::GetUdClass,
CFoo::GetStdString,
};
}
HelloWorld.cpp
#include <iostream>
#include <string>
#include <dirent.h>
#include <dlfcn.h>
#include "Foo.h"
using namespace std;
using namespace foo;
int main()
{
// Retrieve a shared object.
const string LibName("./libFoo.so");
void *pLibHandle = dlopen(LibName.c_str(), RTLD_LAZY);
if (pLibHandle != 0) {
cout << endl;
cout << "Info: " << LibName << " found at " << pLibHandle << endl;
// Try to bind a function pointer table:
const string SymName("FptrInFoo");
const fptr_t *DynLoadedFptr = static_cast<const fptr_t *>(dlsym(pLibHandle, SymName.c_str()));
if (DynLoadedFptr != 0) {
cout << "Info: " << SymName << " found at " << DynLoadedFptr << endl;
cout << endl;
// Do something with the functions in the function table pointer.
DynLoadedFptr->GetUdClass(); // Q1. Why Clang UBSan find this is illegal??
DynLoadedFptr->GetStdString(); // Q2. And why is this legal??
} else {
cout << "Warning: Not found symbol" << endl;
cout << dlerror() << endl;
}
} else {
cout << "Warning: Not found library" << endl;
cout << dlerror() << endl;
}
cout << endl;
return 0;
}
CMakeLists.txt
project (test)
if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-rpath,$ORIGIN")
add_library(Foo SHARED Foo.cpp)
add_executable(HelloWorld HelloWorld.cpp)
target_link_libraries (HelloWorld dl)
build.sh
#!/bin/bash
# 1. create a build directory.
if [ -d _build ]; then
rm -rf _build
fi
mkdir _build
cd _build
# 2. generate a makefile.
CC=clang CXX=clang++ CXXFLAGS="-fvisibility=hidden -fsanitize=undefined -O0 -g3" cmake ..
# 3. build.
make
# 4. and run the executable.
./HelloWorld
我一直在努力寻找线索来深入研究这个问题,并意识到这个问题是由消毒剂 (-fsanitize=function) 的 "function" 选项发现的,但没有太多记录。如果你们能给我一个看起来像来自另一个星球的 运行time 错误消息的合理解释,我将不胜感激。谢谢。
Clang 在输出中指出的 "unknown" 是什么?
下面是 addr2line 的输出,用于检查消毒剂的 "unknown":
$ addr2line -Cfe _build/libFoo.so 0x20af0
foo::CFoo::GetUdClass()
path/to/Foo.cpp:12
嗯,果然是我期待的功能。你能猜出 Clang 看起来有何不同吗?
CBar 的 typeinfo
需要具有默认的函数类型可见性,Clang 在 Linux 上可执行文件和动态库将其视为相同;将 Foo.h 更改为:
class EXPORT CBar
{
...
}
我正在尝试通过函数指针调用一些 C++ 函数 table,该函数指针作为 C 符号从共享对象中导出。该代码实际上可以正常工作,但 Clang 的未定义行为消毒剂(= UBSan)认为我进行的调用是非法的,如下所示:
==11410==WARNING: Trying to symbolize code, but external symbolizer is not initialized!
path/to/HelloWorld.cpp:25:13: runtime error: call to function (unknown) through pointer to incorrect function type 'foo::CBar &(*)()'
(./libFoo.so+0x20af0): note: (unknown) defined here
由于 Clang 的未定义行为净化器,通过函数指针间接调用 returns C++ 标准 class 对象引用的函数是合法的 但这是非法的对于用户定义的 class。有人能告诉我它有什么问题吗?
我一直在尝试在 Ubuntu 14.04 上使用 Clang-llvm 3.4-1ubuntu3 和 CMake 2.8.12.2。要重现该现象,请将以下 5 个文件 放在同一目录中并调用 build.sh。它将创建一个 makefile 并构建项目,并且 运行 executable.
Foo.h
#ifndef FOO_H
#define FOO_H
#include <string>
//
#define EXPORT __attribute__ ((visibility ("default")))
namespace foo {
class CBar
{
// empty
};
class CFoo
{
public:
static CBar& GetUdClass();
static std::string& GetStdString();
};
// function pointer table.
typedef struct
{
CBar& (*GetUdClass)();
std::string& (*GetStdString)();
} fptr_t;
//! function pointer table which is exported.
extern "C" EXPORT const fptr_t FptrInFoo;
}
#endif
Foo.cpp
#include "Foo.h"
#include <iostream>
using namespace std;
namespace foo
{
// returns reference of a static user-defined class object.
CBar& CFoo::GetUdClass()
{
cout << "CFoo::GetUdClass" << endl;
return *(new CBar);
}
// returns reference of a static C++ standard class object.
std::string& CFoo::GetStdString()
{
cout << "CFoo::GetStdString" << endl;
return *(new string("Hello"));
}
// function pointer table which is to be dynamically loaded.
const fptr_t FptrInFoo = {
CFoo::GetUdClass,
CFoo::GetStdString,
};
}
HelloWorld.cpp
#include <iostream>
#include <string>
#include <dirent.h>
#include <dlfcn.h>
#include "Foo.h"
using namespace std;
using namespace foo;
int main()
{
// Retrieve a shared object.
const string LibName("./libFoo.so");
void *pLibHandle = dlopen(LibName.c_str(), RTLD_LAZY);
if (pLibHandle != 0) {
cout << endl;
cout << "Info: " << LibName << " found at " << pLibHandle << endl;
// Try to bind a function pointer table:
const string SymName("FptrInFoo");
const fptr_t *DynLoadedFptr = static_cast<const fptr_t *>(dlsym(pLibHandle, SymName.c_str()));
if (DynLoadedFptr != 0) {
cout << "Info: " << SymName << " found at " << DynLoadedFptr << endl;
cout << endl;
// Do something with the functions in the function table pointer.
DynLoadedFptr->GetUdClass(); // Q1. Why Clang UBSan find this is illegal??
DynLoadedFptr->GetStdString(); // Q2. And why is this legal??
} else {
cout << "Warning: Not found symbol" << endl;
cout << dlerror() << endl;
}
} else {
cout << "Warning: Not found library" << endl;
cout << dlerror() << endl;
}
cout << endl;
return 0;
}
CMakeLists.txt
project (test)
if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-rpath,$ORIGIN")
add_library(Foo SHARED Foo.cpp)
add_executable(HelloWorld HelloWorld.cpp)
target_link_libraries (HelloWorld dl)
build.sh
#!/bin/bash
# 1. create a build directory.
if [ -d _build ]; then
rm -rf _build
fi
mkdir _build
cd _build
# 2. generate a makefile.
CC=clang CXX=clang++ CXXFLAGS="-fvisibility=hidden -fsanitize=undefined -O0 -g3" cmake ..
# 3. build.
make
# 4. and run the executable.
./HelloWorld
我一直在努力寻找线索来深入研究这个问题,并意识到这个问题是由消毒剂 (-fsanitize=function) 的 "function" 选项发现的,但没有太多记录。如果你们能给我一个看起来像来自另一个星球的 运行time 错误消息的合理解释,我将不胜感激。谢谢。
Clang 在输出中指出的 "unknown" 是什么?
下面是 addr2line 的输出,用于检查消毒剂的 "unknown":
$ addr2line -Cfe _build/libFoo.so 0x20af0
foo::CFoo::GetUdClass()
path/to/Foo.cpp:12
嗯,果然是我期待的功能。你能猜出 Clang 看起来有何不同吗?
CBar 的 typeinfo
需要具有默认的函数类型可见性,Clang 在 Linux 上可执行文件和动态库将其视为相同;将 Foo.h 更改为:
class EXPORT CBar
{
...
}