Clang的UBsan对共享对象上导出的虚拟类的警告

时间:2017-07-12 10:09:12

标签: c++ clang ubsan

我正在尝试将clang的UBSan应用于我的应用程序,该应用程序使用了dlopen / dlsym。但是,当我申请UBSan时,发生了一些我无法完全理解的警告。

包含一个标题/两个文件的示例代码。

foo.h中

#ifndef FOO_H
#define FOO_H

#define EXPORT __attribute__((visibility("default")))

class EXPORT Foo {
public:
    virtual ~Foo() noexcept = 0;
    virtual int bar(int value) = 0;
};

inline Foo::~Foo() noexcept {}

#endif // FOO_H

Foo.cpp中

#include "Foo.h"
#include <cstddef>

#define EXPORT_C extern "C" EXPORT

EXPORT_C bool NewFoo(Foo** foo) noexcept;
EXPORT_C void DeleteFoo(Foo* foo) noexcept;

class EXPORT FooImpl: public Foo {
public:
    FooImpl();
    virtual ~FooImpl() noexcept;

    virtual int bar(int value);
private:
    int val_;
};

EXPORT
FooImpl::FooImpl(): val_(1) {};

EXPORT
FooImpl::~FooImpl() noexcept {};

EXPORT
int FooImpl::bar(int value) {
    return value + val_;
}

EXPORT_C
bool NewFoo(Foo** foo) noexcept {
    if (foo == NULL) {
        return false;
    }

    try {
        *foo = new FooImpl();
    }
    catch(...) {
        return false;
    }
    return true;
}

EXPORT_C
void DeleteFoo(Foo* foo) noexcept {
    delete foo;
}

TEST.CPP

#include "Foo.h"
#include <iostream>
#include <unistd.h>
#include <dlfcn.h>

using fNewFoo_t = bool(*)(Foo**);
using fDeleteFoo_t = void(*)(Foo*);

int main() {
    Foo* foo = NULL;

    void* handle = dlopen("./libfoo.so", RTLD_LAZY);
    if (handle == NULL) {
        fprintf(stderr, "dlopen: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    fNewFoo_t fNewFoo = reinterpret_cast<fNewFoo_t>(dlsym(handle, "NewFoo"));
    fDeleteFoo_t fDeleteFoo = reinterpret_cast<fDeleteFoo_t>(dlsym(handle, "DeleteFoo"));

    if ((fNewFoo == NULL) || (fDeleteFoo == NULL)) {
        fprintf(stderr, "dlsym: %s\n", dlerror());
        dlclose(handle);
        exit(EXIT_FAILURE);
    }

    if (!fNewFoo(&foo)) {
        fprintf(stderr, "NewFoo Failed\n");
        dlclose(handle);
        exit(EXIT_FAILURE);
    }

    int i = 0;
    for (int j = 0; j < 3; j++) {
        i = foo->bar(i);
        std::cout << i << "\n";
    }
    fDeleteFoo(foo);
    dlclose(handle);

    return 0;
}

Build&amp;运行

$ clang++ -fsanitize=undefined -g -fPIC -fvisibility=internal -std=gnu++11 -shared -o libfoo.so Foo.cpp
$ clang++ -fsanitize=undefined -g -std=gnu++11 -o test test.cpp -ldl
$ ./test
test.cpp:27:10: runtime error: call to function NewFoo through pointer to incorrect function type 'bool (*)(Foo **)'
Foo.cpp:31: note: NewFoo defined here
test.cpp:35:18: runtime error: member call on address 0x000001f312b0 which does not point to an object of type 'Foo'
0x000001f312b0: note: object is of type 'FooImpl'
 00 00 00 00  20 ad f2 1f 0e 7f 00 00  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  21 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'FooImpl'
1
2
3
test.cpp:38:5: runtime error: call to function DeleteFoo through pointer to incorrect function type 'void (*)(Foo *)'
Foo.cpp:46: note: DeleteFoo defined here

第一个和第三个错误是显而易见的:我使用了reinterpret_cast,而UBSan无法理解这样的指针来自何处。但是,我无法理解为什么在处理vptr时没有应用FooFooImpl的继承关系。它可能是真正的问题还是UBSan的错误(或限制?)

0 个答案:

没有答案