UserspaceEmulator: Exclude special ranges from RangeAllocator

If we do not mark these ranges as reserved, RangeAllocator might later
give us addresses that overlap these, which then causes an assertion
failure in the SoftMMU.  This behavior led to recurring CI failures, and
sometimes made programs as simple as `/bin/true` fail.

Fixes "Crash 1" reported in #9104
This commit is contained in:
Daniel Bertalan 2021-12-24 22:54:26 +01:00 committed by Idan Horowitz
parent 979f300337
commit 4e1898df99
4 changed files with 20 additions and 3 deletions

View file

@ -34,6 +34,8 @@ namespace UserspaceEmulator {
static constexpr u32 stack_location = 0x10000000;
static constexpr size_t stack_size = 1 * MiB;
static constexpr u32 signal_trampoline_location = 0xb0000000;
static Emulator* s_the;
Emulator& Emulator::the()
@ -95,6 +97,7 @@ Vector<ELF::AuxiliaryValue> Emulator::generate_auxiliary_vector(FlatPtr load_bas
void Emulator::setup_stack(Vector<ELF::AuxiliaryValue> aux_vector)
{
m_range_allocator.reserve_user_range(VirtualAddress(stack_location), stack_size);
auto stack_region = make<SimpleRegion>(stack_location, stack_size);
stack_region->set_stack(true);
m_mmu.add_region(move(stack_region));
@ -183,7 +186,9 @@ bool Emulator::load_elf()
VERIFY(program_header.type() != PT_TLS);
if (program_header.type() == PT_LOAD) {
auto region = make<SimpleRegion>(program_header.vaddr().offset(interpreter_load_offset).get(), program_header.size_in_memory());
auto start_address = program_header.vaddr().offset(interpreter_load_offset);
m_range_allocator.reserve_user_range(start_address, program_header.size_in_memory());
auto region = make<SimpleRegion>(start_address.get(), program_header.size_in_memory());
if (program_header.is_executable() && !program_header.is_writable())
region->set_text(true);
memcpy(region->data(), program_header.raw_data(), program_header.size_in_image());
@ -666,7 +671,8 @@ extern "C" void asm_signal_trampoline_end(void);
void Emulator::setup_signal_trampoline()
{
auto trampoline_region = make<SimpleRegion>(0xb0000000, 4096);
m_range_allocator.reserve_user_range(VirtualAddress(signal_trampoline_location), 4096);
auto trampoline_region = make<SimpleRegion>(signal_trampoline_location, 4096);
u8* trampoline = (u8*)asm_signal_trampoline;
u8* trampoline_end = (u8*)asm_signal_trampoline_end;

View file

@ -1533,7 +1533,9 @@ u32 Emulator::virt$allocate_tls(FlatPtr initial_data, size_t size)
// TODO: This matches what Thread::make_thread_specific_region does. The kernel
// ends up allocating one more page. Figure out if this is intentional.
auto region_size = align_up_to(size, PAGE_SIZE) + PAGE_SIZE;
auto tcb_region = make<SimpleRegion>(0x20000000, region_size);
constexpr auto tls_location = VirtualAddress(0x20000000);
m_range_allocator.reserve_user_range(tls_location, region_size);
auto tcb_region = make<SimpleRegion>(tls_location.get(), region_size);
size_t offset = 0;
while (size - offset > 0) {

View file

@ -181,4 +181,11 @@ void RangeAllocator::deallocate(const Range& range)
}
}
void RangeAllocator::reserve_user_range(VirtualAddress begin, size_t size)
{
auto end = round_up_to_power_of_two(begin.offset(size).get(), PAGE_SIZE);
auto allocated_range = allocate_specific(begin.page_base(), end - begin.page_base().get());
VERIFY(allocated_range.has_value());
}
}

View file

@ -22,6 +22,8 @@ public:
Optional<Range> allocate_randomized(size_t, size_t alignment);
void deallocate(const Range&);
void reserve_user_range(VirtualAddress, size_t);
void dump() const;
bool contains(const Range& range) const { return m_total_range.contains(range); }