@@ -359,3 +359,93 @@ extern void crash() {
359359 }());
360360 }
361361}
362+
363+ TEST_CASE (" Fork and run main()" , " [Fork]" )
364+ {
365+ const auto binary = build_and_load (R"M(
366+ int main() {
367+ return 666;
368+ }
369+ static unsigned value = 12345;
370+ void set_value(int v) {
371+ value = v;
372+ }
373+ int func1() {
374+ return value;
375+ }
376+ int func2() {
377+ return 54321;
378+ }
379+ )M" );
380+
381+ tinykvm::Machine machine { binary, { .max_mem = MAX_MEMORY } };
382+
383+ // We need to create a Linux environment for runtimes to work well
384+ machine.setup_linux ({" fork" }, env);
385+ REQUIRE (machine.banked_memory_pages () == 0 );
386+
387+ // Make machine forkable (with *NO* working memory)
388+ machine.prepare_copy_on_write (4ULL << 20 );
389+ REQUIRE (machine.is_forkable ());
390+ REQUIRE (!machine.is_forked ());
391+ REQUIRE (machine.return_value () == 0 ); // Initial register value
392+
393+ // Run for at most 4 seconds before giving up
394+ machine.run (4 .0f );
395+ REQUIRE (machine.return_value () == 666 ); // Main() return value
396+
397+ // We only gave it 4MB working memory, so lets mmap allocate that and verify
398+ // that if we write more than that, we get an exception thrown
399+ REQUIRE_THROWS ([&] () {
400+ const size_t size = 5ULL << 20 ;
401+ uint64_t addr = machine.mmap_allocate (5ULL << 20 );
402+ char buffer[4096 ];
403+ for (int i = 0 ; i < 4096 ; i++)
404+ buffer[i] = ' a' ;
405+ for (size_t i = 0 ; i < size; i += 4096 )
406+ {
407+ machine.copy_to_guest (addr + i, buffer, 4096 );
408+ }
409+ // Unreachable
410+ abort ();
411+ }());
412+
413+ // There are banked pages now
414+ const auto banked_pages_before = machine.banked_memory_pages ();
415+ REQUIRE (banked_pages_before > 500 );
416+
417+ // Create fork
418+ auto fork1 = tinykvm::Machine { machine, {
419+ .max_mem = MAX_MEMORY, .max_cow_mem = MAX_COWMEM
420+ } };
421+ REQUIRE (fork1.return_value () == 666 ); // Main() return value
422+
423+ fork1.vmcall (" func1" );
424+ REQUIRE (fork1.return_value () == 12345 );
425+
426+ fork1.vmcall (" func2" );
427+ REQUIRE (fork1.return_value () == 54321 );
428+
429+ // This is problematic, but we will try to fix this later
430+ // Forked VM is supposed to diverge from the main VM, regardless of mode
431+ machine.vmcall (" set_value" , 99999 );
432+ fork1.vmcall (" func1" );
433+ REQUIRE (fork1.return_value () == 99999 );
434+
435+ REQUIRE (machine.banked_memory_pages () == banked_pages_before);
436+ REQUIRE (fork1.banked_memory_pages () > 0 );
437+
438+ for (int i = 0 ; i < 20 ; i++)
439+ {
440+ fork1.reset_to (machine, {
441+ .max_mem = MAX_MEMORY,
442+ .max_cow_mem = MAX_COWMEM,
443+ });
444+
445+ fork1.vmcall (" func1" );
446+ REQUIRE (fork1.return_value () == 99999 );
447+
448+ fork1.vmcall (" func2" );
449+ REQUIRE (fork1.return_value () == 54321 );
450+ }
451+ }
0 commit comments