1

I'm trying to apply clang's UBSan on my application which uses dlopen/dlsym heavily. However, When I apply UBSan, some warnings occured which I couldn't understand fully.

Sample code with one header / two files.

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 & Run

$ 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

First and and third error is obvious: I used reinterpret_cast and UBSan can't understand where such pointer came from. However, I can't understand why Fooand FooImpls inheritance relationship is not appled when dealing with vptr. Does it could be a real problem or UBSan's bug(or limitations?)

Byoungchan Lee
  • 1,372
  • 13
  • 28
  • I believe the first and third error are actually because `extern "C"` function pointers and undecorated function pointers aren't quite the same. Try putting the two usings from the test in an `extern "C"` block and see if that makes those errors go away. – Sebastian Redl Jul 12 '17 at 10:24
  • Adding `extern "C"` on the function pointer type won't help. – Byoungchan Lee Jul 13 '17 at 00:00

0 Answers0