Skip to content

Commit

Permalink
Fix use-after-free bugs in debuginfo (#45016)
Browse files Browse the repository at this point in the history
Co-authored-by: Dilum Aluthge <dilum@aluthge.com>
  • Loading branch information
2 people authored and KristofferC committed May 25, 2022
1 parent 7a4cef7 commit 2cf85b6
Show file tree
Hide file tree
Showing 6 changed files with 529 additions and 347 deletions.
8 changes: 4 additions & 4 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -279,20 +279,20 @@ $(BUILDDIR)/julia_flisp.boot: $(addprefix $(SRCDIR)/,jlfrontend.scm flisp/aliase

# additional dependency links
$(BUILDDIR)/codegen-stubs.o $(BUILDDIR)/codegen-stubs.dbg.obj: $(SRCDIR)/intrinsics.h
$(BUILDDIR)/aotcompile.o $(BUILDDIR)/aotcompile.dbg.obj: $(SRCDIR)/jitlayers.h $(SRCDIR)/codegen_shared.h
$(BUILDDIR)/aotcompile.o $(BUILDDIR)/aotcompile.dbg.obj: $(SRCDIR)/jitlayers.h $(SRCDIR)/codegen_shared.h $(SRCDIR)/debug-registry.h
$(BUILDDIR)/ast.o $(BUILDDIR)/ast.dbg.obj: $(BUILDDIR)/julia_flisp.boot.inc $(SRCDIR)/flisp/*.h
$(BUILDDIR)/builtins.o $(BUILDDIR)/builtins.dbg.obj: $(SRCDIR)/iddict.c $(SRCDIR)/builtin_proto.h
$(BUILDDIR)/codegen.o $(BUILDDIR)/codegen.dbg.obj: $(addprefix $(SRCDIR)/,\
intrinsics.cpp jitlayers.h intrinsics.h codegen_shared.h cgutils.cpp ccall.cpp abi_*.cpp processor.h builtin_proto.h)
$(BUILDDIR)/debuginfo.o $(BUILDDIR)/debuginfo.dbg.obj: $(addprefix $(SRCDIR)/,debuginfo.h processor.h)
intrinsics.cpp jitlayers.h debug-registry.h intrinsics.h codegen_shared.h cgutils.cpp ccall.cpp abi_*.cpp processor.h builtin_proto.h)
$(BUILDDIR)/debuginfo.o $(BUILDDIR)/debuginfo.dbg.obj: $(addprefix $(SRCDIR)/,debuginfo.h processor.h jitlayers.h debug-registry.h)
$(BUILDDIR)/disasm.o $(BUILDDIR)/disasm.dbg.obj: $(SRCDIR)/debuginfo.h $(SRCDIR)/processor.h
$(BUILDDIR)/dump.o $(BUILDDIR)/dump.dbg.obj: $(addprefix $(SRCDIR)/,common_symbols1.inc common_symbols2.inc builtin_proto.h serialize.h)
$(BUILDDIR)/gc-debug.o $(BUILDDIR)/gc-debug.dbg.obj: $(SRCDIR)/gc.h
$(BUILDDIR)/gc-pages.o $(BUILDDIR)/gc-pages.dbg.obj: $(SRCDIR)/gc.h
$(BUILDDIR)/gc.o $(BUILDDIR)/gc.dbg.obj: $(SRCDIR)/gc.h $(SRCDIR)/gc-alloc-profiler.h
$(BUILDDIR)/init.o $(BUILDDIR)/init.dbg.obj: $(SRCDIR)/builtin_proto.h
$(BUILDDIR)/interpreter.o $(BUILDDIR)/interpreter.dbg.obj: $(SRCDIR)/builtin_proto.h
$(BUILDDIR)/jitlayers.o $(BUILDDIR)/jitlayers.dbg.obj: $(SRCDIR)/jitlayers.h $(SRCDIR)/codegen_shared.h
$(BUILDDIR)/jitlayers.o $(BUILDDIR)/jitlayers.dbg.obj: $(SRCDIR)/jitlayers.h $(SRCDIR)/codegen_shared.h $(SRCDIR)/debug-registry.h
$(BUILDDIR)/jltypes.o $(BUILDDIR)/jltypes.dbg.obj: $(SRCDIR)/builtin_proto.h
$(build_shlibdir)/libllvmcalltest.$(SHLIB_EXT): $(SRCDIR)/codegen_shared.h $(BUILDDIR)/julia_version.h
$(BUILDDIR)/llvm-alloc-helpers.o $(BUILDDIR)/llvm-alloc-helpers.dbg.obj: $(SRCDIR)/codegen_shared.h $(SRCDIR)/llvm-pass-helpers.h $(SRCDIR)/llvm-alloc-helpers.h
Expand Down
3 changes: 0 additions & 3 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8155,8 +8155,6 @@ char jl_using_oprofile_jitevents = 0; // Non-zero if running under OProfile
char jl_using_perf_jitevents = 0;
#endif

void jl_init_debuginfo(void);

extern "C" void jl_init_llvm(void)
{
builtin_func_map =
Expand Down Expand Up @@ -8199,7 +8197,6 @@ extern "C" void jl_init_llvm(void)
jl_default_debug_info_kind = (int) DICompileUnit::DebugEmissionKind::FullDebug;
imaging_mode = jl_options.image_codegen || (jl_generating_output() && !jl_options.incremental);
jl_default_cgparams.generic_context = jl_nothing;
jl_init_debuginfo();

InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
Expand Down
183 changes: 183 additions & 0 deletions src/debug-registry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#include <llvm/ADT/StringMap.h>
#include <llvm/DebugInfo/DIContext.h>
#include <llvm/IR/DataLayout.h>

#include "julia_internal.h"
#include "processor.h"

#include <map>
#include <mutex>
#include <type_traits>

typedef struct {
const llvm::object::ObjectFile *obj;
llvm::DIContext *ctx;
int64_t slide;
} objfileentry_t;


// Central registry for resolving function addresses to `jl_method_instance_t`s and
// originating `ObjectFile`s (for the DWARF debug info).
//
// A global singleton instance is notified by the JIT whenever a new object is emitted,
// and later queried by the various function info APIs. We also use the chance to handle
// some platform-specific unwind info registration (which is unrelated to the query
// functionality).
class JITDebugInfoRegistry
{
public:
template<typename ResourceT>
struct Locked {

template<typename CResourceT>
struct Lock {
std::unique_lock<std::mutex> lock;
CResourceT &resource;

Lock(std::mutex &mutex, CResourceT &resource) JL_NOTSAFEPOINT : lock(mutex), resource(resource) {}
Lock(Lock &&) JL_NOTSAFEPOINT = default;
Lock &operator=(Lock &&) JL_NOTSAFEPOINT = default;

CResourceT &operator*() JL_NOTSAFEPOINT {
return resource;
}

const CResourceT &operator*() const JL_NOTSAFEPOINT {
return resource;
}

CResourceT *operator->() JL_NOTSAFEPOINT {
return &**this;
}

const CResourceT *operator->() const JL_NOTSAFEPOINT {
return &**this;
}

operator const CResourceT &() const JL_NOTSAFEPOINT {
return resource;
}

~Lock() JL_NOTSAFEPOINT = default;
};
private:

mutable std::mutex mutex;
ResourceT resource;
public:
typedef Lock<ResourceT> LockT;
typedef Lock<const ResourceT> ConstLockT;

Locked(ResourceT resource = ResourceT()) JL_NOTSAFEPOINT : mutex(), resource(std::move(resource)) {}

LockT operator*() JL_NOTSAFEPOINT {
return LockT(mutex, resource);
}

ConstLockT operator*() const JL_NOTSAFEPOINT {
return ConstLockT(mutex, resource);
}

~Locked() JL_NOTSAFEPOINT = default;
};

template<typename datatype>
struct jl_pthread_key_t {
static_assert(std::is_trivially_default_constructible<datatype>::value, "Invalid datatype for pthread key!");
static_assert(std::is_trivially_destructible<datatype>::value, "Expected datatype to be trivially destructible!");
static_assert(sizeof(datatype) == sizeof(void*), "Expected datatype to be like a void*!");
pthread_key_t key;

void init() JL_NOTSAFEPOINT {
if (pthread_key_create(&key, NULL))
jl_error("fatal: pthread_key_create failed");
}

operator datatype() JL_NOTSAFEPOINT {
return reinterpret_cast<datatype>(pthread_getspecific(key));
}

jl_pthread_key_t &operator=(datatype val) JL_NOTSAFEPOINT {
pthread_setspecific(key, reinterpret_cast<void*>(val));
return *this;
}

void destroy() JL_NOTSAFEPOINT {
pthread_key_delete(key);
}
};

struct sysimg_info_t {
uint64_t jl_sysimage_base;
jl_sysimg_fptrs_t sysimg_fptrs;
jl_method_instance_t **sysimg_fvars_linfo;
size_t sysimg_fvars_n;
};

struct libc_frames_t {
#if defined(_OS_DARWIN_) && defined(LLVM_SHLIB)
std::atomic<void(*)(void*)> libc_register_frame_{nullptr};
std::atomic<void(*)(void*)> libc_deregister_frame_{nullptr};

void libc_register_frame(const char *Entry) JL_NOTSAFEPOINT;

void libc_deregister_frame(const char *Entry) JL_NOTSAFEPOINT;
#endif
};
private:

struct ObjectInfo {
const llvm::object::ObjectFile *object = nullptr;
size_t SectionSize = 0;
ptrdiff_t slide = 0;
llvm::object::SectionRef Section{};
llvm::DIContext *context = nullptr;
};

template<typename KeyT, typename ValT>
using rev_map = std::map<KeyT, ValT, std::greater<KeyT>>;

typedef rev_map<size_t, ObjectInfo> objectmap_t;
typedef rev_map<uint64_t, objfileentry_t> objfilemap_t;

objectmap_t objectmap{};
rev_map<size_t, std::pair<size_t, jl_method_instance_t *>> linfomap{};

// Maintain a mapping of unrealized function names -> linfo objects
// so that when we see it get emitted, we can add a link back to the linfo
// that it came from (providing name, type signature, file info, etc.)
Locked<llvm::StringMap<jl_code_instance_t*>> codeinst_in_flight{};

Locked<sysimg_info_t> sysimg_info{};

Locked<objfilemap_t> objfilemap{};

static std::string mangle(llvm::StringRef Name, const llvm::DataLayout &DL) JL_NOTSAFEPOINT;

public:

JITDebugInfoRegistry() JL_NOTSAFEPOINT;
~JITDebugInfoRegistry() JL_NOTSAFEPOINT = default;

// Any function that acquires this lock must be either a unmanaged thread
// or in the GC safe region and must NOT allocate anything through the GC
// while holding this lock.
// Certain functions in this file might be called from an unmanaged thread
// and cannot have any interaction with the julia runtime
// They also may be re-entrant, and operating while threads are paused, so we
// separately manage the re-entrant count behavior for safety across platforms
// Note that we cannot safely upgrade read->write
uv_rwlock_t debuginfo_asyncsafe{};
jl_pthread_key_t<uintptr_t> debuginfo_asyncsafe_held{};
libc_frames_t libc_frames{};

void add_code_in_flight(llvm::StringRef name, jl_code_instance_t *codeinst, const llvm::DataLayout &DL) JL_NOTSAFEPOINT;
jl_method_instance_t *lookupLinfo(size_t pointer) JL_NOTSAFEPOINT;
void registerJITObject(const llvm::object::ObjectFile &Object,
std::function<uint64_t(const llvm::StringRef &)> getLoadAddress,
std::function<void*(void*)> lookupWriteAddress) JL_NOTSAFEPOINT;
objectmap_t& getObjectMap() JL_NOTSAFEPOINT;
void set_sysimg_info(sysimg_info_t info) JL_NOTSAFEPOINT;
Locked<sysimg_info_t>::ConstLockT get_sysimg_info() const JL_NOTSAFEPOINT;
Locked<objfilemap_t>::LockT get_objfile_map() JL_NOTSAFEPOINT;
};
Loading

0 comments on commit 2cf85b6

Please sign in to comment.