diff options
author | Andreas Kling <kling@serenityos.org> | 2020-07-15 21:46:50 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-07-15 23:25:20 +0200 |
commit | c3142923191ead8633f33969e118871d85d19ad9 (patch) | |
tree | 8d259e154d1d596250d255cb222395b19b418bce /DevTools/UserspaceEmulator/MallocTracer.cpp | |
parent | d7c87e84f39da880a48ba2e79ab31316adf4436c (diff) | |
download | serenity-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.cpp | 110 |
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; + } +} + +} |