Use the LLVM disassembler to advance %rip.
authorMatt Mullins <mmullins@mmlx.us>
Mon, 26 May 2014 02:04:51 +0000 (19:04 -0700)
committerMatt Mullins <mmullins@mmlx.us>
Mon, 26 May 2014 02:21:10 +0000 (19:21 -0700)
This contains logic to only permit the LLVM disassembler to read the page
containing the faulting address, so this cannot handle instructions that span
two pages.  The effect of doing so would likely be ++%rip, but that has not
been tested yet.

fuckit.cpp

index 5042857..c726297 100644 (file)
@@ -3,26 +3,64 @@
 #include <unistd.h>
 #include <ucontext.h>
 
+#include <llvm/ADT/StringRef.h>
+#include <llvm/MC/MCDisassembler.h>
+#include <llvm/MC/MCInst.h>
+#include <llvm/Support/raw_ostream.h>
+#include <llvm/Support/MemoryObject.h>
+#include <llvm/Support/StringRefMemoryObject.h>
 #include <llvm/Support/TargetRegistry.h>
 #include <llvm/Support/TargetSelect.h>
 
 static const llvm::Target* target;
 static const llvm::MCDisassembler* disassembler;
+static size_t page_size;
+static size_t page_mask;
 
 static void handle_sigsegv(int signal, siginfo_t *info, void *_context)
 {
         (void)(signal); // it's unused, stfu gcc
 
         struct ucontext *context = (struct ucontext *) _context;
+        char* addr = (char*)context->uc_mcontext.gregs[REG_RIP];
+
         fprintf(stderr, "Caught SIGSEGV for address %p\n", info->si_addr);
-        fprintf(stderr, "Code address: %p\n", (void*)context->uc_mcontext.gregs[REG_RIP]);
+        fprintf(stderr, "Code address: %p\n", addr);
+
+        /* Use only the page containing the faulty instruction, or
+         * else we might ourselves access invalid memory.
+         */
+        char *page_base = (char*)((long)addr & ~page_mask);
+        int page_offset = addr - page_base;
+        llvm::StringRef sr_data(page_base, page_size);
+        llvm::StringRefMemoryObject data(sr_data);
+
+        llvm::MCInst inst;
+        uint64_t inst_len;
 
-        context->uc_mcontext.gregs[REG_RIP]++;
+        switch (disassembler->getInstruction(
+                        inst /* reference */, inst_len /* reference */,
+                        data /* const reference */, page_offset,
+                        llvm::nulls(), llvm::nulls()))
+        {
+        case llvm::MCDisassembler::Success:
+                fprintf(stderr, "Code was %lu bytes\n", inst_len);
+                break;
+        case llvm::MCDisassembler::Fail:
+        case llvm::MCDisassembler::SoftFail:
+                inst_len = 1;
+                break;
+        }
+
+        context->uc_mcontext.gregs[REG_RIP] += inst_len;
 }
 
 __attribute__((constructor))
 static void setup_signals()
 {
+        page_size = sysconf(_SC_PAGESIZE);
+        page_mask = page_size - 1;
+
         fprintf(stderr, "Set up LLVM\n");
         llvm::InitializeAllTargets();
         llvm::InitializeAllTargetMCs();