summaryrefslogtreecommitdiff
path: root/DevTools/UserspaceEmulator/MallocTracer.cpp
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-07-15 21:46:50 +0200
committerAndreas Kling <kling@serenityos.org>2020-07-15 23:25:20 +0200
commitc3142923191ead8633f33969e118871d85d19ad9 (patch)
tree8d259e154d1d596250d255cb222395b19b418bce /DevTools/UserspaceEmulator/MallocTracer.cpp
parentd7c87e84f39da880a48ba2e79ab31316adf4436c (diff)
downloadserenity-c3142923191ead8633f33969e118871d85d19ad9.zip
UserspaceEmulator: Catch use-after-frees by tracking malloc/free :^)
This patch introduces a "MallocTracer" to the UserspaceEmulator. If this object is present on the Emulator, it can be notified whenever the emulated program does a malloc() or free(). The notifications come in via a magic instruction sequence that we embed in the LibC malloc() and free() functions. The sequence is: "salc x2, push reg32 x2, pop reg32 x3" The data about the malloc/free operation is in the three pushes. We make sure the sequence is harmless when running natively. Memory accesses on MmapRegion are then audited to see if they fall inside a known-to-be-freed malloc chunk. If so, we complain loud and red in the debugger output. :^) This is very, very cool! :^) It's also a whole lot slower than before, since now we're auditing memory accesses against a new set of metadata. This will need to be optimized (and running in this mode should be opt-in, perhaps even a separate program, etc.)
Diffstat (limited to 'DevTools/UserspaceEmulator/MallocTracer.cpp')
-rw-r--r--DevTools/UserspaceEmulator/MallocTracer.cpp110
1 files changed, 110 insertions, 0 deletions
diff --git a/DevTools/UserspaceEmulator/MallocTracer.cpp b/DevTools/UserspaceEmulator/MallocTracer.cpp
new file mode 100644
index 0000000000..ed24dafee7
--- /dev/null
+++ b/DevTools/UserspaceEmulator/MallocTracer.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "MallocTracer.h"
+#include "Emulator.h"
+#include <AK/LogStream.h>
+
+namespace UserspaceEmulator {
+
+static pid_t s_pid = getpid();
+
+MallocTracer::MallocTracer()
+{
+}
+
+void MallocTracer::target_did_malloc(Badge<SoftCPU>, FlatPtr address, size_t size)
+{
+ if (auto* existing_mallocation = find_mallocation(address)) {
+ ASSERT(existing_mallocation->freed);
+ existing_mallocation->size = size;
+ existing_mallocation->freed = false;
+ return;
+ }
+ m_mallocations.append({ address, size });
+}
+
+void MallocTracer::target_did_free(Badge<SoftCPU>, FlatPtr address)
+{
+ for (auto& mallocation : m_mallocations) {
+ if (mallocation.address == address) {
+ mallocation.freed = true;
+ return;
+ }
+ }
+ ASSERT_NOT_REACHED();
+}
+
+MallocTracer::Mallocation* MallocTracer::find_mallocation(FlatPtr address)
+{
+ for (auto& mallocation : m_mallocations) {
+ if (mallocation.contains(address))
+ return &mallocation;
+ }
+ return nullptr;
+}
+
+void MallocTracer::audit_read(FlatPtr address, size_t size)
+{
+ if (Emulator::the().is_in_malloc_or_free())
+ return;
+
+ auto* mallocation = find_mallocation(address);
+ if (!mallocation)
+ return;
+
+ size_t offset_into_mallocation = address - mallocation->address;
+
+ if (mallocation->freed) {
+ dbgprintf("\n");
+ dbgprintf("==%d== \033[31;1mUAF\033[0m, invalid %zu-byte read at address %p\n", s_pid, size, address);
+ dbgprintf("==%d== Address is %zu bytes into freed block of size %zu\n", s_pid, offset_into_mallocation, mallocation->size);
+ Emulator::the().dump_backtrace();
+ return;
+ }
+}
+
+void MallocTracer::audit_write(FlatPtr address, size_t size)
+{
+ if (Emulator::the().is_in_malloc_or_free())
+ return;
+
+ auto* mallocation = find_mallocation(address);
+ if (!mallocation)
+ return;
+
+ size_t offset_into_mallocation = address - mallocation->address;
+
+ if (mallocation->freed) {
+ dbgprintf("\n");
+ dbgprintf("==%d== \033[31;1mUAF\033[0m, invalid %zu-byte write at address %p\n", s_pid, size, address);
+ dbgprintf("==%d== Address is %zu bytes into freed block of size %zu\n", s_pid, offset_into_mallocation, mallocation->size);
+ Emulator::the().dump_backtrace();
+ return;
+ }
+}
+
+}