1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 10:17:34 +00:00

DevTools: Move to Userland/DevTools/

This commit is contained in:
Andreas Kling 2021-01-12 12:18:55 +01:00
parent 13d7c09125
commit 4055b03291
125 changed files with 2 additions and 2 deletions

View file

@ -0,0 +1,14 @@
set(SOURCES
Emulator.cpp
MallocTracer.cpp
MmapRegion.cpp
Region.cpp
SharedBufferRegion.cpp
SimpleRegion.cpp
SoftCPU.cpp
SoftMMU.cpp
main.cpp
)
serenity_bin(UserspaceEmulator)
target_link_libraries(UserspaceEmulator LibX86 LibDebug LibCore LibPthread)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,235 @@
/*
* 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.
*/
#pragma once
#include "MallocTracer.h"
#include "Report.h"
#include "SoftCPU.h"
#include "SoftMMU.h"
#include <AK/MappedFile.h>
#include <AK/Types.h>
#include <LibDebug/DebugInfo.h>
#include <LibELF/AuxiliaryVector.h>
#include <LibELF/Image.h>
#include <LibX86/Instruction.h>
#include <signal.h>
#include <sys/types.h>
namespace UserspaceEmulator {
class MallocTracer;
class Emulator {
public:
static Emulator& the();
Emulator(const String& executable_path, const Vector<String>& arguments, const Vector<String>& environment);
bool load_elf();
void dump_backtrace();
void dump_backtrace(const Vector<FlatPtr>&);
Vector<FlatPtr> raw_backtrace();
int exec();
u32 virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3);
SoftMMU& mmu() { return m_mmu; }
MallocTracer* malloc_tracer() { return m_malloc_tracer; }
bool is_in_malloc_or_free() const;
bool is_in_loader_code() const;
void did_receive_signal(int signum) { m_pending_signals |= (1 << signum); }
private:
const String m_executable_path;
const Vector<String> m_arguments;
const Vector<String> m_environment;
SoftMMU m_mmu;
SoftCPU m_cpu;
OwnPtr<MallocTracer> m_malloc_tracer;
void setup_stack(Vector<ELF::AuxiliaryValue>);
Vector<ELF::AuxiliaryValue> generate_auxiliary_vector(FlatPtr load_base, FlatPtr entry_eip, String executable_path, int executable_fd) const;
void register_signal_handlers();
void setup_signal_trampoline();
int virt$fork();
int virt$execve(FlatPtr);
int virt$access(FlatPtr, size_t, int);
int virt$sigaction(int, FlatPtr, FlatPtr);
int virt$sigreturn();
int virt$get_dir_entries(int fd, FlatPtr buffer, ssize_t);
int virt$ioctl(int fd, unsigned, FlatPtr);
int virt$stat(FlatPtr);
int virt$realpath(FlatPtr);
int virt$gethostname(FlatPtr, ssize_t);
int virt$shbuf_create(int size, FlatPtr buffer);
int virt$shbuf_allow_pid(int, pid_t peer_pid);
int virt$shbuf_allow_all(int);
FlatPtr virt$shbuf_get(int shbuf_id, FlatPtr size);
int virt$shbuf_release(int shbuf_id);
int virt$shbuf_seal(int shbuf_id);
int virt$shbuf_set_volatile(int shbuf_id, bool);
int virt$profiling_enable(pid_t);
int virt$profiling_disable(pid_t);
int virt$disown(pid_t);
int virt$purge(int mode);
u32 virt$mmap(u32);
FlatPtr virt$mremap(FlatPtr);
u32 virt$mount(u32);
u32 virt$munmap(FlatPtr address, u32 size);
u32 virt$gettid();
u32 virt$getpid();
u32 virt$unveil(u32);
u32 virt$pledge(u32);
uid_t virt$getuid();
uid_t virt$geteuid();
gid_t virt$getgid();
gid_t virt$getegid();
int virt$setuid(uid_t);
int virt$setgid(gid_t);
u32 virt$read(int, FlatPtr, ssize_t);
u32 virt$write(int, FlatPtr, ssize_t);
u32 virt$mprotect(FlatPtr, size_t, int);
u32 virt$madvise(FlatPtr, size_t, int);
u32 virt$open(u32);
int virt$pipe(FlatPtr pipefd, int flags);
int virt$close(int);
int virt$mkdir(FlatPtr path, size_t path_length, mode_t mode);
int virt$unlink(FlatPtr path, size_t path_length);
int virt$get_process_name(FlatPtr buffer, int size);
int virt$set_mmap_name(FlatPtr);
int virt$gettimeofday(FlatPtr);
int virt$clock_gettime(int, FlatPtr);
int virt$clock_nanosleep(FlatPtr);
int virt$dbgputstr(FlatPtr characters, int length);
int virt$dbgputch(char);
int virt$chmod(FlatPtr, size_t, mode_t);
int virt$fchmod(int, mode_t);
int virt$fchown(int, uid_t, gid_t);
int virt$clock_settime(uint32_t clock_id, FlatPtr user_ts);
int virt$listen(int, int);
int virt$kill(pid_t, int);
int virt$fstat(int, FlatPtr);
u32 virt$fcntl(int fd, int, u32);
int virt$getgroups(ssize_t count, FlatPtr);
int virt$setgroups(ssize_t count, FlatPtr);
int virt$lseek(int fd, off_t offset, int whence);
int virt$socket(int, int, int);
int virt$getsockopt(FlatPtr);
int virt$setsockopt(FlatPtr);
int virt$select(FlatPtr);
int virt$get_stack_bounds(FlatPtr, FlatPtr);
int virt$accept(int sockfd, FlatPtr address, FlatPtr address_length);
int virt$bind(int sockfd, FlatPtr address, socklen_t address_length);
int virt$recvmsg(int sockfd, FlatPtr msg_addr, int flags);
int virt$sendmsg(int sockfd, FlatPtr msg_addr, int flags);
int virt$connect(int sockfd, FlatPtr address, socklen_t address_size);
void virt$exit(int);
ssize_t virt$getrandom(FlatPtr buffer, size_t buffer_size, unsigned int flags);
int virt$chdir(FlatPtr, size_t);
int virt$dup2(int, int);
int virt$getpgrp();
int virt$getpgid(pid_t);
int virt$setpgid(pid_t pid, pid_t pgid);
int virt$ttyname(int fd, FlatPtr buffer, size_t buffer_size);
int virt$getcwd(FlatPtr buffer, size_t buffer_size);
int virt$waitid(FlatPtr);
int virt$getsid(pid_t);
int virt$sched_setparam(int, FlatPtr);
int virt$sched_getparam(pid_t, FlatPtr);
int virt$set_thread_name(pid_t, FlatPtr, size_t);
pid_t virt$setsid();
int virt$watch_file(FlatPtr, size_t);
int virt$readlink(FlatPtr);
u32 virt$allocate_tls(size_t);
int virt$ptsname(int fd, FlatPtr buffer, size_t buffer_size);
int virt$beep();
int virt$ftruncate(int fd, off_t);
mode_t virt$umask(mode_t);
FlatPtr allocate_vm(size_t size, size_t alignment);
bool find_malloc_symbols(const MmapRegion& libc_text);
void dispatch_one_pending_signal();
const MmapRegion* find_text_region(FlatPtr address);
String create_backtrace_line(FlatPtr address);
bool m_shutdown { false };
int m_exit_status { 0 };
FlatPtr m_malloc_symbol_start { 0 };
FlatPtr m_malloc_symbol_end { 0 };
FlatPtr m_realloc_symbol_start { 0 };
FlatPtr m_realloc_symbol_end { 0 };
FlatPtr m_free_symbol_start { 0 };
FlatPtr m_free_symbol_end { 0 };
FlatPtr m_malloc_size_symbol_start { 0 };
FlatPtr m_malloc_size_symbol_end { 0 };
sigset_t m_pending_signals { 0 };
sigset_t m_signal_mask { 0 };
struct SignalHandlerInfo {
FlatPtr handler { 0 };
sigset_t mask { 0 };
int flags { 0 };
};
SignalHandlerInfo m_signal_handler[NSIG];
FlatPtr m_signal_trampoline { 0 };
Optional<FlatPtr> m_loader_text_base;
Optional<size_t> m_loader_text_size;
struct CachedELF {
NonnullRefPtr<MappedFile> mapped_file;
NonnullOwnPtr<Debug::DebugInfo> debug_info;
};
HashMap<String, CachedELF> m_dynamic_library_cache;
};
ALWAYS_INLINE bool Emulator::is_in_malloc_or_free() const
{
return (m_cpu.base_eip() >= m_malloc_symbol_start && m_cpu.base_eip() < m_malloc_symbol_end)
|| (m_cpu.base_eip() >= m_free_symbol_start && m_cpu.base_eip() < m_free_symbol_end)
|| (m_cpu.base_eip() >= m_realloc_symbol_start && m_cpu.base_eip() < m_realloc_symbol_end)
|| (m_cpu.base_eip() >= m_malloc_size_symbol_start && m_cpu.base_eip() < m_malloc_size_symbol_end);
}
ALWAYS_INLINE bool Emulator::is_in_loader_code() const
{
if (!m_loader_text_base.has_value() || !m_loader_text_size.has_value())
return false;
return (m_cpu.base_eip() >= m_loader_text_base.value() && m_cpu.base_eip() < m_loader_text_base.value() + m_loader_text_size.value());
}
}

View file

@ -0,0 +1,379 @@
/*
* 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 "MmapRegion.h"
#include <AK/LogStream.h>
#include <AK/TemporaryChange.h>
#include <mallocdefs.h>
#include <string.h>
//#define REACHABLE_DEBUG
namespace UserspaceEmulator {
MallocTracer::MallocTracer(Emulator& emulator)
: m_emulator(emulator)
{
}
template<typename Callback>
inline void MallocTracer::for_each_mallocation(Callback callback) const
{
m_emulator.mmu().for_each_region([&](auto& region) {
if (is<MmapRegion>(region) && static_cast<const MmapRegion&>(region).is_malloc_block()) {
auto* malloc_data = static_cast<MmapRegion&>(region).malloc_metadata();
for (auto& mallocation : malloc_data->mallocations) {
if (mallocation.used && callback(mallocation) == IterationDecision::Break)
return IterationDecision::Break;
}
}
return IterationDecision::Continue;
});
}
void MallocTracer::target_did_malloc(Badge<SoftCPU>, FlatPtr address, size_t size)
{
if (m_emulator.is_in_loader_code())
return;
auto* region = m_emulator.mmu().find_region({ 0x23, address });
ASSERT(region);
ASSERT(is<MmapRegion>(*region));
auto& mmap_region = static_cast<MmapRegion&>(*region);
// Mark the containing mmap region as a malloc block!
mmap_region.set_malloc(true);
auto* shadow_bits = mmap_region.shadow_data() + address - mmap_region.base();
memset(shadow_bits, 0, size);
if (auto* existing_mallocation = find_mallocation(address)) {
ASSERT(existing_mallocation->freed);
existing_mallocation->size = size;
existing_mallocation->freed = false;
existing_mallocation->malloc_backtrace = m_emulator.raw_backtrace();
existing_mallocation->free_backtrace.clear();
return;
}
MallocRegionMetadata* malloc_data = static_cast<MmapRegion&>(*region).malloc_metadata();
if (!malloc_data) {
auto new_malloc_data = make<MallocRegionMetadata>();
malloc_data = new_malloc_data.ptr();
static_cast<MmapRegion&>(*region).set_malloc_metadata({}, move(new_malloc_data));
malloc_data->address = region->base();
malloc_data->chunk_size = mmap_region.read32(offsetof(CommonHeader, m_size)).value();
bool is_chunked_block = malloc_data->chunk_size <= size_classes[num_size_classes - 1];
if (is_chunked_block)
malloc_data->mallocations.resize((ChunkedBlock::block_size - sizeof(ChunkedBlock)) / malloc_data->chunk_size);
else
malloc_data->mallocations.resize(1);
dbgln("Tracking malloc block @ {:p} with chunk_size={}, chunk_count={}", malloc_data->address, malloc_data->chunk_size, malloc_data->mallocations.size());
}
malloc_data->mallocation_for_address(address) = { address, size, true, false, m_emulator.raw_backtrace(), Vector<FlatPtr>() };
}
ALWAYS_INLINE Mallocation& MallocRegionMetadata::mallocation_for_address(FlatPtr address) const
{
return const_cast<Mallocation&>(this->mallocations[chunk_index_for_address(address)]);
}
ALWAYS_INLINE size_t MallocRegionMetadata::chunk_index_for_address(FlatPtr address) const
{
bool is_chunked_block = chunk_size <= size_classes[num_size_classes - 1];
if (!is_chunked_block) {
// This is a BigAllocationBlock
return 0;
}
auto chunk_offset = address - (this->address + sizeof(ChunkedBlock));
ASSERT(this->chunk_size);
return chunk_offset / this->chunk_size;
}
void MallocTracer::target_did_free(Badge<SoftCPU>, FlatPtr address)
{
if (!address)
return;
if (m_emulator.is_in_loader_code())
return;
if (auto* mallocation = find_mallocation(address)) {
if (mallocation->freed) {
reportln("\n=={}== \033[31;1mDouble free()\033[0m, {:p}", getpid(), address);
reportln("=={}== Address {} has already been passed to free()", getpid(), address);
m_emulator.dump_backtrace();
} else {
mallocation->freed = true;
mallocation->free_backtrace = m_emulator.raw_backtrace();
}
return;
}
reportln("\n=={}== \033[31;1mInvalid free()\033[0m, {:p}", getpid(), address);
reportln("=={}== Address {} has never been returned by malloc()", getpid(), address);
m_emulator.dump_backtrace();
}
void MallocTracer::target_did_realloc(Badge<SoftCPU>, FlatPtr address, size_t size)
{
if (m_emulator.is_in_loader_code())
return;
auto* region = m_emulator.mmu().find_region({ 0x23, address });
ASSERT(region);
ASSERT(is<MmapRegion>(*region));
auto& mmap_region = static_cast<MmapRegion&>(*region);
ASSERT(mmap_region.is_malloc_block());
auto* existing_mallocation = find_mallocation(address);
ASSERT(existing_mallocation);
ASSERT(!existing_mallocation->freed);
size_t old_size = existing_mallocation->size;
auto* shadow_bits = mmap_region.shadow_data() + address - mmap_region.base();
if (size > old_size) {
memset(shadow_bits + old_size, 1, size - old_size);
} else {
memset(shadow_bits + size, 1, old_size - size);
}
existing_mallocation->size = size;
// FIXME: Should we track malloc/realloc backtrace separately perhaps?
existing_mallocation->malloc_backtrace = m_emulator.raw_backtrace();
}
Mallocation* MallocTracer::find_mallocation(FlatPtr address)
{
auto* region = m_emulator.mmu().find_region({ 0x23, address });
if (!region)
return nullptr;
return find_mallocation(*region, address);
}
Mallocation* MallocTracer::find_mallocation_before(FlatPtr address)
{
Mallocation* found_mallocation = nullptr;
for_each_mallocation([&](auto& mallocation) {
if (mallocation.address >= address)
return IterationDecision::Continue;
if (!found_mallocation || (mallocation.address > found_mallocation->address))
found_mallocation = const_cast<Mallocation*>(&mallocation);
return IterationDecision::Continue;
});
return found_mallocation;
}
Mallocation* MallocTracer::find_mallocation_after(FlatPtr address)
{
Mallocation* found_mallocation = nullptr;
for_each_mallocation([&](auto& mallocation) {
if (mallocation.address <= address)
return IterationDecision::Continue;
if (!found_mallocation || (mallocation.address < found_mallocation->address))
found_mallocation = const_cast<Mallocation*>(&mallocation);
return IterationDecision::Continue;
});
return found_mallocation;
}
void MallocTracer::audit_read(const Region& region, FlatPtr address, size_t size)
{
if (!m_auditing_enabled)
return;
if (m_emulator.is_in_malloc_or_free()) {
return;
}
if (m_emulator.is_in_loader_code()) {
return;
}
auto* mallocation = find_mallocation(region, address);
if (!mallocation) {
reportln("\n=={}== \033[31;1mHeap buffer overflow\033[0m, invalid {}-byte read at address {:p}", getpid(), size, address);
m_emulator.dump_backtrace();
auto* mallocation_before = find_mallocation_before(address);
auto* mallocation_after = find_mallocation_after(address);
size_t distance_to_mallocation_before = mallocation_before ? (address - mallocation_before->address - mallocation_before->size) : 0;
size_t distance_to_mallocation_after = mallocation_after ? (mallocation_after->address - address) : 0;
if (mallocation_before && (!mallocation_after || distance_to_mallocation_before < distance_to_mallocation_after)) {
reportln("=={}== Address is {} byte(s) after block of size {}, identity {:p}, allocated at:", getpid(), distance_to_mallocation_before, mallocation_before->size, mallocation_before->address);
m_emulator.dump_backtrace(mallocation_before->malloc_backtrace);
return;
}
if (mallocation_after && (!mallocation_before || distance_to_mallocation_after < distance_to_mallocation_before)) {
reportln("=={}== Address is {} byte(s) before block of size {}, identity {:p}, allocated at:", getpid(), distance_to_mallocation_after, mallocation_after->size, mallocation_after->address);
m_emulator.dump_backtrace(mallocation_after->malloc_backtrace);
}
return;
}
size_t offset_into_mallocation = address - mallocation->address;
if (mallocation->freed) {
reportln("\n=={}== \033[31;1mUse-after-free\033[0m, invalid {}-byte read at address {:p}", getpid(), size, address);
m_emulator.dump_backtrace();
reportln("=={}== Address is {} byte(s) into block of size {}, allocated at:", getpid(), offset_into_mallocation, mallocation->size);
m_emulator.dump_backtrace(mallocation->malloc_backtrace);
reportln("=={}== Later freed at:", getpid());
m_emulator.dump_backtrace(mallocation->free_backtrace);
return;
}
}
void MallocTracer::audit_write(const Region& region, FlatPtr address, size_t size)
{
if (!m_auditing_enabled)
return;
if (m_emulator.is_in_malloc_or_free())
return;
if (m_emulator.is_in_loader_code()) {
return;
}
auto* mallocation = find_mallocation(region, address);
if (!mallocation) {
reportln("\n=={}== \033[31;1mHeap buffer overflow\033[0m, invalid {}-byte write at address {:p}", getpid(), size, address);
m_emulator.dump_backtrace();
auto* mallocation_before = find_mallocation_before(address);
auto* mallocation_after = find_mallocation_after(address);
size_t distance_to_mallocation_before = mallocation_before ? (address - mallocation_before->address - mallocation_before->size) : 0;
size_t distance_to_mallocation_after = mallocation_after ? (mallocation_after->address - address) : 0;
if (mallocation_before && (!mallocation_after || distance_to_mallocation_before < distance_to_mallocation_after)) {
reportln("=={}== Address is {} byte(s) after block of size {}, identity {:p}, allocated at:", getpid(), distance_to_mallocation_before, mallocation_before->size, mallocation_before->address);
m_emulator.dump_backtrace(mallocation_before->malloc_backtrace);
return;
}
if (mallocation_after && (!mallocation_before || distance_to_mallocation_after < distance_to_mallocation_before)) {
reportln("=={}== Address is {} byte(s) before block of size {}, identity {:p}, allocated at:", getpid(), distance_to_mallocation_after, mallocation_after->size, mallocation_after->address);
m_emulator.dump_backtrace(mallocation_after->malloc_backtrace);
}
return;
}
size_t offset_into_mallocation = address - mallocation->address;
if (mallocation->freed) {
reportln("\n=={}== \033[31;1mUse-after-free\033[0m, invalid {}-byte write at address {:p}", getpid(), size, address);
m_emulator.dump_backtrace();
reportln("=={}== Address is {} byte(s) into block of size {}, allocated at:", getpid(), offset_into_mallocation, mallocation->size);
m_emulator.dump_backtrace(mallocation->malloc_backtrace);
reportln("=={}== Later freed at:", getpid());
m_emulator.dump_backtrace(mallocation->free_backtrace);
return;
}
}
bool MallocTracer::is_reachable(const Mallocation& mallocation) const
{
ASSERT(!mallocation.freed);
bool reachable = false;
// 1. Search in active (non-freed) mallocations for pointers to this mallocation
for_each_mallocation([&](auto& other_mallocation) {
if (&mallocation == &other_mallocation)
return IterationDecision::Continue;
if (other_mallocation.freed)
return IterationDecision::Continue;
size_t pointers_in_mallocation = other_mallocation.size / sizeof(u32);
for (size_t i = 0; i < pointers_in_mallocation; ++i) {
auto value = m_emulator.mmu().read32({ 0x23, other_mallocation.address + i * sizeof(u32) });
if (value.value() == mallocation.address && !value.is_uninitialized()) {
#ifdef REACHABLE_DEBUG
reportln("mallocation {:p} is reachable from other mallocation {:p}", mallocation.address, other_mallocation.address);
#endif
reachable = true;
return IterationDecision::Break;
}
}
return IterationDecision::Continue;
});
if (reachable)
return true;
// 2. Search in other memory regions for pointers to this mallocation
m_emulator.mmu().for_each_region([&](auto& region) {
// Skip the stack
if (region.is_stack())
return IterationDecision::Continue;
if (region.is_text())
return IterationDecision::Continue;
if (!region.is_readable())
return IterationDecision::Continue;
// Skip malloc blocks
if (is<MmapRegion>(region) && static_cast<const MmapRegion&>(region).is_malloc_block())
return IterationDecision::Continue;
size_t pointers_in_region = region.size() / sizeof(u32);
for (size_t i = 0; i < pointers_in_region; ++i) {
auto value = region.read32(i * sizeof(u32));
if (value.value() == mallocation.address && !value.is_uninitialized()) {
#ifdef REACHABLE_DEBUG
reportln("mallocation {:p} is reachable from region {:p}-{:p}", mallocation.address, region.base(), region.end() - 1);
#endif
reachable = true;
return IterationDecision::Break;
}
}
return IterationDecision::Continue;
});
return reachable;
}
void MallocTracer::dump_leak_report()
{
TemporaryChange change(m_auditing_enabled, false);
size_t bytes_leaked = 0;
size_t leaks_found = 0;
for_each_mallocation([&](auto& mallocation) {
if (mallocation.freed)
return IterationDecision::Continue;
if (is_reachable(mallocation))
return IterationDecision::Continue;
++leaks_found;
bytes_leaked += mallocation.size;
reportln("\n=={}== \033[31;1mLeak\033[0m, {}-byte allocation at address {:p}", getpid(), mallocation.size, mallocation.address);
m_emulator.dump_backtrace(mallocation.malloc_backtrace);
return IterationDecision::Continue;
});
if (!leaks_found)
reportln("\n=={}== \033[32;1mNo leaks found!\033[0m", getpid());
else
reportln("\n=={}== \033[31;1m{} leak(s) found: {} byte(s) leaked\033[0m", getpid(), leaks_found, bytes_leaked);
}
}

View file

@ -0,0 +1,112 @@
/*
* 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.
*/
#pragma once
#include "MmapRegion.h"
#include "SoftMMU.h"
#include <AK/Badge.h>
#include <AK/HashMap.h>
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <AK/Vector.h>
namespace UserspaceEmulator {
class Emulator;
class SoftCPU;
struct Mallocation {
bool contains(FlatPtr a) const
{
return a >= address && a < (address + size);
}
FlatPtr address { 0 };
size_t size { 0 };
bool used { false };
bool freed { false };
Vector<FlatPtr> malloc_backtrace;
Vector<FlatPtr> free_backtrace;
};
class MallocRegionMetadata {
public:
FlatPtr address { 0 };
size_t chunk_size { 0 };
size_t chunk_index_for_address(FlatPtr) const;
Mallocation& mallocation_for_address(FlatPtr) const;
Vector<Mallocation> mallocations;
};
class MallocTracer {
public:
explicit MallocTracer(Emulator&);
void target_did_malloc(Badge<SoftCPU>, FlatPtr address, size_t);
void target_did_free(Badge<SoftCPU>, FlatPtr address);
void target_did_realloc(Badge<SoftCPU>, FlatPtr address, size_t);
void audit_read(const Region&, FlatPtr address, size_t);
void audit_write(const Region&, FlatPtr address, size_t);
void dump_leak_report();
private:
template<typename Callback>
void for_each_mallocation(Callback callback) const;
Mallocation* find_mallocation(const Region&, FlatPtr);
Mallocation* find_mallocation(FlatPtr);
Mallocation* find_mallocation_before(FlatPtr);
Mallocation* find_mallocation_after(FlatPtr);
bool is_reachable(const Mallocation&) const;
Emulator& m_emulator;
bool m_auditing_enabled { true };
};
ALWAYS_INLINE Mallocation* MallocTracer::find_mallocation(const Region& region, FlatPtr address)
{
if (!is<MmapRegion>(region))
return nullptr;
if (!static_cast<const MmapRegion&>(region).is_malloc_block())
return nullptr;
auto* malloc_data = static_cast<MmapRegion&>(const_cast<Region&>(region)).malloc_metadata();
if (!malloc_data)
return nullptr;
auto& mallocation = malloc_data->mallocation_for_address(address);
if (!mallocation.used)
return nullptr;
ASSERT(mallocation.contains(address));
return &mallocation;
}
}

View file

@ -0,0 +1,224 @@
/*
* 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 "MmapRegion.h"
#include "Emulator.h"
#include <string.h>
#include <sys/mman.h>
namespace UserspaceEmulator {
NonnullOwnPtr<MmapRegion> MmapRegion::create_anonymous(u32 base, u32 size, u32 prot)
{
auto region = adopt_own(*new MmapRegion(base, size, prot));
region->m_file_backed = false;
region->m_data = (u8*)calloc(1, size);
return region;
}
NonnullOwnPtr<MmapRegion> MmapRegion::create_file_backed(u32 base, u32 size, u32 prot, int flags, int fd, off_t offset, String name)
{
auto region = adopt_own(*new MmapRegion(base, size, prot));
region->m_file_backed = true;
if (!name.is_empty()) {
name = String::format("%s (Emulated)", name.characters());
region->m_name = name;
}
region->m_data = (u8*)mmap_with_name(nullptr, size, prot, flags, fd, offset, name.is_empty() ? nullptr : name.characters());
ASSERT(region->m_data != MAP_FAILED);
return region;
}
MmapRegion::MmapRegion(u32 base, u32 size, int prot)
: Region(base, size)
{
set_prot(prot);
m_shadow_data = (u8*)malloc(size);
memset(m_shadow_data, 1, size);
}
MmapRegion::~MmapRegion()
{
free(m_shadow_data);
if (m_file_backed)
munmap(m_data, size());
else
free(m_data);
}
ValueWithShadow<u8> MmapRegion::read8(FlatPtr offset)
{
if (!is_readable()) {
reportln("8-bit read from unreadable MmapRegion @ {:p}", base() + offset);
emulator().dump_backtrace();
TODO();
}
if (is_malloc_block()) {
if (auto* tracer = emulator().malloc_tracer())
tracer->audit_read(*this, base() + offset, 1);
}
ASSERT(offset < size());
return { *reinterpret_cast<const u8*>(m_data + offset), *reinterpret_cast<const u8*>(m_shadow_data + offset) };
}
ValueWithShadow<u16> MmapRegion::read16(u32 offset)
{
if (!is_readable()) {
reportln("16-bit read from unreadable MmapRegion @ {:p}", base() + offset);
emulator().dump_backtrace();
TODO();
}
if (is_malloc_block()) {
if (auto* tracer = emulator().malloc_tracer())
tracer->audit_read(*this, base() + offset, 2);
}
ASSERT(offset + 1 < size());
return { *reinterpret_cast<const u16*>(m_data + offset), *reinterpret_cast<const u16*>(m_shadow_data + offset) };
}
ValueWithShadow<u32> MmapRegion::read32(u32 offset)
{
if (!is_readable()) {
reportln("32-bit read from unreadable MmapRegion @ {:p}", base() + offset);
emulator().dump_backtrace();
TODO();
}
if (is_malloc_block()) {
if (auto* tracer = emulator().malloc_tracer())
tracer->audit_read(*this, base() + offset, 4);
}
ASSERT(offset + 3 < size());
return { *reinterpret_cast<const u32*>(m_data + offset), *reinterpret_cast<const u32*>(m_shadow_data + offset) };
}
ValueWithShadow<u64> MmapRegion::read64(u32 offset)
{
if (!is_readable()) {
reportln("64-bit read from unreadable MmapRegion @ {:p}", base() + offset);
emulator().dump_backtrace();
TODO();
}
if (is_malloc_block()) {
if (auto* tracer = emulator().malloc_tracer())
tracer->audit_read(*this, base() + offset, 8);
}
ASSERT(offset + 7 < size());
return { *reinterpret_cast<const u64*>(m_data + offset), *reinterpret_cast<const u64*>(m_shadow_data + offset) };
}
void MmapRegion::write8(u32 offset, ValueWithShadow<u8> value)
{
if (!is_writable()) {
reportln("8-bit write from unwritable MmapRegion @ {:p}", base() + offset);
emulator().dump_backtrace();
TODO();
}
if (is_malloc_block()) {
if (auto* tracer = emulator().malloc_tracer())
tracer->audit_write(*this, base() + offset, 1);
}
ASSERT(offset < size());
*reinterpret_cast<u8*>(m_data + offset) = value.value();
*reinterpret_cast<u8*>(m_shadow_data + offset) = value.shadow();
}
void MmapRegion::write16(u32 offset, ValueWithShadow<u16> value)
{
if (!is_writable()) {
reportln("16-bit write from unwritable MmapRegion @ {:p}", base() + offset);
emulator().dump_backtrace();
TODO();
}
if (is_malloc_block()) {
if (auto* tracer = emulator().malloc_tracer())
tracer->audit_write(*this, base() + offset, 2);
}
ASSERT(offset + 1 < size());
*reinterpret_cast<u16*>(m_data + offset) = value.value();
*reinterpret_cast<u16*>(m_shadow_data + offset) = value.shadow();
}
void MmapRegion::write32(u32 offset, ValueWithShadow<u32> value)
{
if (!is_writable()) {
reportln("32-bit write from unwritable MmapRegion @ {:p}", base() + offset);
emulator().dump_backtrace();
TODO();
}
if (is_malloc_block()) {
if (auto* tracer = emulator().malloc_tracer())
tracer->audit_write(*this, base() + offset, 4);
}
ASSERT(offset + 3 < size());
ASSERT(m_data != m_shadow_data);
*reinterpret_cast<u32*>(m_data + offset) = value.value();
*reinterpret_cast<u32*>(m_shadow_data + offset) = value.shadow();
}
void MmapRegion::write64(u32 offset, ValueWithShadow<u64> value)
{
if (!is_writable()) {
reportln("64-bit write from unwritable MmapRegion @ {:p}", base() + offset);
emulator().dump_backtrace();
TODO();
}
if (is_malloc_block()) {
if (auto* tracer = emulator().malloc_tracer())
tracer->audit_write(*this, base() + offset, 8);
}
ASSERT(offset + 7 < size());
ASSERT(m_data != m_shadow_data);
*reinterpret_cast<u64*>(m_data + offset) = value.value();
*reinterpret_cast<u64*>(m_shadow_data + offset) = value.shadow();
}
void MmapRegion::set_prot(int prot)
{
set_readable(prot & PROT_READ);
set_writable(prot & PROT_WRITE);
set_executable(prot & PROT_EXEC);
if (m_file_backed) {
mprotect(m_data, size(), prot);
}
}
}

View file

@ -0,0 +1,78 @@
/*
* 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.
*/
#pragma once
#include "SoftMMU.h"
#include <sys/mman.h>
namespace UserspaceEmulator {
class MallocRegionMetadata;
class MallocTracer;
class MmapRegion final : public Region {
public:
static NonnullOwnPtr<MmapRegion> create_anonymous(u32 base, u32 size, u32 prot);
static NonnullOwnPtr<MmapRegion> create_file_backed(u32 base, u32 size, u32 prot, int flags, int fd, off_t offset, String name = {});
virtual ~MmapRegion() override;
virtual ValueWithShadow<u8> read8(u32 offset) override;
virtual ValueWithShadow<u16> read16(u32 offset) override;
virtual ValueWithShadow<u32> read32(u32 offset) override;
virtual ValueWithShadow<u64> read64(u32 offset) override;
virtual void write8(u32 offset, ValueWithShadow<u8>) override;
virtual void write16(u32 offset, ValueWithShadow<u16>) override;
virtual void write32(u32 offset, ValueWithShadow<u32>) override;
virtual void write64(u32 offset, ValueWithShadow<u64>) override;
virtual u8* data() override { return m_data; }
virtual u8* shadow_data() override { return m_shadow_data; }
bool is_malloc_block() const { return m_malloc; }
void set_malloc(bool b) { m_malloc = b; }
void set_prot(int prot);
MallocRegionMetadata* malloc_metadata() { return m_malloc_metadata; }
void set_malloc_metadata(Badge<MallocTracer>, NonnullOwnPtr<MallocRegionMetadata> metadata) { m_malloc_metadata = move(metadata); }
const String& name() const { return m_name; }
private:
MmapRegion(u32 base, u32 size, int prot);
u8* m_data { nullptr };
u8* m_shadow_data { nullptr };
bool m_file_backed { false };
bool m_malloc { false };
OwnPtr<MallocRegionMetadata> m_malloc_metadata;
String m_name;
};
}

View file

@ -0,0 +1,39 @@
/*
* 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 "Region.h"
#include "Emulator.h"
namespace UserspaceEmulator {
Region::Region(u32 base, u32 size)
: m_emulator(Emulator::the())
, m_base(base)
, m_size(size)
{
}
}

View file

@ -0,0 +1,95 @@
/*
* 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.
*/
#pragma once
#include "ValueWithShadow.h"
#include <AK/TypeCasts.h>
#include <AK/Types.h>
namespace UserspaceEmulator {
class Emulator;
class Region {
public:
virtual ~Region() { }
u32 base() const { return m_base; }
u32 size() const { return m_size; }
u32 end() const { return m_base + m_size; }
bool contains(u32 address) const { return address >= base() && address < end(); }
virtual void write8(u32 offset, ValueWithShadow<u8>) = 0;
virtual void write16(u32 offset, ValueWithShadow<u16>) = 0;
virtual void write32(u32 offset, ValueWithShadow<u32>) = 0;
virtual void write64(u32 offset, ValueWithShadow<u64>) = 0;
virtual ValueWithShadow<u8> read8(u32 offset) = 0;
virtual ValueWithShadow<u16> read16(u32 offset) = 0;
virtual ValueWithShadow<u32> read32(u32 offset) = 0;
virtual ValueWithShadow<u64> read64(u32 offset) = 0;
virtual u8* cacheable_ptr([[maybe_unused]] u32 offset) { return nullptr; }
bool is_stack() const { return m_stack; }
void set_stack(bool b) { m_stack = b; }
bool is_text() const { return m_text; }
void set_text(bool b) { m_text = b; }
bool is_readable() const { return m_readable; }
bool is_writable() const { return m_writable; }
bool is_executable() const { return m_executable; }
void set_readable(bool b) { m_readable = b; }
void set_writable(bool b) { m_writable = b; }
void set_executable(bool b) { m_executable = b; }
virtual u8* data() = 0;
virtual u8* shadow_data() = 0;
Emulator& emulator() { return m_emulator; }
const Emulator& emulator() const { return m_emulator; }
protected:
Region(u32 base, u32 size);
private:
Emulator& m_emulator;
u32 m_base { 0 };
u32 m_size { 0 };
bool m_stack { false };
bool m_text { false };
bool m_readable { true };
bool m_writable { true };
bool m_executable { true };
};
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* 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.
*/
#pragma once
#include <AK/LogStream.h>
extern bool g_report_to_debug;
template<typename... Ts>
void reportln(const StringView& format, Ts... args)
{
if (g_report_to_debug)
dbgln(format, args...);
else
warnln(format, args...);
}

View file

@ -0,0 +1,132 @@
/*
* 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 "SharedBufferRegion.h"
#include "Emulator.h"
#include <Kernel/API/Syscall.h>
#include <serenity.h>
#include <string.h>
#include <sys/mman.h>
namespace UserspaceEmulator {
NonnullOwnPtr<SharedBufferRegion> SharedBufferRegion::create_with_shbuf_id(u32 base, u32 size, int shbuf_id, u8* host_data)
{
return adopt_own(*new SharedBufferRegion(base, size, shbuf_id, host_data));
}
SharedBufferRegion::SharedBufferRegion(u32 base, u32 size, int shbuf_id, u8* host_data)
: Region(base, size)
, m_data(host_data)
, m_shbuf_id(shbuf_id)
{
m_shadow_data = (u8*)malloc(size);
memset(m_shadow_data, 1, size);
}
SharedBufferRegion::~SharedBufferRegion()
{
free(m_shadow_data);
}
ValueWithShadow<u8> SharedBufferRegion::read8(FlatPtr offset)
{
ASSERT(offset < size());
return { *reinterpret_cast<const u8*>(m_data + offset), *reinterpret_cast<const u8*>(m_shadow_data + offset) };
}
ValueWithShadow<u16> SharedBufferRegion::read16(u32 offset)
{
ASSERT(offset + 1 < size());
return { *reinterpret_cast<const u16*>(m_data + offset), *reinterpret_cast<const u16*>(m_shadow_data + offset) };
}
ValueWithShadow<u32> SharedBufferRegion::read32(u32 offset)
{
ASSERT(offset + 3 < size());
return { *reinterpret_cast<const u32*>(m_data + offset), *reinterpret_cast<const u32*>(m_shadow_data + offset) };
}
ValueWithShadow<u64> SharedBufferRegion::read64(u32 offset)
{
ASSERT(offset + 7 < size());
return { *reinterpret_cast<const u64*>(m_data + offset), *reinterpret_cast<const u64*>(m_shadow_data + offset) };
}
void SharedBufferRegion::write8(u32 offset, ValueWithShadow<u8> value)
{
ASSERT(offset < size());
*reinterpret_cast<u8*>(m_data + offset) = value.value();
*reinterpret_cast<u8*>(m_shadow_data + offset) = value.shadow();
}
void SharedBufferRegion::write16(u32 offset, ValueWithShadow<u16> value)
{
ASSERT(offset + 1 < size());
*reinterpret_cast<u16*>(m_data + offset) = value.value();
*reinterpret_cast<u16*>(m_shadow_data + offset) = value.shadow();
}
void SharedBufferRegion::write32(u32 offset, ValueWithShadow<u32> value)
{
ASSERT(offset + 3 < size());
*reinterpret_cast<u32*>(m_data + offset) = value.value();
*reinterpret_cast<u32*>(m_shadow_data + offset) = value.shadow();
}
void SharedBufferRegion::write64(u32 offset, ValueWithShadow<u64> value)
{
ASSERT(offset + 7 < size());
*reinterpret_cast<u64*>(m_data + offset) = value.value();
*reinterpret_cast<u64*>(m_shadow_data + offset) = value.shadow();
}
int SharedBufferRegion::allow_all()
{
return syscall(SC_shbuf_allow_all, m_shbuf_id);
}
int SharedBufferRegion::allow_pid(pid_t pid)
{
return syscall(SC_shbuf_allow_pid, m_shbuf_id, pid);
}
int SharedBufferRegion::seal()
{
return syscall(SC_shbuf_seal, m_shbuf_id);
}
int SharedBufferRegion::release()
{
return syscall(SC_shbuf_release, m_shbuf_id);
}
int SharedBufferRegion::set_volatile(bool is_volatile)
{
return syscall(SC_shbuf_set_volatile, m_shbuf_id, is_volatile);
}
}

View file

@ -0,0 +1,68 @@
/*
* 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.
*/
#pragma once
#include "SoftMMU.h"
#include <sys/mman.h>
namespace UserspaceEmulator {
class SharedBufferRegion final : public Region {
public:
static NonnullOwnPtr<SharedBufferRegion> create_with_shbuf_id(u32 base, u32 size, int shbuf_id, u8* shbuf_data);
virtual ~SharedBufferRegion() override;
virtual ValueWithShadow<u8> read8(u32 offset) override;
virtual ValueWithShadow<u16> read16(u32 offset) override;
virtual ValueWithShadow<u32> read32(u32 offset) override;
virtual ValueWithShadow<u64> read64(u32 offset) override;
virtual void write8(u32 offset, ValueWithShadow<u8>) override;
virtual void write16(u32 offset, ValueWithShadow<u16>) override;
virtual void write32(u32 offset, ValueWithShadow<u32>) override;
virtual void write64(u32 offset, ValueWithShadow<u64>) override;
virtual u8* data() override { return m_data; }
virtual u8* shadow_data() override { return m_shadow_data; }
int shbuf_id() const { return m_shbuf_id; }
int allow_all();
int allow_pid(pid_t);
int seal();
int release();
int set_volatile(bool);
private:
SharedBufferRegion(u32 base, u32 size, int shbuf_id, u8* shbuf_data);
u8* m_data { nullptr };
u8* m_shadow_data { nullptr };
int m_shbuf_id { 0 };
};
}

View file

@ -0,0 +1,103 @@
/*
* 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 "SimpleRegion.h"
#include <string.h>
namespace UserspaceEmulator {
SimpleRegion::SimpleRegion(u32 base, u32 size)
: Region(base, size)
{
m_data = (u8*)calloc(1, size);
m_shadow_data = (u8*)malloc(size);
memset(m_shadow_data, 1, size);
}
SimpleRegion::~SimpleRegion()
{
free(m_shadow_data);
free(m_data);
}
ValueWithShadow<u8> SimpleRegion::read8(FlatPtr offset)
{
ASSERT(offset < size());
return { *reinterpret_cast<const u8*>(m_data + offset), *reinterpret_cast<const u8*>(m_shadow_data + offset) };
}
ValueWithShadow<u16> SimpleRegion::read16(u32 offset)
{
ASSERT(offset + 1 < size());
return { *reinterpret_cast<const u16*>(m_data + offset), *reinterpret_cast<const u16*>(m_shadow_data + offset) };
}
ValueWithShadow<u32> SimpleRegion::read32(u32 offset)
{
ASSERT(offset + 3 < size());
return { *reinterpret_cast<const u32*>(m_data + offset), *reinterpret_cast<const u32*>(m_shadow_data + offset) };
}
ValueWithShadow<u64> SimpleRegion::read64(u32 offset)
{
ASSERT(offset + 7 < size());
return { *reinterpret_cast<const u64*>(m_data + offset), *reinterpret_cast<const u64*>(m_shadow_data + offset) };
}
void SimpleRegion::write8(u32 offset, ValueWithShadow<u8> value)
{
ASSERT(offset < size());
*reinterpret_cast<u8*>(m_data + offset) = value.value();
*reinterpret_cast<u8*>(m_shadow_data + offset) = value.shadow();
}
void SimpleRegion::write16(u32 offset, ValueWithShadow<u16> value)
{
ASSERT(offset + 1 < size());
*reinterpret_cast<u16*>(m_data + offset) = value.value();
*reinterpret_cast<u16*>(m_shadow_data + offset) = value.shadow();
}
void SimpleRegion::write32(u32 offset, ValueWithShadow<u32> value)
{
ASSERT(offset + 3 < size());
*reinterpret_cast<u32*>(m_data + offset) = value.value();
*reinterpret_cast<u32*>(m_shadow_data + offset) = value.shadow();
}
void SimpleRegion::write64(u32 offset, ValueWithShadow<u64> value)
{
ASSERT(offset + 7 < size());
*reinterpret_cast<u64*>(m_data + offset) = value.value();
*reinterpret_cast<u64*>(m_shadow_data + offset) = value.shadow();
}
u8* SimpleRegion::cacheable_ptr(u32 offset)
{
return m_data + offset;
}
}

View file

@ -0,0 +1,58 @@
/*
* 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.
*/
#pragma once
#include "SoftMMU.h"
namespace UserspaceEmulator {
class SimpleRegion final : public Region {
public:
SimpleRegion(u32 base, u32 size);
virtual ~SimpleRegion() override;
virtual ValueWithShadow<u8> read8(u32 offset) override;
virtual ValueWithShadow<u16> read16(u32 offset) override;
virtual ValueWithShadow<u32> read32(u32 offset) override;
virtual ValueWithShadow<u64> read64(u32 offset) override;
virtual void write8(u32 offset, ValueWithShadow<u8>) override;
virtual void write16(u32 offset, ValueWithShadow<u16>) override;
virtual void write32(u32 offset, ValueWithShadow<u32>) override;
virtual void write64(u32 offset, ValueWithShadow<u64>) override;
virtual u8* data() override { return m_data; }
virtual u8* shadow_data() override { return m_shadow_data; }
virtual u8* cacheable_ptr(u32 offset) override;
private:
u8* m_data { nullptr };
u8* m_shadow_data { nullptr };
};
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,296 @@
/*
* 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 "SoftMMU.h"
#include "Emulator.h"
#include "MmapRegion.h"
#include "Report.h"
#include "SharedBufferRegion.h"
#include <AK/ByteBuffer.h>
#include <AK/Memory.h>
namespace UserspaceEmulator {
SoftMMU::SoftMMU(Emulator& emulator)
: m_emulator(emulator)
{
}
void SoftMMU::add_region(NonnullOwnPtr<Region> region)
{
ASSERT(!find_region({ 0x23, region->base() }));
// FIXME: More sanity checks pls
if (is<SharedBufferRegion>(*region))
m_shbuf_regions.set(static_cast<SharedBufferRegion*>(region.ptr())->shbuf_id(), region.ptr());
size_t first_page_in_region = region->base() / PAGE_SIZE;
size_t last_page_in_region = (region->base() + region->size() - 1) / PAGE_SIZE;
for (size_t page = first_page_in_region; page <= last_page_in_region; ++page) {
m_page_to_region_map[page] = region.ptr();
}
m_regions.append(move(region));
}
void SoftMMU::remove_region(Region& region)
{
size_t first_page_in_region = region.base() / PAGE_SIZE;
for (size_t i = 0; i < ceil_div(region.size(), PAGE_SIZE); ++i) {
m_page_to_region_map[first_page_in_region + i] = nullptr;
}
if (is<SharedBufferRegion>(region))
m_shbuf_regions.remove(static_cast<SharedBufferRegion&>(region).shbuf_id());
m_regions.remove_first_matching([&](auto& entry) { return entry.ptr() == &region; });
}
void SoftMMU::set_tls_region(NonnullOwnPtr<Region> region)
{
ASSERT(!m_tls_region);
m_tls_region = move(region);
}
ValueWithShadow<u8> SoftMMU::read8(X86::LogicalAddress address)
{
auto* region = find_region(address);
if (!region) {
reportln("SoftMMU::read8: No region for @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
if (!region->is_readable()) {
reportln("SoftMMU::read8: Non-readable region @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
return region->read8(address.offset() - region->base());
}
ValueWithShadow<u16> SoftMMU::read16(X86::LogicalAddress address)
{
auto* region = find_region(address);
if (!region) {
reportln("SoftMMU::read16: No region for @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
if (!region->is_readable()) {
reportln("SoftMMU::read16: Non-readable region @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
return region->read16(address.offset() - region->base());
}
ValueWithShadow<u32> SoftMMU::read32(X86::LogicalAddress address)
{
auto* region = find_region(address);
if (!region) {
reportln("SoftMMU::read32: No region for @ {:04x}:{:p}", address.selector(), address.offset());
m_emulator.dump_backtrace();
TODO();
}
if (!region->is_readable()) {
reportln("SoftMMU::read32: Non-readable region @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
return region->read32(address.offset() - region->base());
}
ValueWithShadow<u64> SoftMMU::read64(X86::LogicalAddress address)
{
auto* region = find_region(address);
if (!region) {
reportln("SoftMMU::read64: No region for @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
if (!region->is_readable()) {
reportln("SoftMMU::read64: Non-readable region @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
return region->read64(address.offset() - region->base());
}
void SoftMMU::write8(X86::LogicalAddress address, ValueWithShadow<u8> value)
{
auto* region = find_region(address);
if (!region) {
reportln("SoftMMU::write8: No region for @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
if (!region->is_writable()) {
reportln("SoftMMU::write8: Non-writable region @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
region->write8(address.offset() - region->base(), value);
}
void SoftMMU::write16(X86::LogicalAddress address, ValueWithShadow<u16> value)
{
auto* region = find_region(address);
if (!region) {
reportln("SoftMMU::write16: No region for @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
if (!region->is_writable()) {
reportln("SoftMMU::write16: Non-writable region @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
region->write16(address.offset() - region->base(), value);
}
void SoftMMU::write32(X86::LogicalAddress address, ValueWithShadow<u32> value)
{
auto* region = find_region(address);
if (!region) {
reportln("SoftMMU::write32: No region for @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
if (!region->is_writable()) {
reportln("SoftMMU::write32: Non-writable region @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
region->write32(address.offset() - region->base(), value);
}
void SoftMMU::write64(X86::LogicalAddress address, ValueWithShadow<u64> value)
{
auto* region = find_region(address);
if (!region) {
reportln("SoftMMU::write64: No region for @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
if (!region->is_writable()) {
reportln("SoftMMU::write64: Non-writable region @ {:p}", address.offset());
m_emulator.dump_backtrace();
TODO();
}
region->write64(address.offset() - region->base(), value);
}
void SoftMMU::copy_to_vm(FlatPtr destination, const void* source, size_t size)
{
// FIXME: We should have a way to preserve the shadow data here as well.
for (size_t i = 0; i < size; ++i)
write8({ 0x23, destination + i }, shadow_wrap_as_initialized(((const u8*)source)[i]));
}
void SoftMMU::copy_from_vm(void* destination, const FlatPtr source, size_t size)
{
// FIXME: We should have a way to preserve the shadow data here as well.
for (size_t i = 0; i < size; ++i)
((u8*)destination)[i] = read8({ 0x23, source + i }).value();
}
ByteBuffer SoftMMU::copy_buffer_from_vm(const FlatPtr source, size_t size)
{
auto buffer = ByteBuffer::create_uninitialized(size);
copy_from_vm(buffer.data(), source, size);
return buffer;
}
SharedBufferRegion* SoftMMU::shbuf_region(int shbuf_id)
{
return (SharedBufferRegion*)m_shbuf_regions.get(shbuf_id).value_or(nullptr);
}
bool SoftMMU::fast_fill_memory8(X86::LogicalAddress address, size_t size, ValueWithShadow<u8> value)
{
if (!size)
return true;
auto* region = find_region(address);
if (!region)
return false;
if (!region->contains(address.offset() + size - 1))
return false;
if (is<MmapRegion>(*region) && static_cast<const MmapRegion&>(*region).is_malloc_block()) {
if (auto* tracer = m_emulator.malloc_tracer()) {
// FIXME: Add a way to audit an entire range of memory instead of looping here!
for (size_t i = 0; i < size; ++i) {
tracer->audit_write(*region, address.offset() + (i * sizeof(u8)), sizeof(u8));
}
}
}
size_t offset_in_region = address.offset() - region->base();
memset(region->data() + offset_in_region, value.value(), size);
memset(region->shadow_data() + offset_in_region, value.shadow(), size);
return true;
}
bool SoftMMU::fast_fill_memory32(X86::LogicalAddress address, size_t count, ValueWithShadow<u32> value)
{
if (!count)
return true;
auto* region = find_region(address);
if (!region)
return false;
if (!region->contains(address.offset() + (count * sizeof(u32)) - 1))
return false;
if (is<MmapRegion>(*region) && static_cast<const MmapRegion&>(*region).is_malloc_block()) {
if (auto* tracer = m_emulator.malloc_tracer()) {
// FIXME: Add a way to audit an entire range of memory instead of looping here!
for (size_t i = 0; i < count; ++i) {
tracer->audit_write(*region, address.offset() + (i * sizeof(u32)), sizeof(u32));
}
}
}
size_t offset_in_region = address.offset() - region->base();
fast_u32_fill((u32*)(region->data() + offset_in_region), value.value(), count);
fast_u32_fill((u32*)(region->shadow_data() + offset_in_region), value.shadow(), count);
return true;
}
}

View file

@ -0,0 +1,102 @@
/*
* 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.
*/
#pragma once
#include "Region.h"
#include "ValueWithShadow.h"
#include <AK/HashMap.h>
#include <AK/NonnullOwnPtrVector.h>
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <LibX86/Instruction.h>
namespace UserspaceEmulator {
class Emulator;
class SharedBufferRegion;
class SoftMMU {
public:
explicit SoftMMU(Emulator&);
ValueWithShadow<u8> read8(X86::LogicalAddress);
ValueWithShadow<u16> read16(X86::LogicalAddress);
ValueWithShadow<u32> read32(X86::LogicalAddress);
ValueWithShadow<u64> read64(X86::LogicalAddress);
void write8(X86::LogicalAddress, ValueWithShadow<u8>);
void write16(X86::LogicalAddress, ValueWithShadow<u16>);
void write32(X86::LogicalAddress, ValueWithShadow<u32>);
void write64(X86::LogicalAddress, ValueWithShadow<u64>);
ALWAYS_INLINE Region* find_region(X86::LogicalAddress address)
{
if (address.selector() == 0x2b)
return m_tls_region.ptr();
size_t page_index = (address.offset() & ~(PAGE_SIZE - 1)) / PAGE_SIZE;
return m_page_to_region_map[page_index];
}
void add_region(NonnullOwnPtr<Region>);
void remove_region(Region&);
void set_tls_region(NonnullOwnPtr<Region>);
bool fast_fill_memory8(X86::LogicalAddress, size_t size, ValueWithShadow<u8>);
bool fast_fill_memory32(X86::LogicalAddress, size_t size, ValueWithShadow<u32>);
void copy_to_vm(FlatPtr destination, const void* source, size_t);
void copy_from_vm(void* destination, const FlatPtr source, size_t);
ByteBuffer copy_buffer_from_vm(const FlatPtr source, size_t);
SharedBufferRegion* shbuf_region(int shbuf_id);
template<typename Callback>
void for_each_region(Callback callback)
{
if (m_tls_region) {
if (callback(*m_tls_region) == IterationDecision::Break)
return;
}
for (auto& region : m_regions) {
if (callback(region) == IterationDecision::Break)
return;
}
}
private:
Emulator& m_emulator;
Region* m_page_to_region_map[786432];
OwnPtr<Region> m_tls_region;
NonnullOwnPtrVector<Region> m_regions;
HashMap<int, Region*> m_shbuf_regions;
};
}

View file

@ -0,0 +1,171 @@
/*
* 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 <AK/Format.h>
#include <AK/Platform.h>
#pragma once
namespace UserspaceEmulator {
template<typename T>
class ValueAndShadowReference;
template<typename T>
class ValueWithShadow {
public:
using ValueType = T;
ValueWithShadow(T value, T shadow)
: m_value(value)
, m_shadow(shadow)
{
}
ValueWithShadow(const ValueAndShadowReference<T>&);
T value() const { return m_value; }
T shadow() const { return m_shadow; }
bool is_uninitialized() const
{
if constexpr (sizeof(T) == 4)
return (m_shadow & 0x01010101) != 0x01010101;
if constexpr (sizeof(T) == 2)
return (m_shadow & 0x0101) != 0x0101;
if constexpr (sizeof(T) == 1)
return (m_shadow & 0x01) != 0x01;
}
void set_initialized()
{
if constexpr (sizeof(T) == 4)
m_shadow = 0x01010101;
if constexpr (sizeof(T) == 2)
m_shadow = 0x0101;
if constexpr (sizeof(T) == 1)
m_shadow = 0x01;
}
private:
T m_value;
T m_shadow;
};
template<typename T>
class ValueAndShadowReference {
public:
using ValueType = T;
ValueAndShadowReference(T& value, T& shadow)
: m_value(value)
, m_shadow(shadow)
{
}
bool is_uninitialized() const
{
if constexpr (sizeof(T) == 4)
return (m_shadow & 0x01010101) != 0x01010101;
if constexpr (sizeof(T) == 2)
return (m_shadow & 0x0101) != 0x0101;
if constexpr (sizeof(T) == 1)
return (m_shadow & 0x01) != 0x01;
}
void operator=(const ValueWithShadow<T>&);
T& value() { return m_value; }
T& shadow() { return m_shadow; }
const T& value() const { return m_value; }
const T& shadow() const { return m_shadow; }
private:
T& m_value;
T& m_shadow;
};
template<typename T>
ALWAYS_INLINE ValueWithShadow<T> shadow_wrap_as_initialized(T value)
{
if constexpr (sizeof(T) == 8)
return { value, 0x01010101'01010101LLU };
if constexpr (sizeof(T) == 4)
return { value, 0x01010101 };
if constexpr (sizeof(T) == 2)
return { value, 0x0101 };
if constexpr (sizeof(T) == 1)
return { value, 0x01 };
}
template<typename T, typename U>
ALWAYS_INLINE ValueWithShadow<T> shadow_wrap_with_taint_from(T value, const U& taint_a)
{
if (taint_a.is_uninitialized())
return { value, 0 };
return shadow_wrap_as_initialized(value);
}
template<typename T, typename U, typename V>
ALWAYS_INLINE ValueWithShadow<T> shadow_wrap_with_taint_from(T value, const U& taint_a, const V& taint_b)
{
if (taint_a.is_uninitialized() || taint_b.is_uninitialized())
return { value, 0 };
return shadow_wrap_as_initialized(value);
}
template<typename T, typename U, typename V, typename X>
ALWAYS_INLINE ValueWithShadow<T> shadow_wrap_with_taint_from(T value, const U& taint_a, const V& taint_b, const X& taint_c)
{
if (taint_a.is_uninitialized() || taint_b.is_uninitialized() || taint_c.is_uninitialized())
return { value, 0 };
return shadow_wrap_as_initialized(value);
}
template<typename T>
inline ValueWithShadow<T>::ValueWithShadow(const ValueAndShadowReference<T>& other)
: m_value(other.value())
, m_shadow(other.shadow())
{
}
template<typename T>
inline void ValueAndShadowReference<T>::operator=(const ValueWithShadow<T>& other)
{
m_value = other.value();
m_shadow = other.shadow();
}
}
template<typename T>
struct AK::Formatter<UserspaceEmulator::ValueWithShadow<T>> : AK::Formatter<T> {
void format(FormatBuilder& builder, UserspaceEmulator::ValueWithShadow<T> value)
{
return Formatter<T>::format(builder, value.value());
}
};

View file

@ -0,0 +1,81 @@
/*
* 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 "Emulator.h"
#include "SoftCPU.h"
#include <AK/Format.h>
#include <AK/LexicalPath.h>
#include <AK/MappedFile.h>
#include <AK/StringBuilder.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DirIterator.h>
#include <LibELF/Image.h>
#include <getopt.h>
#include <pthread.h>
#include <string.h>
bool g_report_to_debug = false;
int main(int argc, char** argv, char** env)
{
Vector<const char*> command;
Core::ArgsParser parser;
parser.add_option(g_report_to_debug, "Write reports to the debug log", "report-to-debug", 0);
parser.add_positional_argument(command, "Command to emulate", "command");
parser.parse(argc, argv);
auto executable_path = Core::find_executable_in_path(command[0]);
Vector<String> arguments;
for (auto arg : command) {
arguments.append(arg);
}
Vector<String> environment;
for (int i = 0; env[i]; ++i) {
environment.append(env[i]);
}
// FIXME: It might be nice to tear down the emulator properly.
auto& emulator = *new UserspaceEmulator::Emulator(executable_path, arguments, environment);
if (!emulator.load_elf())
return 1;
StringBuilder builder;
builder.append("(UE) ");
builder.append(LexicalPath(arguments[0]).basename());
if (set_process_name(builder.string_view().characters_without_null_termination(), builder.string_view().length()) < 0) {
perror("set_process_name");
return 1;
}
int rc = pthread_setname_np(pthread_self(), builder.to_string().characters());
if (rc != 0) {
reportln("pthread_setname_np: {}", strerror(rc));
return 1;
}
return emulator.exec();
}