-
Notifications
You must be signed in to change notification settings - Fork 20
Description
tinykvm has a forkserver feature, where you can prepare a VM for copy-on-write, and then initialize a forked machine off of that parent.
This works fine in cases where the parent machine has ran and the forks do vmcalls to other functions (such as the Fork and run main() testcase), but trying to use it for a forkserver which repeatedly runs the main entry point instead crashes with a kernel fault.
For example:
TEST_CASE("Fork before main()", "[Fork]")
{
const auto binary = build_and_load(R"M(
#include <stdio.h>
int main() {
printf("Hello World!\n");
return 666;
}
static unsigned value = 12345;
void set_value(int v) {
value = v;
}
int func1() {
return value;
}
int func2() {
return 54321;
}
)M");
tinykvm::Machine machine { binary, {
.max_mem = MAX_MEMORY,
.master_direct_memory_writes = true
} };
// We need to create a Linux environment for runtimes to work well
machine.setup_linux({"fork"}, env);
auto main_regs = machine.registers();
machine.prepare_copy_on_write();
REQUIRE(machine.is_forkable());
REQUIRE(!machine.is_forked());
auto fork1 = tinykvm::Machine { machine, {
.max_mem = MAX_MEMORY, .max_cow_mem = MAX_COWMEM
} };
fork1.run(4.0f);
REQUIRE(fork1.return_value() == 666); // Main() return value
fork1.reset_to(machine, tinykvm::MachineOptions { });
fork1.run(4.0f);
REQUIRE(fork1.return_value() == 666); // Main() return value
}
crashes with
3: *** Page Fault on address 0x0
3: Error code: 0x4 (memory read)
3: * Page not present
3: * CPL=3 Page fault
3: CR0: 0x80040033 CR3: 0x7000000000
3: CR2: 0x0 CR4: 0x350E20
3: RAX: 0x4 RBX: 0x1 RCX: 0xA
3: RDX: 0x4A5F50 RSI: 0x0 RDI: 0x0
3: RIP: 0x21E6 RBP: 0x641AE0 RSP: 0x3FC0
3: SS: 0x0 CS: 0x8 DS: 0x23 FS: 0x0 GS: 0x0
3: FS BASE: 0x0 GS BASE: 0x5050
3: Failing RIP: 0x404224
3: Fail RFLAGS: 0x13046
3: Failing CS: 0x2B
3: Failing RSP: 0x641A80
3: Failing SS: 0x23
3: RIP 0x404224 __libc_setup_tls + 0x64
I suspect what's going on here is something to do with the usercode.asm entrypoint stub? But honestly I have been trying to figure out what's the issue for a while and it's kind of impossible.
I'd like to use tinykvm for a fuzzer, which needs to be able to run a guest program from its ELF entrypoint repeatedly. I can probably work around this by always using a shim fuzzing harness which has a "setup" function which loads the program under test and then a vmcall-able "run_one" instead, but this seems like something that probably should be working.