├── detcore ├── .ignore ├── tests │ ├── lit │ │ ├── utime │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── read_badfd │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── utimes │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── fcntl_dupfd │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── open_null_ptr │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── dup2_existing_fd │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── fcntl_getfd_setfd │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── openat_lowest_fd │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── sched_getaffinity │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run-strict-verify.lit │ │ │ └── main.rs │ │ ├── openat_next_lowest_fd │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── pipe_creates_valid_fds │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── dup_should_create_a_valid_fd │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── hello_world_go │ │ │ ├── hermit-run-strict-verify.lit │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.go │ │ ├── print_race │ │ │ ├── hermit-run-strict-verify.lit │ │ │ ├── hermit-run-strict.lit │ │ │ └── main.rs │ │ ├── close_on_exec │ │ │ └── hermit-run.lit │ │ ├── hello_world_c │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.c │ │ ├── hello_world_rs │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── file_race_openwrite │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── no_close_on_exec │ │ │ └── hermit-run.lit │ │ ├── file_write_race │ │ │ ├── hermit-run-strict.lit │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── child_should_inherit_fds │ │ │ ├── hermit-run.lit │ │ │ └── main.rs │ │ ├── uname.test │ │ ├── cat.test │ │ ├── networking │ │ │ └── hermit-run.lit │ │ ├── hostname.test │ │ ├── rt_sigaction │ │ │ ├── hermit-run.lit │ │ │ └── main.c │ │ ├── exit_code.test │ │ ├── rt_sigprocmask │ │ │ ├── hermit-run.lit │ │ │ └── main.c │ │ ├── tmpfs_preserved.test │ │ ├── tmpfs.test │ │ ├── scheduler_strategies │ │ │ ├── hermit-run-shedulers-strict-verify.sh │ │ │ └── hermit-run-shedulers-chaos-verify.sh │ │ ├── README.md │ │ └── fstat │ │ │ ├── main.rs │ │ │ ├── hermit-run-strict.lit │ │ │ └── hermit-run.lit │ ├── testdata │ │ └── musl-1.2.1.tar.gz │ ├── testutils │ │ └── Cargo.toml │ └── scripts │ │ └── test_build_musl.sh ├── rustfmt.toml ├── src │ ├── config.rs │ ├── syscalls │ │ └── mod.rs │ ├── types.rs │ ├── consts.rs │ ├── detlog.rs │ ├── record_or_replay.rs │ └── util.rs ├── scripts │ ├── syscaller.rs │ ├── get_syscall_support.py │ └── local_validate.sh └── .gitignore ├── tests ├── shell │ ├── date.sh │ ├── race.sh │ ├── devrand.sh │ ├── py_hello.sh │ ├── taskset.sh │ ├── non_strict │ │ └── curl.sh │ └── par_work.sh ├── python │ └── non_strict │ │ ├── rand.py │ │ ├── timed_progress_bar.py │ │ └── hello.py ├── c │ ├── vforkExec.c │ ├── print_memaddrs.c │ ├── just_spin.c │ ├── memoryPress.c │ ├── simple │ │ ├── nanosleep-threads-simple.c │ │ ├── nanosleep-threads-nocrash.c │ │ ├── hello_nostdlib.c │ │ └── racewrite_nostdlib.c │ ├── printf_with_threads.c │ ├── threadExhaustion.c │ ├── hello_alarm.c │ ├── hello_signals.c │ ├── clone.c │ ├── sysinfo.c │ ├── getpid.c │ ├── getCpu.c │ └── uname.c ├── util │ ├── README.md │ ├── ssl_server.py │ ├── simplest_server.py │ ├── curl_to_server_with_ssl.sh │ ├── chaos_stress_wrapper.sh │ └── hermit_analyze_test.sh ├── standalone │ ├── README.md │ ├── test_no_networking_network_bind.sh │ ├── cat_proc_uptime.sh │ ├── stacktrace_signal.sh │ ├── curl_client_only.sh │ ├── network_bind_full.rs │ ├── replay_trace.sh │ ├── network_bind.rs │ ├── validate_race_example_properties.sh │ ├── replay_and_print_stacktraces.sh │ ├── replay_chaos_trace.sh │ └── minimal_hello_backtraces.sh ├── rust │ ├── exit_group.rs │ ├── stack_ptr.rs │ ├── rdtsc.rs │ ├── nanosleep.rs │ ├── print_clock_nanosleep_monotonic_race.rs │ ├── futex_and_print.rs │ ├── pipe_basics.rs │ ├── sched_yield.rs │ ├── futex_timeout.rs │ ├── heap_ptrs.rs │ ├── socketpair.rs │ ├── print_clock_nanosleep_realtime_abs_race.rs │ ├── print_clock_nanosleep_monotonic_abs_race.rs │ ├── print_nanosleep_race.rs │ ├── interrogate_tty.rs │ ├── futex_wait_child.rs │ └── test_utils │ │ └── mod.rs └── chaos │ ├── hello_chaos.rs │ ├── order_violation.c │ ├── nanosleep-threads-nocrash-rust.rs │ ├── cas_sequence.rs │ ├── keyvalue.rs │ └── lock_granularity.c ├── .gitignore ├── rust-toolchain.toml ├── examples ├── devrand.sh ├── date.sh ├── rand.py ├── race.sh └── timed-progress-bar.py ├── docs ├── README.md ├── Developers.md ├── sync_me.sh ├── Users.md └── Developers │ └── Architecture.md ├── rustfmt.toml ├── detcore-model ├── src │ ├── collections │ │ └── mod.rs │ ├── lib.rs │ ├── futex.rs │ ├── fd.rs │ └── pid.rs └── Cargo.toml ├── .github ├── ISSUE_TEMPLATE │ ├── feature.md │ └── bug_report.md └── workflows │ └── ci.yml ├── Cargo.toml ├── common ├── digest │ └── Cargo.toml ├── test-allocator │ ├── Cargo.toml │ └── src │ │ └── test_bin.rs └── edit-distance │ ├── src │ ├── kendall_tau.rs │ └── lib.rs │ └── Cargo.toml ├── hermit-cli ├── src │ ├── bin │ │ └── hermit │ │ │ ├── analyze │ │ │ ├── mod.rs │ │ │ └── consts.rs │ │ │ ├── remove.rs │ │ │ ├── clean.rs │ │ │ ├── logdiff.rs │ │ │ ├── list.rs │ │ │ ├── version.rs │ │ │ ├── container.rs │ │ │ └── record.rs │ ├── consts.rs │ ├── replayer │ │ ├── macros.rs │ │ ├── random.rs │ │ ├── time.rs │ │ └── network.rs │ ├── recorder │ │ └── random.rs │ ├── id.rs │ └── interp.rs └── Cargo.toml ├── hermit-verify ├── src │ ├── common │ │ ├── mod.rs │ │ └── opts.rs │ └── main.rs └── Cargo.toml ├── flaky-tests ├── Cargo.toml ├── README.md ├── hello_race_mini.rs ├── external_flaky_service │ ├── flaky_server.sh │ └── flaky_server_test.sh ├── bind │ ├── bind_random.cpp │ └── bind_same.cpp ├── cas_sequence_easy.rs └── use_configurable_flaky_service.cpp ├── CONTRIBUTING.md └── LICENSE /detcore/.ignore: -------------------------------------------------------------------------------- 1 | .gitignore -------------------------------------------------------------------------------- /tests/shell/date.sh: -------------------------------------------------------------------------------- 1 | ../../examples/date.sh -------------------------------------------------------------------------------- /tests/shell/race.sh: -------------------------------------------------------------------------------- 1 | ../../examples/race.sh -------------------------------------------------------------------------------- /tests/shell/devrand.sh: -------------------------------------------------------------------------------- 1 | ../../examples/devrand.sh -------------------------------------------------------------------------------- /tests/python/non_strict/rand.py: -------------------------------------------------------------------------------- 1 | ../../../examples/rand.py -------------------------------------------------------------------------------- /tests/c/vforkExec.c: -------------------------------------------------------------------------------- 1 | ../../../reverie/tests/c_tests/vforkExec.c -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg 2 | .buckd 3 | .tmp* 4 | target/ 5 | *.preempts 6 | -------------------------------------------------------------------------------- /detcore/tests/lit/utime/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/read_badfd/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/utimes/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/fcntl_dupfd/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/open_null_ptr/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/dup2_existing_fd/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/fcntl_getfd_setfd/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/openat_lowest_fd/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/sched_getaffinity/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /tests/python/non_strict/timed_progress_bar.py: -------------------------------------------------------------------------------- 1 | ../../../examples/timed-progress-bar.py -------------------------------------------------------------------------------- /detcore/tests/lit/openat_next_lowest_fd/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/pipe_creates_valid_fds/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/dup_should_create_a_valid_fd/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_go/hermit-run-strict-verify.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --verify -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/print_race/hermit-run-strict-verify.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --verify -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/sched_getaffinity/hermit-run-strict-verify.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --verify -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/utime/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/utimes/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/close_on_exec/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/fcntl_dupfd/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_c/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me | FileCheck %s 2 | CHECK: Hello world! 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_go/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me | FileCheck %s 2 | CHECK: Hello world! 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_rs/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me | FileCheck %s 2 | CHECK: Hello world! 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/open_null_ptr/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/read_badfd/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/dup2_existing_fd/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/fcntl_getfd_setfd/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/file_race_openwrite/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me | FileCheck %s 2 | CHECK: 1111111111 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/no_close_on_exec/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/openat_lowest_fd/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /tests/util/README.md: -------------------------------------------------------------------------------- 1 | This directory contains bits and pieces used by other tests. 2 | So these are not tests in themselves. 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/file_write_race/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me | FileCheck %s 2 | CHECK: 22222222221111111111 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/openat_next_lowest_fd/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/pipe_creates_valid_fds/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/child_should_inherit_fds/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/lit/dup_should_create_a_valid_fd/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me 2 | -------------------------------------------------------------------------------- /detcore/tests/testdata/musl-1.2.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/hermit/HEAD/detcore/tests/testdata/musl-1.2.1.tar.gz -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # @fb-only: path = "../../third-party-buck/platform010/build/rust/llvm-fb-15" 3 | channel = "nightly" # @oss-only 4 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_go/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me | FileCheck %s 2 | CHECK: Hello world! 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_rs/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me | FileCheck %s 2 | CHECK: Hello world! 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/file_write_race/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me | FileCheck %s 2 | CHECK: {{([12]{20})}} 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/file_race_openwrite/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me | FileCheck %s 2 | CHECK: {{(1*2+|2*1+)}} 3 | -------------------------------------------------------------------------------- /detcore/tests/lit/uname.test: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- uname -nr | FileCheck %s 2 | CHECK: hermetic-container.local 5.2.0 3 | CHECK-EMPTY: 4 | -------------------------------------------------------------------------------- /detcore/tests/lit/cat.test: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- cat /etc/issue | FileCheck %s 2 | RUN: %hermit run -- cat /etc/issue | FileCheck %s 3 | CHECK: {{.+}} 4 | -------------------------------------------------------------------------------- /detcore/tests/lit/networking/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io --analyze-networking -- %me |& FileCheck %s 2 | CHECK: {{.* 0.0.0.0:1299}} 3 | CHECK-NEXT: {{.* :::1299}} 4 | -------------------------------------------------------------------------------- /detcore/tests/lit/print_race/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | // We see a precise alternating behavior with round-robin scheduling: 2 | RUN: %hermit --log=error run -- %me | FileCheck %s 3 | CHECK: {{((ba){200})}} 4 | CHECK-EMPTY: 5 | -------------------------------------------------------------------------------- /detcore/tests/lit/hostname.test: -------------------------------------------------------------------------------- 1 | 2 | # This just to test a preemption-timeout without chaos mode. 3 | RUN: %hermit run --preemption-timeout=10000 -- hostname | FileCheck %s 4 | CHECK: hermetic-container.local 5 | CHECK-EMPTY: 6 | -------------------------------------------------------------------------------- /detcore/tests/lit/rt_sigaction/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me | FileCheck %s 2 | CHECK: SIGHUP is masked 3 | CHECK-NEXT: SIGALRM is masked 4 | CHECK-NEXT: SIGSTKFLT is not masked 5 | CHECK-EMPTY: 6 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_c/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: [ "$(%hermit run --no-sequentialize-threads --no-deterministic-io -- %me)" == "$(echo -ne 'Hello world!\n')" ] 2 | 3 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me | FileCheck %s 4 | CHECK: Hello world! 5 | -------------------------------------------------------------------------------- /detcore/tests/lit/exit_code.test: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- true 2 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- false; [ "$?" == 1 ] 3 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- sh -c "exit 42"; [ "$?" == 42 ] 4 | -------------------------------------------------------------------------------- /examples/devrand.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | exec hexdump /dev/urandom --length 50 9 | -------------------------------------------------------------------------------- /tests/shell/py_hello.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -xeuE 9 | 10 | python3 -c "print('hello')" 11 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Hermit Users Guide & Developers Guide 2 | 3 | This static documentation is generated from docs under source control (fbcode/hermetic_infra/hermit/docs). 4 | Do not modify it directly. 5 | 6 | Please see these separate guide: 7 | - [[Hermit/Docs/Users | User's Guide]] 8 | - [[Hermit/Docs/Developers | Developer's Guide]] 9 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Get help on options with `rustfmt --help=config` 2 | # Please keep these in alphabetical order. 3 | edition = "2024" 4 | format_code_in_doc_comments = true 5 | group_imports = "StdExternalCrate" 6 | imports_granularity = "Item" 7 | merge_derives = false 8 | style_edition = "2024" 9 | use_field_init_shorthand = true 10 | -------------------------------------------------------------------------------- /tests/standalone/README.md: -------------------------------------------------------------------------------- 1 | These tests are not implicitly wrapped with a call to `hermit run`. 2 | Rather, they are standalone shell scripts that invoke hermit themselves. 3 | 4 | By convention, they expect to be passed a path to the hermit binary as 5 | their first argument. Otherwise, they will simply assume `hermit` is 6 | on the path. 7 | -------------------------------------------------------------------------------- /detcore/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Get help on options with `rustfmt --help=config` 2 | # Please keep these in alphabetical order. 3 | edition = "2021" 4 | format_code_in_doc_comments = true 5 | group_imports = "StdExternalCrate" 6 | imports_granularity = "Item" 7 | merge_derives = false 8 | style_edition = "2024" 9 | use_field_init_shorthand = true 10 | -------------------------------------------------------------------------------- /tests/python/non_strict/hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # pyre-unsafe 9 | 10 | print("Hello world") 11 | -------------------------------------------------------------------------------- /detcore-model/src/collections/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | mod replay_cursor; 10 | pub use replay_cursor::ReplayCursor; 11 | -------------------------------------------------------------------------------- /detcore/src/config.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Detcore configuration and widely used types. 10 | pub use detcore_model::config::*; 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Use this template for new feature requests. 4 | title: "RFC: [FEATURE NAME]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Feature Description** 11 | 12 | Please replace this text with a description of what you would like hermit to do, and why. 13 | 14 | **Feature purpose and use cases** 15 | -------------------------------------------------------------------------------- /examples/date.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # Print exact time with nanoseconds so it is always different in consecutive calls: 9 | exec /usr/bin/date +'%Y-%M-%d_%R:%S_%N' 10 | -------------------------------------------------------------------------------- /docs/Developers.md: -------------------------------------------------------------------------------- 1 | # Hermit Developers Guide 2 | 3 | If you're working on hermit in the context of the **Hermetic 4 | Infrastructure team**, first you'll want to make sure you've checked out 5 | the team [Onboarding Guide](https://www.internalfb.com/intern/wiki/T+V/T+V_Internal/T+V_Team_Onboarding/Testing_Infrastructure_and_Frameworks/Hermetic_Infrastructure/). 6 | 7 | See the System [[Architecture]] here. 8 | -------------------------------------------------------------------------------- /detcore/tests/lit/open_null_ptr/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | fn main() { 12 | assert_eq!(unsafe { libc::open(core::ptr::null(), 0, 0) }, -1); 13 | } 14 | -------------------------------------------------------------------------------- /detcore/tests/lit/rt_sigprocmask/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me mask | FileCheck --check-prefix=RUN1 %s 2 | RUN1: SIGHUP is masked 3 | RUN1-NEXT: SIGSTKFLT is not masked 4 | 5 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me block | FileCheck --check-prefix=RUN2 %s 6 | RUN2: SIGHUP is masked 7 | RUN2-NEXT: SIGSTKFLT is not masked 8 | -------------------------------------------------------------------------------- /docs/sync_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -xeuo pipefail 9 | 10 | buck run //scripts/richiev/wiki_sync:wiki_sync -- \ 11 | --wiki-root=Hermit/Docs/ \ 12 | --md-root=./ 13 | -------------------------------------------------------------------------------- /examples/rand.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # pyre-unsafe 9 | 10 | import random 11 | 12 | for _x in range(10): 13 | print(random.randint(1, 101), end=" ") 14 | print("") 15 | -------------------------------------------------------------------------------- /tests/standalone/test_no_networking_network_bind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -eu 9 | 10 | hermit="$1" 11 | 12 | input_program="$2" 13 | 14 | "$hermit" --log=error run "$input_program" 15 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_rs/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %hermit run -- %me | FileCheck %s 10 | // CHECK: Hello world! 11 | 12 | fn main() { 13 | println!("Hello world!"); 14 | } 15 | -------------------------------------------------------------------------------- /examples/race.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | function prnt { 9 | for ((i=0; i<200; i++)); do 10 | echo -n "$1"; 11 | done; 12 | echo; 13 | } 14 | 15 | prnt a & 16 | prnt b 17 | wait 18 | echo 19 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "common/digest", 4 | "common/edit-distance", 5 | "common/test-allocator", 6 | "detcore", 7 | "detcore-model", 8 | "detcore/tests/testutils", 9 | "flaky-tests", 10 | "hermit-cli", 11 | "hermit-verify", 12 | "tests", 13 | ] 14 | resolver = "2" 15 | 16 | [workspace.package] 17 | license = "BSD-3-Clause" 18 | repository = "https://github.com/facebookexperimental/hermit" 19 | -------------------------------------------------------------------------------- /common/digest/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //hermetic_infra/hermit/common:digest 2 | 3 | [package] 4 | name = "digest" 5 | version = "0.0.0" 6 | edition = "2024" 7 | repository = "https://github.com/facebookexperimental/hermit" 8 | license = "BSD-3-Clause" 9 | 10 | [dependencies] 11 | hex = { version = "0.4.3", features = ["alloc"] } 12 | serde = { version = "1.0.219", features = ["derive", "rc"] } 13 | sha2 = "0.10.6" 14 | -------------------------------------------------------------------------------- /common/test-allocator/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //hermetic_infra/hermit/common/test-allocator:[test-allocator,test_bin] 2 | 3 | [package] 4 | name = "test-allocator" 5 | version = "0.0.0" 6 | edition = "2024" 7 | repository = "https://github.com/facebookexperimental/hermit" 8 | license = "BSD-3-Clause" 9 | 10 | [[bin]] 11 | name = "test_bin" 12 | path = "src/test_bin.rs" 13 | 14 | [dependencies] 15 | libc = "0.2.139" 16 | -------------------------------------------------------------------------------- /detcore/tests/lit/tmpfs_preserved.test: -------------------------------------------------------------------------------- 1 | # Check that we can pass in our own temp directory. The temp dir should get 2 | # created automatically. 3 | 4 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io --tmp my_temp -- touch /tmp/{1,2,3}.txt 5 | RUN: ls -aR my_temp | FileCheck %s 6 | CHECK: my_temp 7 | CHECK-NEXT: . 8 | CHECK-NEXT: .. 9 | CHECK-NEXT: 1.txt 10 | CHECK-NEXT: 2.txt 11 | CHECK-NEXT: 3.txt 12 | CHECK-EMPTY: 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Dependencies 20 | run: sudo apt-get install -y libunwind-dev 21 | - name: Build 22 | run: cargo build 23 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_go/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me | FileCheck %s 10 | // CHECK: Hello world! 11 | package main 12 | 13 | import "fmt" 14 | 15 | func main() { 16 | fmt.Println("Hello world!") 17 | } 18 | -------------------------------------------------------------------------------- /detcore/src/syscalls/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! This module just aggregates submodules. 10 | 11 | mod files; 12 | pub mod helpers; 13 | mod io; 14 | mod misc; 15 | mod signal; 16 | mod sysinfo; 17 | mod threads; 18 | mod time; 19 | -------------------------------------------------------------------------------- /detcore/tests/lit/hello_world_c/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me | FileCheck %s 10 | // CHECK: Hello world! 11 | 12 | #include 13 | 14 | int main(int argc, char* argv[]) { 15 | printf("Hello world!\n"); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /tests/shell/taskset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # Report for the bash process, not the taskset child process: 9 | taskset -c -p $$ 10 | # The root process in the container is always pid 3 under hermit currently. 11 | # In this case the bash process will be pid 3. 12 | -------------------------------------------------------------------------------- /hermit-cli/src/bin/hermit/analyze/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! A mode for analyzing a hermit run to detect concurrency bugs. 10 | 11 | mod consts; 12 | mod minimize; 13 | mod phases; 14 | mod rundata; 15 | mod types; 16 | 17 | pub use types::AnalyzeOpts; 18 | -------------------------------------------------------------------------------- /hermit-cli/src/consts.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | /// The name of the JSON metadata file that is saved for each recording. 10 | pub const METADATA_NAME: &str = "metadata.json"; 11 | 12 | /// The name of the root executable. 13 | pub const EXE_NAME: &str = "exe"; 14 | -------------------------------------------------------------------------------- /detcore-model/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Widely-shared type definitions. 10 | 11 | pub mod collections; 12 | pub mod config; 13 | pub mod fd; 14 | pub mod futex; 15 | pub mod pedigree; 16 | pub mod pid; 17 | pub mod schedule; 18 | pub mod summary; 19 | pub mod time; 20 | -------------------------------------------------------------------------------- /common/edit-distance/src/kendall_tau.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | /// Finds the distance between `source` and `target` by looking at only swaps. 10 | pub fn kendall_tau(_source: &[T], _target: &[T], _len: usize) -> usize { 11 | 0 12 | } 13 | 14 | #[cfg(test)] 15 | mod tests {} 16 | -------------------------------------------------------------------------------- /hermit-verify/src/common/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | mod env; 10 | mod opts; 11 | mod test_artifacts; 12 | mod verify; 13 | 14 | pub use env::*; 15 | pub use opts::CommonOpts; 16 | pub use test_artifacts::TestArtifacts; 17 | pub use verify::LogDiffOptions; 18 | pub use verify::Verify; 19 | -------------------------------------------------------------------------------- /tests/rust/exit_group.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! The simplest possible exit_group test. 10 | 11 | fn main() { 12 | let _ = std::thread::spawn(move || { 13 | loop { 14 | print!("") 15 | } 16 | }); 17 | let _ = unsafe { libc::syscall(libc::SYS_exit_group, 0) }; 18 | } 19 | -------------------------------------------------------------------------------- /common/edit-distance/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | mod bubbles; 10 | mod damerau_levenshtein; 11 | mod kendall_tau; 12 | mod needleman_wunsch; 13 | mod schedule; 14 | 15 | pub use bubbles::*; 16 | pub use damerau_levenshtein::damerau_lev; 17 | pub use kendall_tau::kendall_tau; 18 | pub use needleman_wunsch::*; 19 | -------------------------------------------------------------------------------- /detcore/src/types.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Widely-shared type definitions. 10 | 11 | pub use detcore_model::fd::*; 12 | pub use detcore_model::futex::*; 13 | pub use detcore_model::pid::*; 14 | pub use detcore_model::schedule::SigWrapper; 15 | pub use detcore_model::schedule::*; 16 | pub use detcore_model::time::*; 17 | -------------------------------------------------------------------------------- /detcore-model/src/futex.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use crate::pid::DetPid; 10 | 11 | // TODO: use newtype pattern here. 12 | /// Because we don't yet support inter-process sharing of futexes, a futex is uniquely 13 | /// identified by Pid and virtual address (within that Pid's address space). 14 | pub type FutexID = (DetPid, usize); 15 | -------------------------------------------------------------------------------- /tests/rust/stack_ptr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Simple program to print out a pointer to the stack. This helps to test that 10 | //! our address space is deterministic and that the stack is consistent across 11 | //! runs. 12 | 13 | fn main() { 14 | let x = 42; 15 | println!("Stack pointer: {:p}", &x as *const _); 16 | } 17 | -------------------------------------------------------------------------------- /common/edit-distance/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //hermetic_infra/hermit/common:edit-distance 2 | 3 | [package] 4 | name = "edit-distance" 5 | version = "0.0.0" 6 | edition = "2024" 7 | repository = "https://github.com/facebookexperimental/hermit" 8 | license = "BSD-3-Clause" 9 | 10 | [dependencies] 11 | serde = { version = "1.0.219", features = ["derive", "rc"] } 12 | 13 | [dev-dependencies] 14 | rand = { version = "0.8", features = ["small_rng"] } 15 | serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "raw_value", "unbounded_depth"] } 16 | -------------------------------------------------------------------------------- /flaky-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //hermetic_infra/hermit/flaky-tests:[cas_sequence_easy_bin,hello_race,hello_race_mini] 2 | 3 | [package] 4 | name = "hermetic_infra_hermit_flaky-tests" 5 | version = "0.0.0" 6 | edition = "2024" 7 | repository = "https://github.com/facebookexperimental/hermit" 8 | license = "BSD-3-Clause" 9 | 10 | [[bin]] 11 | name = "cas_sequence_easy_bin" 12 | path = "cas_sequence_easy.rs" 13 | 14 | [[bin]] 15 | name = "hello_race" 16 | path = "hello_race.rs" 17 | 18 | [[bin]] 19 | name = "hello_race_mini" 20 | path = "hello_race_mini.rs" 21 | -------------------------------------------------------------------------------- /detcore/tests/lit/tmpfs.test: -------------------------------------------------------------------------------- 1 | RUN: mkdir foo bar 2 | RUN: touch foo/{1,2}.txt bar/{1,2,3}.txt 3 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io --bind foo:/tmp/foo --bind bar:/tmp/bar -- ls -aR /tmp | FileCheck %s 4 | CHECK: /tmp 5 | CHECK-NEXT: . 6 | CHECK-NEXT: .. 7 | CHECK-NEXT: bar 8 | CHECK-NEXT: foo 9 | CHECK-EMPTY: 10 | CHECK-NEXT: /tmp/bar 11 | CHECK-NEXT: . 12 | CHECK-NEXT: .. 13 | CHECK-NEXT: 1.txt 14 | CHECK-NEXT: 2.txt 15 | CHECK-NEXT: 3.txt 16 | CHECK-EMPTY: 17 | CHECK-NEXT: /tmp/foo 18 | CHECK-NEXT: . 19 | CHECK-NEXT: .. 20 | CHECK-NEXT: 1.txt 21 | CHECK-NEXT: 2.txt 22 | CHECK-EMPTY: 23 | -------------------------------------------------------------------------------- /detcore-model/src/fd.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | /// For now we use the definiton of `RawFd` from `std::os`. 10 | // (Workaround: reexporting this type directly triggers a rust-anlazer glitch.) 11 | pub type RawFd = std::os::unix::io::RawFd; 12 | 13 | /// Nondeterministic "physical" inode 14 | pub type RawInode = u64; 15 | 16 | /// Deterministic "virtual" inode. 17 | pub type DetInode = RawInode; 18 | -------------------------------------------------------------------------------- /detcore/scripts/syscaller.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::env; 10 | 11 | fn main() { 12 | let args: Vec = env::args().collect(); 13 | if args.len() < 2 { 14 | panic!("Please provide an integer argument"); 15 | } 16 | 17 | let syscall_num: i64 = args[1].parse().unwrap(); 18 | 19 | unsafe { 20 | libc::syscall(syscall_num, 0, 0, 0, 0, 0, 0); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /detcore/tests/lit/read_badfd/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | // FIXME(T103294612): %hermit run --strict --verify -- %me 12 | 13 | use std::os::fd::BorrowedFd; 14 | 15 | use nix::errno::Errno; 16 | use nix::unistd::read; 17 | 18 | fn main() { 19 | let mut buf = [0u8; 4]; 20 | assert_eq!( 21 | read(unsafe { BorrowedFd::borrow_raw(9999) }, &mut buf), 22 | Err(Errno::EBADF) 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /detcore/tests/lit/pipe_creates_valid_fds/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | use close_err::Closable; 12 | use nix::sys::stat::fstat; 13 | use nix::unistd::pipe; 14 | 15 | fn main() { 16 | let (fd_read, fd_write) = pipe().unwrap(); 17 | assert!(fstat(&fd_read).is_ok()); 18 | assert!(fstat(&fd_write).is_ok()); 19 | fd_write.close().expect("close failed"); 20 | fd_read.close().expect("close failed"); 21 | } 22 | -------------------------------------------------------------------------------- /hermit-cli/src/bin/hermit/analyze/consts.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Constants used by analyze. 10 | 11 | // We identify a run by a root file name, and then append a standard set of suffixes to store the 12 | // associated files for that run. 13 | pub const LOG_EXT: &str = "log"; 14 | pub const PREEMPTS_EXT: &str = "preempts"; 15 | pub const PREEMPTS_EXT_PRESTRIPPED: &str = "preempts_with_times"; 16 | pub const SCHED_EXT: &str = "events"; 17 | -------------------------------------------------------------------------------- /tests/c/print_memaddrs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // Check that the C stack and heap are deterministic. 10 | 11 | #include 12 | #include 13 | 14 | int main(int argc, char** argv) { 15 | int x = argc * 99; 16 | printf("Stack address: %p\n", &x); 17 | void* p1 = malloc(100); 18 | void* p2 = malloc(1000); 19 | void* p3 = malloc(10000); 20 | void* p4 = malloc(100000); 21 | void* p5 = malloc(1000000); 22 | printf("Malloc'd pointers: %p %p %p %p %p\n", p1, p2, p3, p4, p5); 23 | } 24 | -------------------------------------------------------------------------------- /tests/rust/rdtsc.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use core::arch::x86_64::__rdtscp; 10 | use core::arch::x86_64::_rdtsc; 11 | 12 | #[inline(never)] 13 | fn rdtsc() -> u64 { 14 | unsafe { _rdtsc() } 15 | } 16 | 17 | #[allow(unused_mut)] 18 | #[inline(never)] 19 | fn rdtscp() -> u64 { 20 | let mut _aux = 0; 21 | unsafe { __rdtscp(&mut _aux) } 22 | } 23 | 24 | fn main() { 25 | for _ in 0..16 { 26 | println!("rdtsc: {:#x}", rdtsc()); 27 | println!("rdtscp: {:#x}", rdtscp()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/standalone/cat_proc_uptime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -u 9 | 10 | if [ "$*" == "" ]; then 11 | hermit="hermit" 12 | else 13 | hermit="$1" 14 | fi 15 | 16 | "$hermit" run --verify --no-sequentialize-threads --no-deterministic-io -- bash -c 'echo hello; cat /proc/uptime; cat /proc/uptime; cat /proc/uptime' 17 | res=$? 18 | 19 | if [ "$res" == 0 ]; then 20 | echo "Error! Zero exit code where differences expected." 21 | exit 1 22 | else 23 | echo "Differences found, as expected." 24 | exit 0 25 | fi 26 | -------------------------------------------------------------------------------- /examples/timed-progress-bar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # pyre-unsafe 9 | 10 | import datetime 11 | 12 | 13 | def millis(): 14 | return datetime.datetime.now().timestamp() * 1000 15 | 16 | 17 | start = millis() 18 | prev = start 19 | step = 20 20 | numdots = 50 21 | 22 | print("[", end="", flush=True) 23 | for _x in range(numdots): 24 | current = millis() 25 | while current - prev < step: 26 | current = millis() 27 | print(".", end="", flush=True) 28 | prev = current 29 | 30 | print("]", flush=True) 31 | -------------------------------------------------------------------------------- /detcore/tests/lit/scheduler_strategies/hermit-run-shedulers-strict-verify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # RUN: %hermit run --bind /tmp -- %s 9 | # RUN: %hermit run --bind /tmp --sched-heuristic=random --verify -- %s 10 | # RUN: %hermit run --bind /tmp --seed-from=Args --sched-heuristic=random --verify -- %s 11 | # RUN: %hermit run --bind /tmp --seed-from=Args --sched-heuristic=stickyrandom --verify -- %s 12 | 13 | function prnt { 14 | for ((i=0; i<500; i++)); do 15 | echo -n "$1"; 16 | done; 17 | echo; 18 | } 19 | 20 | prnt a & 21 | prnt b 22 | wait 23 | echo 24 | -------------------------------------------------------------------------------- /tests/c/just_spin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | static long x = 0; 13 | 14 | void __attribute__((noinline)) foo() { 15 | x *= 2; 16 | x += 10; 17 | x /= 2; 18 | } 19 | void __attribute__((optnone)) spin() { 20 | for (int i = 0; i < 15 * 1000 * 1000; i++) { 21 | foo(); 22 | } 23 | } 24 | 25 | void* thread(void* vargp) { 26 | spin(); 27 | return NULL; 28 | } 29 | 30 | int main() { 31 | pthread_t thread_id; 32 | pthread_create(&thread_id, NULL, thread, NULL); 33 | spin(); 34 | pthread_join(thread_id, NULL); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /detcore/tests/lit/scheduler_strategies/hermit-run-shedulers-chaos-verify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # RUN: %hermit run --bind /tmp --chaos --verify -- %s 9 | # RUN: %hermit run --bind /tmp --chaos --sched-heuristic=random --verify -- %s 10 | # RUN: %hermit run --bind /tmp --chaos --seed-from=Args --sched-heuristic=random --verify -- %s 11 | # RUN: %hermit run --bind /tmp --chaos --seed-from=Args --sched-heuristic=stickyrandom --verify -- %s 12 | 13 | function prnt { 14 | for ((i=0; i<500; i++)); do 15 | echo -n "$1"; 16 | done; 17 | echo; 18 | } 19 | 20 | prnt a & 21 | prnt b 22 | wait 23 | echo 24 | -------------------------------------------------------------------------------- /detcore/tests/lit/openat_lowest_fd/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | use std::os::fd::IntoRawFd; 12 | 13 | use nix::fcntl::AT_FDCWD; 14 | use nix::fcntl::OFlag; 15 | use nix::fcntl::openat; 16 | use nix::sys::stat::Mode; 17 | 18 | fn main() { 19 | // Make sure stdin is closed. This should get reused by openat. Ignore the 20 | // error in case it wasn't inherited from the parent process. 21 | let _ = nix::unistd::close(0); 22 | 23 | assert_eq!( 24 | openat(AT_FDCWD, "/dev/null", OFlag::O_RDONLY, Mode::S_IRUSR).map(|fd| fd.into_raw_fd()), 25 | Ok(0) 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /tests/c/memoryPress.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /* 14 | This is a very simple test that mallocs lots of space. It should always 15 | pass when the necessary space is available and always fail when it is not 16 | */ 17 | int mem() { 18 | size_t test = 100; 19 | void** pointers[10000]; 20 | for (int i = 0; i < 10000; i++) { 21 | pointers[i] = malloc(test); 22 | } 23 | for (int i = 0; i < 10000; i++) { 24 | free(pointers[i]); 25 | } 26 | return 0; 27 | } 28 | 29 | int main() { 30 | assert(mem() == 0); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /detcore/tests/lit/print_race/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me | FileCheck %s 10 | // CHECK: {{([ab]+)}} 11 | // CHECK-EMPTY: 12 | use std::os::fd::BorrowedFd; 13 | 14 | fn main() { 15 | let child = std::thread::spawn(move || { 16 | for _ in 0..200 { 17 | nix::unistd::write(unsafe { BorrowedFd::borrow_raw(1) }, b"a").unwrap(); 18 | } 19 | }); 20 | for _ in 0..200 { 21 | nix::unistd::write(unsafe { BorrowedFd::borrow_raw(1) }, b"b").unwrap(); 22 | } 23 | child.join().unwrap(); 24 | nix::unistd::write(unsafe { BorrowedFd::borrow_raw(1) }, b"\n").unwrap(); 25 | } 26 | -------------------------------------------------------------------------------- /detcore/tests/lit/fcntl_getfd_setfd/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | use std::os::fd::BorrowedFd; 12 | 13 | use nix::fcntl::FcntlArg; 14 | use nix::fcntl::FdFlag; 15 | use nix::fcntl::fcntl; 16 | use syscalls::Sysno; 17 | use syscalls::syscall; 18 | 19 | fn main() { 20 | // Raw syscall used here because unistd::dup3 is simulated from dup2 21 | let dup_fd = unsafe { syscall!(Sysno::dup3, 2, 255, libc::O_CLOEXEC) }.unwrap() as i32; 22 | assert_eq!(dup_fd, 255); 23 | 24 | let flag = fcntl(unsafe { BorrowedFd::borrow_raw(dup_fd) }, FcntlArg::F_GETFD); 25 | assert_eq!(flag, Ok(FdFlag::FD_CLOEXEC.bits())); 26 | } 27 | -------------------------------------------------------------------------------- /tests/standalone/stacktrace_signal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -u 9 | 10 | if [ "$*" == "" ]; then 11 | hermit="hermit" 12 | else 13 | hermit="$1" 14 | fi 15 | 16 | # Make sure that we can successfully interrupt the guest: 17 | stderr=$(mktemp) 18 | 19 | # Equivalent if we use SIGABRT, SIGINT, etc 20 | SIG=SIGQUIT 21 | "$hermit" --log=info run -u --stacktrace-signal="$SIG" --stacktrace-event=10 --record-preemptions /bin/date 2> "$stderr" 22 | 23 | if grep -s "$SIG" "$stderr"; then 24 | echo "Successfully interrupted the guest with $SIG as expected." 25 | rm "$stderr" 26 | else 27 | echo "ERROR: Did not find $SIG as expected in log!" 28 | exit 1 29 | fi 30 | -------------------------------------------------------------------------------- /detcore/tests/lit/openat_next_lowest_fd/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | use std::os::fd::AsRawFd; 11 | use std::os::fd::IntoRawFd; 12 | 13 | use close_err::Closable; 14 | use nix::fcntl::AT_FDCWD; 15 | use nix::fcntl::OFlag; 16 | use nix::fcntl::openat; 17 | use nix::sys::stat::Mode; 18 | 19 | fn main() { 20 | let (fd3, fd4) = nix::unistd::pipe().unwrap(); 21 | assert_eq!(fd4.as_raw_fd(), fd3.as_raw_fd() + 1); 22 | 23 | let fd3_raw = fd3.as_raw_fd(); 24 | fd3.close().expect("close failed"); 25 | 26 | assert_eq!( 27 | openat(AT_FDCWD, "/dev/null", OFlag::O_RDONLY, Mode::S_IRUSR).map(|f| f.into_raw_fd()), 28 | Ok(fd3_raw) 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /tests/c/simple/nanosleep-threads-simple.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int arr[1] = {10}; 15 | int* lets_break_it = arr; 16 | 17 | void* myThreadFun(void* vargp) { 18 | prctl(PR_SET_NAME, "myThreadFun\0", NULL, NULL, NULL); 19 | sleep(1); 20 | lets_break_it = NULL; 21 | return NULL; 22 | } 23 | 24 | int main() { 25 | prctl(PR_SET_NAME, "mainThread\0", NULL, NULL, NULL); 26 | pthread_t thread_id; 27 | pthread_create(&thread_id, NULL, myThreadFun, NULL); 28 | 29 | sleep(1); 30 | 31 | lets_break_it[0] = 1; 32 | 33 | pthread_join(thread_id, NULL); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /tests/c/printf_with_threads.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | /* 13 | For some reason this triggers registry divergence under trace/replay usecase 14 | in hermit-verify when detlogs are taken for consideration 15 | 16 | The theory behind why "printf" is doing it is unclear but it looks to be 17 | connected with jemalloc behavior as the divergence is happening on jemalloc 18 | thread for 'gettimeofday' syscall 19 | */ 20 | 21 | void* thread1(void* vargp) { 22 | printf("thread 1\n"); 23 | return NULL; 24 | } 25 | 26 | int main() { 27 | pthread_t thread; 28 | pthread_create(&thread, NULL, thread1, NULL); 29 | pthread_join(thread, NULL); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /hermit-cli/src/replayer/macros.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | /// Gets the next event from the event stream. 10 | /// 11 | /// # Example 12 | /// 13 | /// ```ignore 14 | /// let event = next_event!(guest, Read); 15 | /// ``` 16 | /// 17 | /// The return type in this example will be `Result`. 18 | macro_rules! next_event { 19 | ($guest:expr, $event:ident) => { 20 | $guest 21 | .thread_state_mut() 22 | .next_event() 23 | .unwrap() 24 | .event 25 | .map(|e| match e { 26 | $crate::event::SyscallEvent::$event(event) => event, 27 | event => panic!("Got unexpected event: {:?}", event), 28 | }) 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /tests/c/threadExhaustion.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define NUM_THREADS 5 14 | 15 | void* PrintHello(void* threadid) { 16 | printf("Hello"); 17 | int x = 0; 18 | while (x < 1000) { 19 | x++; 20 | } 21 | return NULL; 22 | } 23 | int main() { 24 | pthread_t threads[NUM_THREADS]; 25 | int rc; 26 | int i; 27 | for (i = 0; i < NUM_THREADS; i++) { 28 | rc = pthread_create(&threads[i], NULL, PrintHello, NULL); 29 | if (rc) { 30 | printf("Error:unable to create thread, %d\n", rc); 31 | return -1; 32 | } 33 | } 34 | for (i = 0; i < NUM_THREADS; i++) { 35 | pthread_join(threads[i], NULL); 36 | } 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /hermit-cli/src/replayer/random.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use reverie::Errno; 10 | use reverie::Guest; 11 | use reverie::syscalls::Getrandom; 12 | use reverie::syscalls::MemoryAccess; 13 | 14 | use super::Replayer; 15 | 16 | impl Replayer { 17 | pub(super) async fn handle_getrandom>( 18 | &self, 19 | guest: &mut G, 20 | syscall: Getrandom, 21 | ) -> Result { 22 | let buf = next_event!(guest, Bytes)?; 23 | 24 | assert!(buf.len() <= syscall.buflen()); 25 | 26 | // Write out the buffer. 27 | guest 28 | .memory() 29 | .write_exact(syscall.buf().unwrap(), &buf) 30 | .unwrap(); 31 | Ok(buf.len() as i64) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/rust/nanosleep.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | /*! Make sure sleeping all threads doesn't get us stuck. 10 | 11 | Run with rust-script if you like: 12 | 13 | ```cargo 14 | [dependencies] 15 | libc = "0.2.94" 16 | 17 | [build] 18 | target = "x86_64-unknown-linux-musl" 19 | 20 | ``` 21 | 22 | */ 23 | 24 | fn sleep(milli: i64) { 25 | let tp = libc::timespec { 26 | tv_sec: milli / 1_000, 27 | tv_nsec: milli % 1_000 * 1_000_000, 28 | }; 29 | unsafe { 30 | // issue a raw nanosleep syscall to the OS 31 | libc::nanosleep(&tp, std::ptr::null_mut()) 32 | }; 33 | } 34 | 35 | fn main() { 36 | // 170ms is just a random amount of time that makes the test fast to complete. 37 | sleep(170); 38 | } 39 | -------------------------------------------------------------------------------- /detcore/tests/lit/fcntl_dupfd/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | use std::os::fd::AsRawFd; 12 | use std::os::fd::FromRawFd; 13 | use std::os::fd::OwnedFd; 14 | 15 | use nix::fcntl::FcntlArg; 16 | use nix::fcntl::fcntl; 17 | use nix::sys::stat::fstat; 18 | use nix::unistd::close; 19 | use nix::unistd::dup2_raw; 20 | 21 | fn main() { 22 | let stdin_duped = unsafe { dup2_raw(std::io::stdin(), 100) }.unwrap(); 23 | assert_eq!(stdin_duped.as_raw_fd(), 100); 24 | 25 | let dup_fd = fcntl(std::io::stdin(), FcntlArg::F_DUPFD_CLOEXEC(100)).unwrap(); 26 | assert_eq!(dup_fd, 101); 27 | let dup_fd = unsafe { OwnedFd::from_raw_fd(dup_fd) }; 28 | 29 | assert!(fstat(&dup_fd).is_ok()); 30 | assert!(close(dup_fd).is_ok()); 31 | } 32 | -------------------------------------------------------------------------------- /tests/c/hello_alarm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int flag = 0; 15 | 16 | void sig_handler(int signo) { 17 | flag = 1; 18 | printf("++ Received signal: %d = %s\n", signo, strsignal(signo)); 19 | } 20 | 21 | int main() { 22 | puts("== Start test\n"); 23 | if (signal(SIGALRM, sig_handler) == SIG_ERR) { 24 | printf("\nError: Cannot register signal handler for SIGINT\n"); 25 | return 1; 26 | } 27 | 28 | alarm(1); 29 | sleep(3); 30 | 31 | if (flag) { 32 | puts("== Success: Delivered control to the signal handler and back.\n"); 33 | return 0; 34 | } else { 35 | puts("Error: failed to invoke signal handler\n"); 36 | return 1; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/util/ssl_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | import http.server 9 | import ssl 10 | import sys 11 | 12 | httpd = http.server.HTTPServer(("0.0.0.0", 0), http.server.SimpleHTTPRequestHandler) 13 | httpd.socket = ssl.wrap_socket( 14 | httpd.socket, 15 | certfile="/var/facebook/x509_identities/server.pem", 16 | ca_certs="/var/facebook/rootcanal/ca.pem", 17 | server_side=True, 18 | ) 19 | 20 | requests = -1 21 | if len(sys.argv) > 1: 22 | requests = int(sys.argv[1]) 23 | sys.stderr.write("[server] Answering only " + str(requests) + " requests.\n") 24 | 25 | 26 | print(f"{httpd.server_name}:{httpd.server_port}", flush=True) 27 | if requests > 0: 28 | for _i in range(0, requests): 29 | httpd.handle_request() 30 | else: 31 | httpd.serve_forever() 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | Indicate any of these common scenarios that apply: 14 | 15 | - [ ] a program hangs under hermit 16 | - [ ] hermit panics internally 17 | - [ ] hermit runs the program but divergence (nondeterminism) occurs 18 | 19 | **To Reproduce** 20 | Minimal input to reproduce the behavior. 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Environment** 26 | 27 | - [ ] Linux kernel version (`uname -a`): 28 | - [ ] CPU version (`/proce/cpuinfo`): 29 | - [ ] Linux distro flavor (`/etc/issue`, `/etc/redhat-release`): 30 | 31 | **Additional context** 32 | Attach the logs to this issue as a text file generated by `hermit --log=trace --log-file=FOO run`. 33 | 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /detcore/src/consts.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Constant values. 10 | 11 | #![allow(unused)] 12 | 13 | use reverie::Pid; 14 | 15 | use crate::types::DetInode; 16 | use crate::types::DetPid; 17 | use crate::types::DetTid; 18 | 19 | /// A separate offset for special devices, including file descriptors 0,1,2 20 | /// stdin/stdout/stderr. 21 | pub static DET_SPECIAL_INODE_OFFSET: DetInode = 1000; 22 | 23 | /// The starting point for deterministic inodes *other* than those addressed by 24 | /// `DET_SPECIAL_INODE_OFFSET`. 25 | pub static DET_INODE_OFFSET: DetInode = 9000; 26 | 27 | pub const DEFAULT_HOSTNAME: &str = "hermetic-container.local"; 28 | 29 | /// A convention of how we set up our PID namespace leaves us with a starting pid of 3. 30 | pub const ROOT_DETPID: DetPid = DetPid::from_raw(3); 31 | -------------------------------------------------------------------------------- /tests/c/hello_signals.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int flag = 0; 15 | 16 | void sig_handler(int signo) { 17 | flag = 1; 18 | printf("++ Received signal: %d = %s\n", signo, strsignal(signo)); 19 | } 20 | 21 | int main() { 22 | printf("== Start test\n"); 23 | if (signal(SIGILL, sig_handler) == SIG_ERR) { 24 | printf("\nError: Cannot register signal handler for SIGINT\n"); 25 | return 1; 26 | } 27 | pthread_kill(pthread_self(), SIGILL); 28 | if (flag) { 29 | printf( 30 | "== Success: Synchronously delivered control to the signal handler and back.\n"); 31 | return 0; 32 | } else { 33 | printf("Error: failed to invoke signal handler\n"); 34 | return 1; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /detcore/tests/lit/README.md: -------------------------------------------------------------------------------- 1 | # Lit Tests 2 | 3 | This directory contains integration tests where a specific (or patterned) 4 | stdout is expected. This is good for testing that a program's output is 5 | deterministic. 6 | 7 | These tests are run through LLVM's [`lit`][] tool. Please reference the 8 | [`FileCheck`][] documentation to understand the directives that can be used 9 | for checking on the output of tests. 10 | 11 | [`lit`]: https://llvm.org/docs/CommandGuide/lit.html 12 | [`FileCheck`]: https://llvm.org/docs/CommandGuide/FileCheck.html 13 | 14 | ## Test discovery 15 | 16 | Buck will automatically discover new tests. Just add a new file and the test 17 | will get compiled and executed automatically. 18 | 19 | You can also use `generate-test.py` in the same directory as this README to 20 | easily generate a new lit test: 21 | 22 | ./generate-test.py foobar.rs 23 | 24 | This will generate a test called `foobar` with a `main.rs`. 25 | 26 | ## Running the tests 27 | 28 | buck test //hermetic_infra/detcore/tests/lit/... 29 | -------------------------------------------------------------------------------- /tests/util/simplest_server.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # All rights reserved. 3 | # 4 | # This source code is licensed under the BSD-style license found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | import http.server 8 | import socketserver 9 | import sys 10 | from http import HTTPStatus 11 | 12 | 13 | class Hello(http.server.SimpleHTTPRequestHandler): 14 | def do_GET(self): 15 | self.send_response(HTTPStatus.OK) 16 | self.end_headers() 17 | self.wfile.write(b"Hello world\n") 18 | 19 | 20 | requests = -1 21 | if len(sys.argv) > 1: 22 | requests = int(sys.argv[1]) 23 | sys.stderr.write("[server] Answering only " + str(requests) + " requests.\n") 24 | 25 | with socketserver.TCPServer(("", 0), Hello) as server: 26 | address, port = server.server_address 27 | print(f"{address}:{port}", flush=True) 28 | if requests > 0: 29 | for _i in range(0, requests): 30 | server.handle_request() 31 | else: 32 | server.serve_forever() 33 | -------------------------------------------------------------------------------- /tests/chaos/hello_chaos.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // This simply tests that two schedules are possible with two threads and no dummy work. 10 | 11 | use std::sync::Arc; 12 | use std::sync::atomic::AtomicUsize; 13 | use std::sync::atomic::Ordering::SeqCst; 14 | 15 | fn main() { 16 | let d = Arc::new(AtomicUsize::new(0)); 17 | let d1 = Arc::clone(&d); 18 | let h1 = std::thread::spawn(move || { 19 | d1.store(1, SeqCst); 20 | }); 21 | d.store(2, SeqCst); 22 | h1.join().unwrap(); 23 | 24 | let val = Arc::try_unwrap(d).unwrap().into_inner(); 25 | println!("Final value: {}", val); 26 | if val == 1 { 27 | println!("Parent went first, arbitrarily counting that as an error."); 28 | std::process::exit(1); 29 | } else { 30 | println!("Child went first, counting that as success"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /detcore/tests/testutils/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //hermetic_infra/hermit/detcore:detcore-testutils 2 | 3 | [package] 4 | name = "detcore-testutils" 5 | version = "0.0.0" 6 | edition = "2024" 7 | repository = "https://github.com/facebookexperimental/hermit" 8 | license = "BSD-3-Clause" 9 | 10 | [dependencies] 11 | detcore = { version = "0.0.0", path = "../.." } 12 | lazy_static = "1.5" 13 | pretty_assertions = { version = "1.2", features = ["alloc"], default-features = false } 14 | reverie = { version = "0.1.0", git = "https://github.com/facebookexperimental/reverie.git", branch = "main" } 15 | reverie-ptrace = { version = "0.1.0", git = "https://github.com/facebookexperimental/reverie.git", branch = "main" } 16 | test-allocator = { version = "0.0.0", path = "../../../common/test-allocator" } 17 | tokio = { version = "1.47.1", features = ["full", "test-util", "tracing"] } 18 | tracing = { version = "0.1.41", features = ["attributes", "valuable"] } 19 | tracing-subscriber = { version = "0.3.20", features = ["chrono", "env-filter", "json", "local-time", "parking_lot", "registry"] } 20 | -------------------------------------------------------------------------------- /tests/rust/print_clock_nanosleep_monotonic_race.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Race a (parent) thread sleeping, with one spinning/printing. 10 | mod test_utils; 11 | 12 | use crate::test_utils::sleep_race; 13 | 14 | fn run_test() -> u64 { 15 | sleep_race(|| { 16 | unsafe { 17 | let tp = libc::timespec { 18 | tv_sec: 0, 19 | tv_nsec: 100_000_000, // 100ms is just a random amount of time that makes the test fast to complete. 20 | }; 21 | // issue a raw clock_nanosleep syscall to the OS 22 | libc::clock_nanosleep(libc::CLOCK_MONOTONIC, 0, &tp, std::ptr::null_mut()) 23 | }; 24 | }) 25 | } 26 | 27 | /// Raw execution exposes nondeterminism with this race. 28 | fn main() { 29 | let final_count = run_test(); 30 | assert!(final_count > 0); // Safe bet with a 100ms sleep. 31 | } 32 | -------------------------------------------------------------------------------- /tests/rust/futex_and_print.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::sync::Arc; 10 | use std::sync::atomic::AtomicI64; 11 | use std::sync::atomic::Ordering; 12 | 13 | const TOTAL: i64 = 4; 14 | 15 | #[inline(never)] 16 | fn noopy(counter: &Arc) { 17 | counter.fetch_add(1, Ordering::SeqCst); 18 | counter.fetch_add(-1, Ordering::SeqCst); 19 | } 20 | 21 | fn main() { 22 | let counter = Arc::new(AtomicI64::new(0)); 23 | for _ix in 0..TOTAL { 24 | let counter = counter.clone(); 25 | let _ = std::thread::spawn(move || { 26 | println!("Child thread starting.."); 27 | counter.fetch_add(1, Ordering::SeqCst); 28 | }); 29 | } 30 | while counter.load(Ordering::SeqCst) < TOTAL { 31 | noopy(&counter); 32 | } 33 | println!("Done."); 34 | let _ = unsafe { libc::syscall(libc::SYS_exit_group, 0) }; 35 | } 36 | -------------------------------------------------------------------------------- /tests/rust/pipe_basics.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::str; 10 | 11 | use close_err::Closable; 12 | use nix::unistd; 13 | 14 | fn main() { 15 | let (fdread, fdwrite) = unistd::pipe().unwrap(); 16 | let handle = std::thread::spawn(move || { 17 | let mut buf: [u8; 14] = [0; 14]; 18 | for _ in 10..20 { 19 | assert_eq!(unistd::read(&fdread, &mut buf), Ok(14)); 20 | println!("Child received message: {}", str::from_utf8(&buf).unwrap()); 21 | } 22 | fdread.close().expect("close failed"); 23 | }); 24 | 25 | for ix in 10..20 { 26 | println!("Parent writing to pipe.."); 27 | let msg = format!("hello world {}", ix); 28 | assert_eq!(unistd::write(&fdwrite, msg.as_bytes()), Ok(14)); 29 | } 30 | fdwrite.close().expect("close failed"); 31 | 32 | println!("Joining child.."); 33 | handle.join().unwrap(); 34 | } 35 | -------------------------------------------------------------------------------- /hermit-cli/src/recorder/random.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use reverie::Errno; 10 | use reverie::Guest; 11 | use reverie::syscalls::Getrandom; 12 | use reverie::syscalls::MemoryAccess; 13 | 14 | use super::Recorder; 15 | use crate::event::SyscallEvent; 16 | 17 | impl Recorder { 18 | pub(super) async fn handle_getrandom>( 19 | &self, 20 | guest: &mut G, 21 | syscall: Getrandom, 22 | ) -> Result { 23 | let result = guest.inject(syscall).await; 24 | 25 | self.record_event( 26 | guest, 27 | result.and_then(|length| { 28 | let mut buf = vec![0; length as usize]; 29 | let addr = syscall.buf().ok_or(Errno::EFAULT)?; 30 | guest.memory().read_exact(addr, &mut buf)?; 31 | Ok(SyscallEvent::Bytes(buf)) 32 | }), 33 | ); 34 | 35 | result 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/rust/sched_yield.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Parent thread loops sched_yield and child thread tries to exit_group. 10 | 11 | fn main() { 12 | if matches!(std::env::var("HERMIT_MODE"), Ok(x) if x == "chaos" || x == "chaosreplay" || x == "tracereplay") 13 | { 14 | // This test is prone to deadlocking in the current chaos mode because 15 | // 1. There are no branches to cause preemption points. 16 | // 2. Timeouts are reset by syscalls. 17 | // TODO(T100400409) enable this test by (1) adding branches when (2) is fixed 18 | eprintln!("Skipping test in unsupported mode."); 19 | return; 20 | } 21 | let _ = std::thread::spawn(move || { 22 | loop { 23 | let _ = unsafe { libc::syscall(libc::SYS_exit_group, 0) }; 24 | } 25 | }); 26 | loop { 27 | let _ = unsafe { libc::syscall(libc::SYS_sched_yield, 0) }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /flaky-tests/README.md: -------------------------------------------------------------------------------- 1 | # Flaky-tests - patterns that are known to cause test flakiness 2 | 3 | Known flaky tests patterns. These patterns are derived from fbcode real 4 | tests, ideally the size of the folder should grow, while the flaky tests 5 | in fbcode should shrink. 6 | 7 | ## Bind arbitrary/random number 8 | The bind(2) syscall allow specify a port number, however, using arbitrary or 9 | random number could cause bind fail with -EADDRINUSE, or flakiness for tests, 10 | because it may not always fail. 11 | 12 | One mitigation is use Linux network namespace, and mount a private network so 13 | that the test with sockets could still work unless it uses external network. 14 | 15 | However, the test should not rely on network namespace, instead of binding an 16 | arbitrary port, the test could also bind with zero, which kernel will assign 17 | next available port hence it would always success. The port number can be 18 | retrieved by calling getsockname(2). 19 | 20 | Same tests use a for loop for bind: always retry with next port when bind 21 | returns -EADDRINUSE, this is less ideal, not only it is brute force, but also 22 | it may cause TOCTOU issue. 23 | -------------------------------------------------------------------------------- /tests/c/simple/nanosleep-threads-nocrash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | int shared_var = 0; 14 | 15 | void sleep_ms(int ms) { 16 | int secs = ms / 1000; 17 | int left = ms % 1000; 18 | const struct timespec req = {secs, 1000 * left}; 19 | struct timespec rem = {0, 0}; 20 | nanosleep(&req, &rem); 21 | } 22 | 23 | void* myThreadFun(void* vargp) { 24 | prctl(PR_SET_NAME, "myThreadFun\0", NULL, NULL, NULL); 25 | sleep_ms(1000); 26 | shared_var = 1; 27 | return NULL; 28 | } 29 | 30 | int main() { 31 | prctl(PR_SET_NAME, "mainThread\0", NULL, NULL, NULL); 32 | pthread_t thread_id; 33 | pthread_create(&thread_id, NULL, myThreadFun, NULL); 34 | 35 | sleep_ms(1000); 36 | shared_var = 2; 37 | 38 | pthread_join(thread_id, NULL); 39 | 40 | if (shared_var == 2) { 41 | return 0; 42 | } else { 43 | return 1; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /flaky-tests/hello_race_mini.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! One of the simplest possible race conditions. 10 | //! Exits with a nonzero code under one order, and not the other. 11 | 12 | use std::sync::Arc; 13 | use std::sync::atomic::AtomicUsize; 14 | use std::sync::atomic::Ordering::SeqCst; 15 | 16 | #[inline(never)] 17 | fn thread1(var: Arc) { 18 | var.store(1, SeqCst); 19 | } 20 | 21 | fn main() { 22 | let d = Arc::new(AtomicUsize::new(1)); 23 | let d1 = Arc::clone(&d); 24 | 25 | let h1 = std::thread::spawn(move || thread1(d1)); 26 | d.store(2, SeqCst); 27 | h1.join().unwrap(); 28 | 29 | let val = Arc::try_unwrap(d).unwrap().into_inner(); 30 | println!("Final value: {}", val); 31 | if val == 2 { 32 | println!("Antagonistic schedule reached, failing."); 33 | std::process::exit(1); 34 | } 35 | println!("Did not find antagonistic schedule. Succeeding."); 36 | } 37 | -------------------------------------------------------------------------------- /tests/standalone/curl_client_only.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -eu 9 | 10 | if [ "$*" == "" ]; then 11 | hermit="hermit" 12 | else 13 | hermit="$1" 14 | fi 15 | 16 | dir=$(dirname "$0") 17 | 18 | # Run the server asynchronously, with an open file descriptor to its stdout, 19 | # which is needed to read the address and port of the server. 20 | coproc server { python3 "$dir/../util/simplest_server.py" 1; } 21 | 22 | # Read the address:port combo that the server itself writes to stdout. This 23 | # also ensures the server has fully started up and is ready to receive incoming 24 | # connections. 25 | IFS= read -r -u "${server[0]}" address 26 | echo "Server address: $address" 27 | 28 | echo "Running curl with hermit command: $hermit" 29 | echo "Now run curl from $(command -v curl)..." 30 | "$hermit" --log=info run --network=host --base-env=minimal curl "$address" 31 | 32 | echo "Waiting for server shutdown" 33 | wait 34 | echo "Done." 35 | -------------------------------------------------------------------------------- /hermit-cli/src/id.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use core::fmt; 10 | use core::str::FromStr; 11 | 12 | use serde::Deserialize; 13 | use serde::Serialize; 14 | use uuid::Uuid; 15 | 16 | /// A recording ID. 17 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 18 | pub struct Id(Uuid); 19 | 20 | impl Id { 21 | /// Generates a new, random ID. 22 | pub fn unique() -> Self { 23 | Self(Uuid::new_v4()) 24 | } 25 | } 26 | 27 | impl FromStr for Id { 28 | type Err = ::Err; 29 | 30 | fn from_str(s: &str) -> Result { 31 | Uuid::from_str(s).map(Self) 32 | } 33 | } 34 | 35 | impl fmt::Display for Id { 36 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 37 | fmt::Display::fmt(&self.0.as_simple(), f) 38 | } 39 | } 40 | 41 | impl fmt::Debug for Id { 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | fmt::Display::fmt(&self.0.as_simple(), f) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/rust/futex_timeout.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Wait on a futex with a timeout. 10 | use nix::errno::Errno; 11 | 12 | fn main() { 13 | let layout = std::alloc::Layout::from_size_align(4, 4).unwrap(); // u32 14 | let ptr: *mut u8 = unsafe { std::alloc::alloc_zeroed(layout) }; 15 | 16 | eprintln!("main start futex wait ({:?})..", ptr); 17 | let tp = libc::timespec { 18 | tv_sec: 0, 19 | tv_nsec: 10_000_000, 20 | }; 21 | let res = unsafe { 22 | libc::syscall( 23 | libc::SYS_futex, 24 | ptr, 25 | libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG, 26 | 0, // val, 27 | &tp, // timeout, 28 | 0, // uaddr - ignored 29 | 0, // val3 - ignored 30 | ) 31 | }; 32 | let err = Errno::last_raw(); 33 | eprintln!( 34 | "Main thread: futex wait timed out and returned {}, errno {:?}", 35 | res, 36 | Errno::from_raw(err) 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /hermit-verify/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //hermetic_infra/hermit/hermit-verify:hermit-verify 2 | 3 | [package] 4 | name = "hermit-verify" 5 | version = "0.0.0" 6 | edition = "2024" 7 | repository = "https://github.com/facebookexperimental/hermit" 8 | license = "BSD-3-Clause" 9 | 10 | [dependencies] 11 | anyhow = "1.0.98" 12 | clap = { version = "3.2.25", features = ["derive", "env", "regex", "unicode", "wrap_help"] } 13 | colored = "2.1.0" 14 | detcore = { version = "0.0.0", path = "../detcore" } 15 | detcore-model = { version = "0.0.0", path = "../detcore-model" } 16 | edit-distance = { version = "0.0.0", path = "../common/edit-distance" } 17 | fbinit = { version = "0.2.0", git = "https://github.com/facebookexperimental/rust-shed.git", branch = "main" } 18 | rand = { version = "0.8", features = ["small_rng"] } 19 | serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "raw_value", "unbounded_depth"] } 20 | similar = { version = "2.2.0", features = ["bytes", "inline"] } 21 | tempfile = "3.22" 22 | tracing = { version = "0.1.41", features = ["attributes", "valuable"] } 23 | 24 | [dev-dependencies] 25 | pretty_assertions = { version = "1.2", features = ["alloc"], default-features = false } 26 | -------------------------------------------------------------------------------- /detcore/.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # coredump file 55 | core 56 | 57 | # Hack for ripgrep 58 | .ignore 59 | 60 | # Emacs temp files 61 | # ---------------- 62 | *~ 63 | \#*\# 64 | /.emacs.desktop 65 | /.emacs.desktop.lock 66 | *.elc 67 | auto-save-list 68 | tramp 69 | .\#* 70 | *_flymake.* 71 | /elpa/ 72 | flycheck_*.el 73 | .projectile 74 | .dir-locals.el 75 | 76 | 77 | # Rust related 78 | # ------------ 79 | # Generated by Cargo 80 | # will have compiled files and executables 81 | target/ 82 | 83 | # These are backup files generated by rustfmt 84 | **/*.rs.bk 85 | 86 | rustfmt.toml 87 | -------------------------------------------------------------------------------- /detcore-model/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //hermetic_infra/hermit/detcore-model:detcore-model 2 | 3 | [package] 4 | name = "detcore-model" 5 | version = "0.0.0" 6 | edition = "2024" 7 | repository = "https://github.com/facebookexperimental/hermit" 8 | license = "BSD-3-Clause" 9 | 10 | [dependencies] 11 | anyhow = "1.0.98" 12 | bitvec = { version = "1.0", features = ["atomic", "serde"] } 13 | bytesize = "2.0" 14 | chrono = { version = "0.4.41", features = ["clock", "serde", "std"], default-features = false } 15 | clap = { version = "3.2.25", features = ["derive", "env", "regex", "unicode", "wrap_help"] } 16 | libc = "0.2.139" 17 | nix = { version = "0.30.1", features = ["dir", "event", "hostname", "inotify", "ioctl", "mman", "mount", "net", "poll", "ptrace", "reboot", "resource", "sched", "signal", "term", "time", "user", "zerocopy"] } 18 | reverie-syscalls = { version = "0.1.0", git = "https://github.com/facebookexperimental/reverie.git", branch = "main" } 19 | serde = { version = "1.0.219", features = ["derive", "rc"] } 20 | shell-words = "1.1.0" 21 | tracing = { version = "0.1.41", features = ["attributes", "valuable"] } 22 | 23 | [dev-dependencies] 24 | pretty_assertions = { version = "1.2", features = ["alloc"], default-features = false } 25 | -------------------------------------------------------------------------------- /tests/standalone/network_bind_full.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::net::Ipv4Addr; 10 | use std::net::SocketAddr; 11 | use std::net::SocketAddrV4; 12 | use std::net::TcpListener; 13 | 14 | fn main() { 15 | let mut port_list = Vec::new(); 16 | for i in 32768..65535 { 17 | port_list.push(TcpListener::bind(("127.0.0.1", i)).unwrap()); 18 | assert_eq!( 19 | port_list.last().unwrap().local_addr().unwrap(), 20 | SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), i)) 21 | ); 22 | } 23 | let result = TcpListener::bind("127.0.0.1:0"); 24 | assert_eq!(result.unwrap_err().raw_os_error(), Some(libc::EADDRINUSE)); 25 | drop(port_list.remove(0)); 26 | let result = TcpListener::bind("127.0.0.1:0").unwrap(); 27 | assert_eq!( 28 | result.local_addr().unwrap(), 29 | SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 32768)) 30 | ); 31 | for port in port_list { 32 | drop(port); 33 | } 34 | println!("Done"); 35 | } 36 | -------------------------------------------------------------------------------- /detcore/tests/lit/dup_should_create_a_valid_fd/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | use std::os::fd::AsRawFd; 12 | 13 | use nix::fcntl::OFlag; 14 | use nix::sys::stat::fstat; 15 | use nix::unistd::close; 16 | use nix::unistd::dup; 17 | use nix::unistd::dup2_raw; 18 | use nix::unistd::dup3_raw; 19 | 20 | fn main() { 21 | let dup_fd = unsafe { dup2_raw(std::io::stdin(), 255) }.unwrap(); 22 | assert_eq!(dup_fd.as_raw_fd(), 255); 23 | assert!(fstat(&dup_fd).is_ok()); 24 | assert!(close(dup_fd).is_ok()); 25 | 26 | // Don't care if this fails. We just need fd 3 to be free. 27 | let _ = close(3); 28 | 29 | let dup_fd = dup(std::io::stderr()).unwrap(); 30 | assert_eq!(dup_fd.as_raw_fd(), 3); 31 | assert!(fstat(&dup_fd).is_ok()); 32 | assert!(close(dup_fd).is_ok()); 33 | 34 | let dup_fd = unsafe { dup3_raw(std::io::stderr(), 255, OFlag::empty()) }.unwrap(); 35 | assert_eq!(dup_fd.as_raw_fd(), 255); 36 | assert!(fstat(&dup_fd).is_ok()); 37 | assert!(close(dup_fd).is_ok()); 38 | } 39 | -------------------------------------------------------------------------------- /tests/util/curl_to_server_with_ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -eu 9 | 10 | if [ "$*" == "" ]; then 11 | hermit="hermit" 12 | else 13 | hermit="$1" 14 | fi 15 | 16 | dir=$(dirname "$0") 17 | 18 | # Run the server asynchronously, with an open file descriptor to its stdout, 19 | # which is needed to read the address and port of the server. 20 | coproc server { python3 "${dir}/../util/ssl_server.py" 1; } 21 | 22 | # Read the address:port combo that the server itself writes to stdout. This 23 | # also ensures the server has fully started up and is ready to receive incoming 24 | # connections. 25 | IFS= read -r -u "${server[0]}" address 26 | echo "Server address: ${address}" 27 | 28 | echo "Running curl with hermit command: ${hermit}" 29 | echo "Now run curl from $(command -v curl)..." 30 | "${hermit}" run --epoch "$(date --iso-8601=seconds)" --no-sequentialize-threads --no-deterministic-io -- "$(command -v curl)" -vL --resolve "${address}:127.0.0.1" "https://${address}" 31 | 32 | echo "Waiting for server shutdown" 33 | wait 34 | echo "Done." 35 | -------------------------------------------------------------------------------- /tests/rust/heap_ptrs.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Test deterministic Rust heap allocation 10 | 11 | #[allow(clippy::useless_vec)] 12 | fn main() { 13 | // Go far enough that we trigger an mmap directly for a big allocation. 14 | // Interleaving print/alloc makes it easy to see which ones call mmap in an strace. 15 | let x1 = vec![0u8; 1]; 16 | println!("alloc 1: {:#p}", x1.as_ptr()); 17 | let x2 = vec![0u8; 10]; 18 | println!("alloc 10: {:#p}", x2.as_ptr()); 19 | let x3 = vec![0u8; 100]; 20 | println!("alloc 100: {:#p}", x3.as_ptr()); 21 | let x4 = vec![0u8; 1000]; 22 | println!("alloc 1000: {:#p}", x4.as_ptr()); 23 | let x5 = vec![0u8; 10000]; 24 | println!("alloc 10,000: {:#p}", x5.as_ptr()); 25 | let x6 = vec![0u8; 100000]; 26 | println!("alloc 100,000: {:#p}", x6.as_ptr()); 27 | let x7 = vec![0u8; 1000000]; 28 | println!("alloc 1,000,000: {:#p}", x7.as_ptr()); 29 | let x8 = vec![0u8; 10000000]; 30 | println!("alloc 10,000,000: {:#p}", x8.as_ptr()); 31 | } 32 | -------------------------------------------------------------------------------- /detcore/tests/lit/file_race_openwrite/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me | FileCheck %s 10 | // CHECK: {{(1*2+|2*1+)}} 11 | 12 | // FIXME: This doesn't work yet: 13 | // %hermit run --strict --verify -- %me 14 | 15 | use std::fs; 16 | use std::io; 17 | use std::io::Write; 18 | use std::path::Path; 19 | 20 | use tempfile::NamedTempFile; 21 | 22 | fn write(path: &Path, s: &[u8], count: usize) -> io::Result<()> { 23 | let mut file = fs::File::create(path)?; 24 | for _ in 0..count { 25 | file.write_all(s)?; 26 | } 27 | file.sync_all() 28 | } 29 | 30 | fn main() { 31 | let path = NamedTempFile::new().unwrap().into_temp_path(); 32 | 33 | let thread = { 34 | let path = path.to_path_buf(); 35 | std::thread::spawn(move || write(&path, b"1", 10)) 36 | }; 37 | 38 | write(&path, b"2", 10).unwrap(); 39 | 40 | thread.join().unwrap().unwrap(); 41 | 42 | let contents = fs::read_to_string(&path).unwrap(); 43 | 44 | // Only one thread wins. 45 | assert_eq!(contents.len(), 10); 46 | 47 | println!("{}", contents); 48 | } 49 | -------------------------------------------------------------------------------- /tests/rust/socketpair.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use nix::sys::socket::AddressFamily; 10 | use nix::sys::socket::SockFlag; 11 | use nix::sys::socket::SockType; 12 | use nix::sys::socket::socketpair; 13 | 14 | fn main() { 15 | if matches!(std::env::var("HERMIT_MODE"), Ok(mode) if mode == "record") { 16 | // TODO: Record mode currently hangs for this test 17 | eprintln!("Skipping test in record mode."); 18 | return; 19 | } 20 | 21 | let (sock1, sock2) = socketpair( 22 | AddressFamily::Unix, 23 | SockType::Stream, 24 | None, 25 | SockFlag::empty(), 26 | ) 27 | .unwrap(); 28 | 29 | // WARNING: this assumes the Linux socket behavior, which is to NOT block a write call if there 30 | // is enough buffer space. This test therefore depends on that buffer being more than 5 bytes. 31 | nix::unistd::write(&sock1, b"Hello").unwrap(); 32 | 33 | let mut buf = [0; 5]; 34 | nix::unistd::read(&sock2, &mut buf).unwrap(); 35 | assert_eq!(&buf[..], b"Hello"); 36 | println!("Received message. Test complete.\n"); 37 | } 38 | -------------------------------------------------------------------------------- /hermit-cli/src/bin/hermit/remove.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::path::PathBuf; 10 | 11 | use clap::Parser; 12 | use hermit::Context; 13 | use hermit::Error; 14 | use hermit::HermitData; 15 | use hermit::Id; 16 | use reverie::ExitStatus; 17 | 18 | use super::global_opts::GlobalOpts; 19 | 20 | /// Subcommand for removing a previous recording. 21 | #[derive(Debug, Parser)] 22 | pub struct RemoveOpts { 23 | /// The ID of the recording to delete. 24 | #[clap(value_name = "ID")] 25 | id: Id, 26 | 27 | /// Directory where recorded syscall data is stored. 28 | #[clap(long, value_name = "DIR", env = "HERMIT_DATA_DIR")] 29 | data_dir: Option, 30 | } 31 | 32 | impl RemoveOpts { 33 | pub fn main(&self, global: &GlobalOpts) -> Result { 34 | let _guard = global.init_tracing(); 35 | 36 | let hermit = HermitData::from(self.data_dir.as_ref()); 37 | 38 | hermit 39 | .remove(self.id) 40 | .with_context(|| format!("Failed to delete recording {}", self.id))?; 41 | 42 | Ok(ExitStatus::SUCCESS) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /detcore/src/detlog.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Module contains macroses that help tracing DETLOG entires for the purpose of verifiying determinism 10 | //! ['detlog'] can be used to write a deterministic log entry at INFO level 11 | //! ['detlog_debug] can be use to write a deterministic log entry at DEBUG level 12 | 13 | /// Macro used to encapsulate tracing should-be-deterministic information. 14 | /// This is currently at the INFO log level. 15 | #[macro_export] 16 | macro_rules! detlog { 17 | ($($arg:tt)+) => {{ 18 | tracing::info!("DETLOG {}", format!($($arg)+)); 19 | }}; 20 | } 21 | 22 | /// Macro used to encapsulate tracing should-be-deterministic information. 23 | /// This variant is at a higher log level and requires that logging verbosity is 24 | /// set to DEBUG. 25 | #[macro_export] 26 | macro_rules! detlog_debug { 27 | ($($arg:tt)+) => {{ 28 | tracing::debug!("DETLOG {}", format!($($arg)+)); 29 | }}; 30 | } 31 | 32 | #[cfg(test)] 33 | mod tests { 34 | #[test] 35 | fn test_detlog() { 36 | detlog!("Hello : {}. From {:?}", "World", 31337); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /detcore/tests/lit/file_write_race/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Threads racing to write the same file, dup'ing the file descriptor. Exercises 10 | //! SYS_fcntl with F_DUPFD_CLOEXEC. 11 | 12 | // RUN: %me | FileCheck %s 13 | // CHECK: {{([12]{20})}} 14 | 15 | // FIXME: This doesn't work yet: 16 | // %hermit run --strict --verify -- %me 17 | 18 | use std::fs; 19 | use std::io::Write; 20 | 21 | use tempfile::NamedTempFile; 22 | 23 | fn main() { 24 | let (mut file, path) = NamedTempFile::new().unwrap().into_parts(); 25 | 26 | let mut file2 = file.try_clone().unwrap(); // Implicitly dups fd w/ fcntl 27 | 28 | let child = std::thread::spawn(move || { 29 | for _ in 0..10 { 30 | file2.write_all(b"1").unwrap(); 31 | } 32 | }); 33 | 34 | for _ in 0..10 { 35 | file.write_all(b"2").unwrap(); 36 | } 37 | 38 | child.join().unwrap(); 39 | 40 | let contents = fs::read_to_string(path).unwrap(); 41 | 42 | // None of the writes from either thread should be dropped: 43 | assert_eq!(contents.len(), 20); 44 | 45 | println!("{}", contents); 46 | } 47 | -------------------------------------------------------------------------------- /common/test-allocator/src/test_bin.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // Do some general allocations as a test 10 | 11 | use std::collections::BTreeMap; 12 | use std::collections::HashMap; 13 | 14 | #[global_allocator] 15 | static ALLOC: test_allocator::Global = test_allocator::Global; 16 | 17 | fn main() { 18 | let mut x = vec![0u64, 1]; 19 | for _ in 0..92 { 20 | x.push(x[x.len() - 1] + x[x.len() - 2]); 21 | } 22 | assert_eq!(x[93], 12200160415121876738); 23 | println!("{:?}", x); 24 | println!("test print"); 25 | 26 | let h = (0..100) 27 | .zip((100..200).map(|x| x.to_string())) 28 | .collect::>(); 29 | let b = (0..100) 30 | .zip((100..200).map(|x| x.to_string())) 31 | .collect::>(); 32 | 33 | assert_eq!(h.get(&50), b.get(&50)); 34 | 35 | test_allocator::GLOBAL.skip_to_offset(0x1000000).unwrap(); 36 | let x = Box::new(5u64); 37 | assert_eq!( 38 | Box::into_raw(x) as usize, 39 | test_allocator::GLOBAL_SLAB_TOP - 0x1000000 - std::mem::size_of::() 40 | ); 41 | println!("Success!"); 42 | } 43 | -------------------------------------------------------------------------------- /tests/standalone/replay_trace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -xeuo pipefail 9 | 10 | if [ "$*" == "" ]; then 11 | hermit="hermit" 12 | else 13 | hermit="$1" 14 | fi 15 | 16 | tmpdir=$(mktemp -d) 17 | log1=$(mktemp -p "$tmpdir") 18 | log2=$(mktemp -p "$tmpdir") 19 | 20 | function on_exit { 21 | rm -rf -- "$tmpdir" 22 | } 23 | trap on_exit EXIT 24 | 25 | RUST_LOG=detcore=trace "$hermit" --log-file="$log1".log run --bind="$tmpdir" --base-env=minimal \ 26 | --record-preemptions-to="$log1".trace \ 27 | -- bash -c 'find ./hermetic_infra/hermit/hermit-cli/src' 28 | 29 | RUST_LOG=detcore=trace "$hermit" --log-file="$log2".log run --bind="$tmpdir" --base-env=minimal \ 30 | --replay-schedule-from="$log1".trace \ 31 | --record-preemptions-to="$log2".trace \ 32 | -- bash -c 'find ./hermetic_infra/hermit/hermit-cli/src' 33 | 34 | wc "$log1".log "$log2".log "$log1".trace "$log2".trace 35 | 36 | grep "Trace loaded" "$log2".log 37 | 38 | if grep -s DESYNC "$log2".log; then 39 | echo "ERROR: Found DESYNC event on trace replay!" 40 | exit 1 41 | fi 42 | 43 | "$hermit" log-diff "$log1".log "$log2".log 44 | 45 | echo "Test passed." 46 | -------------------------------------------------------------------------------- /detcore/tests/scripts/test_build_musl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -Eeou pipefail 9 | 10 | echo "Invoking script test: $0 $*" 11 | 12 | MUSL_TARBALL="$1" 13 | shift 14 | RUNNER="$*" 15 | 16 | MUSL_VER=1.2.1 17 | MUSL_SOURCE_WORK_DIR=$(mktemp -d) 18 | 19 | TIME='/bin/time -o /dev/stdout' 20 | 21 | trap cleanup EXIT 22 | 23 | function cleanup { 24 | echo "Cleanup .." && rm -fr "${MUSL_SOURCE_WORK_DIR}" 25 | } 26 | 27 | echo "Building Musl $MUSL_VER as $(whoami) on $(hostname)" 28 | 29 | cd "$MUSL_SOURCE_WORK_DIR" 30 | echo; echo "Unpacking sources..." 31 | $TIME /bin/tar -xf "$MUSL_TARBALL" 32 | echo "Done unpacking into $MUSL_SOURCE_WORK_DIR" 33 | mkdir build 34 | cd build 35 | 36 | echo; echo "Configure step:" 37 | $TIME ../musl-${MUSL_VER}/configure --prefix="" 38 | 39 | echo; echo "Parallel build step (stdout to file):" 40 | echo "Build prefixed with: $RUNNER" 41 | # Here we explicitly want $RUNNER to split into multiple words: 42 | # shellcheck disable=SC2086 43 | /bin/time --verbose ${RUNNER} -- make -j > ./build_output.txt 44 | 45 | echo 46 | echo "Successfully finished build. Lines of stdout: $(wc -l ./build_output.txt | awk '{ print $1 }' )" 47 | -------------------------------------------------------------------------------- /hermit-cli/src/bin/hermit/clean.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::path::PathBuf; 10 | 11 | use clap::Parser; 12 | use colored::Colorize; 13 | use hermit::Context; 14 | use hermit::Error; 15 | use hermit::HermitData; 16 | use reverie::ExitStatus; 17 | 18 | use super::global_opts::GlobalOpts; 19 | 20 | /// Command-line options for the "clean" subcommand. 21 | #[derive(Debug, Parser)] 22 | pub struct CleanOpts { 23 | /// Directory where recorded syscall data is stored. 24 | #[clap(long, value_name = "DIR", env = "HERMIT_DATA_DIR")] 25 | data_dir: Option, 26 | } 27 | 28 | impl CleanOpts { 29 | pub fn main(&self, global: &GlobalOpts) -> Result { 30 | let _guard = global.init_tracing(); 31 | 32 | let hermit = HermitData::from(self.data_dir.as_ref()); 33 | 34 | for id in hermit.recordings() { 35 | println!("{} {} ...", "Deleting".red().bold(), id.to_string().bold()); 36 | hermit 37 | .remove(id) 38 | .with_context(|| format!("Failed to delete recording {}", id))?; 39 | } 40 | 41 | Ok(ExitStatus::SUCCESS) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /flaky-tests/external_flaky_service/flaky_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | NUMBER_OF_REQUESTS=$1 9 | port=$2 10 | function request_handler () { 11 | # Number of times to handle request 12 | for (( n=0; n<"$NUMBER_OF_REQUESTS"; n++ )) 13 | do 14 | # Read from stdin, we have redirected to read from REQUEST 15 | read -r 16 | # Generate an random number and store in output 17 | output=$(( RANDOM % 2 )) 18 | # Print the output 19 | echo $output 20 | done 21 | } 22 | 23 | # coproc takes 2 arguments. First is the buffer that stores stdin and stdout, second is a function or file to run. 24 | # In this case, it runs request_handler function and stores the stdin and stdout in REQUEST 25 | coproc REQUEST { request_handler; } 26 | 27 | # Run nc (shortform of netcat), -4 specifies ipv4, -n is for not to use DNS. We do not need DNS as we are running on localhost. 28 | # -l is to bind and listen at the specified port. It mainly means server mode. 29 | # Between < > is for read from and write to REQUEST. 30 | nc -4 -n 0.0.0.0 -l "$port" -k <&"${REQUEST[0]}" >&"${REQUEST[1]}" 31 | 32 | # REQUEST is the buffer between function request_handler and nc. 33 | -------------------------------------------------------------------------------- /hermit-cli/src/bin/hermit/logdiff.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::path::Path; 10 | use std::path::PathBuf; 11 | 12 | use clap::Parser; 13 | use detcore::logdiff; 14 | use reverie::process::ExitStatus; 15 | 16 | use super::global_opts::GlobalOpts; 17 | 18 | /// Command-line options for the "logdiff" subcommand. 19 | #[derive(Debug, Parser)] 20 | pub struct LogDiffCLIOpts { 21 | /// First log to compare. 22 | file_a: PathBuf, 23 | /// Second log to compare. 24 | file_b: PathBuf, 25 | 26 | #[clap(flatten)] 27 | pub more: logdiff::LogDiffOpts, 28 | } 29 | 30 | impl LogDiffCLIOpts { 31 | /// Construct LogDiffOpts to compare two files. 32 | pub fn new(a: &Path, b: &Path) -> Self { 33 | Self { 34 | file_a: PathBuf::from(a), 35 | file_b: PathBuf::from(b), 36 | more: Default::default(), 37 | } 38 | } 39 | 40 | /// Process log messages from two files. 41 | pub fn main(&self, _global: &GlobalOpts) -> ExitStatus { 42 | if logdiff::log_diff(&self.file_a, &self.file_b, &self.more) { 43 | ExitStatus::Exited(1) 44 | } else { 45 | ExitStatus::Exited(0) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /hermit-verify/src/common/opts.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::path::PathBuf; 10 | 11 | use clap::Parser; 12 | 13 | #[derive(Parser, Debug)] 14 | pub struct CommonOpts { 15 | /// Specify hermit binary path. 16 | #[clap(long, env = "HERMIT_BIN", default_value = "hermit")] 17 | pub hermit_bin: PathBuf, 18 | 19 | // In test environment allows to specify root directory with produced artifacts 20 | #[clap(long, env = "TEST_RESULT_ARTIFACTS_DIR")] 21 | pub test_result_artifact_dir: Option, 22 | 23 | // Allows to ignore TEST_RESULT_ARTIFACTS_DIR when running in test environment 24 | #[clap(long, default_value = "false", parse(try_from_str))] 25 | pub ignore_test_result_artifacts_dir: bool, 26 | 27 | // Specify root directory with produced artifacts 28 | #[clap(long)] 29 | pub temp_dir_path: Option, 30 | 31 | /// Allow the run itself to exit with a nonzero code, but still count the 32 | /// verification as successful if all runs match each other. 33 | #[clap(long)] 34 | pub allow_nonzero_exit: bool, 35 | 36 | /// Print extra information while running. 37 | #[clap(long, short = 'v')] 38 | pub verbose: bool, 39 | } 40 | -------------------------------------------------------------------------------- /tests/standalone/network_bind.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::net::Ipv4Addr; 10 | use std::net::SocketAddr; 11 | use std::net::SocketAddrV4; 12 | use std::net::TcpListener; 13 | 14 | fn main() { 15 | let first_listener = TcpListener::bind("127.0.0.1:0").unwrap(); 16 | assert_eq!( 17 | first_listener.local_addr().unwrap(), 18 | SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 32768)) 19 | ); 20 | let second_listener = TcpListener::bind("127.0.0.1:32769").unwrap(); 21 | assert_eq!( 22 | second_listener.local_addr().unwrap(), 23 | SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 32769)) 24 | ); 25 | let third_listener = TcpListener::bind("127.0.0.1:0").unwrap(); 26 | assert_eq!( 27 | third_listener.local_addr().unwrap(), 28 | SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 32770)) 29 | ); 30 | drop(second_listener); 31 | let second_listener = TcpListener::bind("127.0.0.1:32769").unwrap(); 32 | assert_eq!( 33 | second_listener.local_addr().unwrap(), 34 | SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 32769)) 35 | ); 36 | println!("Done"); 37 | } 38 | -------------------------------------------------------------------------------- /hermit-cli/src/bin/hermit/list.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::path::PathBuf; 10 | 11 | use clap::Parser; 12 | use colored::Colorize; 13 | use hermit::Error; 14 | use hermit::HermitData; 15 | use reverie::ExitStatus; 16 | 17 | use super::global_opts::GlobalOpts; 18 | 19 | /// Command-line options for the "list" subcommand. 20 | #[derive(Debug, Parser)] 21 | pub struct ListOpts { 22 | /// Directory where recorded syscall data is stored. 23 | #[clap(long, value_name = "DIR", env = "HERMIT_DATA_DIR")] 24 | data_dir: Option, 25 | } 26 | 27 | impl ListOpts { 28 | pub fn main(&self, global: &GlobalOpts) -> Result { 29 | let _guard = global.init_tracing(); 30 | 31 | let hermit = HermitData::from(self.data_dir.as_ref()); 32 | 33 | for id in hermit.recordings() { 34 | let metadata = hermit.recording_metadata(id)?; 35 | 36 | println!( 37 | "{id} {program} {args}", 38 | id = id.to_string().bold(), 39 | program = metadata.program.cyan().bold(), 40 | args = metadata.args.join(" ").bold().dimmed(), 41 | ); 42 | } 43 | 44 | Ok(ExitStatus::SUCCESS) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /detcore/tests/lit/sched_getaffinity/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | use nix::unistd::Pid; 12 | use syscalls::Errno; 13 | use syscalls::Sysno; 14 | use syscalls::syscall; 15 | 16 | fn raw_sched_getaffinity() -> Result<(), Errno> { 17 | let mut buf = [0u8; N]; 18 | 19 | unsafe { syscall!(Sysno::sched_getaffinity, 0, buf.len(), &mut buf as *mut u8) }?; 20 | 21 | // The resulting buffer should have at least 1 bit set. 22 | assert!( 23 | buf.iter().any(|byte| *byte != 0), 24 | "Affinity mask should not be all zeroes. Got {:?}", 25 | buf 26 | ); 27 | 28 | Ok(()) 29 | } 30 | 31 | fn main() { 32 | // Call via a wrapper, which allocates its own bitset. 33 | let _ = nix::sched::sched_getaffinity(Pid::from_raw(0)).unwrap(); 34 | 35 | // Call with various buffer sizes to make sure we can handle them all. 36 | raw_sched_getaffinity::<128>().unwrap(); 37 | raw_sched_getaffinity::<1024>().unwrap(); 38 | raw_sched_getaffinity::<2048>().unwrap(); 39 | raw_sched_getaffinity::<0x1000>().unwrap(); 40 | raw_sched_getaffinity::<0x2000>().unwrap(); 41 | raw_sched_getaffinity::<0x4000>().unwrap(); 42 | raw_sched_getaffinity::<0x8000>().unwrap(); 43 | } 44 | -------------------------------------------------------------------------------- /tests/chaos/order_violation.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // Though one thread does a lot of work before checking, there's no enforcement 14 | // that global_str is actually set to a non-null value before use. 15 | 16 | #define DO_WORK \ 17 | do { \ 18 | volatile int _work_var = 10000; \ 19 | while (_work_var > 0) { \ 20 | _work_var--; \ 21 | } \ 22 | } while (0); 23 | 24 | char* global_str = NULL; 25 | 26 | void* Thread1(void* x) { 27 | DO_WORK 28 | if (!global_str) { 29 | // Simulate SEGFAULT, but exit cleanly because hermit doesn't handle this 30 | // well right now 31 | printf("ERROR! global_str is null at use.\n"); 32 | exit(1); 33 | } 34 | printf("%s\n", global_str); 35 | return NULL; 36 | } 37 | 38 | void* Thread2(void* x) { 39 | global_str = "Hello world!"; 40 | return NULL; 41 | } 42 | 43 | int main() { 44 | pthread_t t[2]; 45 | pthread_create(&t[1], NULL, Thread2, NULL); 46 | pthread_create(&t[0], NULL, Thread1, NULL); 47 | pthread_join(t[1], NULL); 48 | pthread_join(t[0], NULL); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /flaky-tests/bind/bind_random.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | static int tcp4_stream(void) { 16 | return socket(AF_INET, SOCK_STREAM, 0); 17 | } 18 | 19 | static int bind_port(int sockfd, int port) { 20 | struct sockaddr_in sa; 21 | 22 | memset(&sa, 0, sizeof(sa)); 23 | sa.sin_family = AF_INET; 24 | sa.sin_port = port; 25 | sa.sin_addr.s_addr = INADDR_ANY; 26 | 27 | return bind(sockfd, (const struct sockaddr*)&sa, sizeof(sa)); 28 | } 29 | 30 | static void do_some_stuff(void) { 31 | struct timespec tp = { 32 | .tv_sec = 0, 33 | .tv_nsec = 100000000, 34 | }; 35 | clock_nanosleep(CLOCK_MONOTONIC, 0, &tp, nullptr); 36 | } 37 | 38 | // Bind with a random port, this is generally a bad idea for tests because 39 | // the tests would fail when run in parallel, and when other parallel job's 40 | // rand32 call return the same number. 41 | TEST(BindRandom, bindRandom) { 42 | int sockfd = tcp4_stream(); 43 | ASSERT_GE(sockfd, 0); 44 | int port = folly::Random::rand32(9000, 9999); 45 | int ret = bind_port(sockfd, port); 46 | EXPECT_EQ(ret, 0); 47 | do_some_stuff(); 48 | close(sockfd); 49 | } 50 | -------------------------------------------------------------------------------- /tests/c/clone.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #define __ARCH_WANT_SYS_CLONE 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | int child_set_text(void* arg); 19 | 20 | // Tests clone syscall. Parents waits for child to set shared memory and prints 21 | // the text set by the child. 22 | int main(void) { 23 | const int STACK_SIZE = 0x1000; 24 | void* child_stack = malloc(STACK_SIZE); 25 | if (child_stack == NULL) { 26 | fprintf(stderr, "Malloc failed, reason:\n%s\n", strerror(errno)); 27 | exit(1); 28 | } 29 | 30 | char* text = "Hello from parent!"; 31 | pid_t pid = clone( 32 | child_set_text, child_stack + STACK_SIZE, CLONE_VM | SIGCHLD, &text); 33 | if (pid == -1) { 34 | fprintf(stderr, "Clone failed, reason:\n%s\n", strerror(errno)); 35 | exit(1); 36 | } 37 | 38 | int status; 39 | if (wait(&status) == -1) { 40 | fprintf(stderr, "Wait failed, reason %s\n", strerror(errno)); 41 | exit(1); 42 | } 43 | 44 | printf("Child exited with status %d, set text to \"%s\"\n", status, text); 45 | return 0; 46 | } 47 | 48 | int child_set_text(void* arg) { 49 | *(char**)arg = "Hello from child!"; 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /hermit-cli/src/bin/hermit/version.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use once_cell::sync::OnceCell; 10 | 11 | #[cfg(not(fbcode_build))] 12 | const OSS_VERSION: &str = "0.1"; 13 | 14 | pub struct Version(String); 15 | 16 | impl Version { 17 | /// Gets a static string of the version. Useful for integration with 18 | /// clap. 19 | pub fn get() -> &'static str { 20 | static VERSION: OnceCell = OnceCell::new(); 21 | VERSION.get_or_init(Self::new).version() 22 | } 23 | 24 | /// Returns the version string. 25 | pub fn version(&self) -> &str { 26 | &self.0 27 | } 28 | 29 | /// Computes the version string from the build info. 30 | pub fn new() -> Self { 31 | #[cfg(fbcode_build)] 32 | { 33 | use build_info::BuildInfo; 34 | 35 | let revision = Some(BuildInfo::get_revision()).filter(|s| !s.is_empty()); 36 | let pkg_version = Some(BuildInfo::get_package_version()).filter(|s| !s.is_empty()); 37 | 38 | Self(format!( 39 | "fbsource: {}, fbpkg: hermit:{}", 40 | revision.unwrap_or("unknown"), 41 | pkg_version.unwrap_or("unknown") 42 | )) 43 | } 44 | 45 | #[cfg(not(fbcode_build))] 46 | Self(OSS_VERSION.to_owned()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/shell/non_strict/curl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -eu 9 | 10 | # Make sure the proxy doesn't get in the way. Otherwise, we will get a 11 | # "kErrorAddressPrivate" error. 12 | unset http_proxy 13 | 14 | dir=$(dirname "$0") 15 | 16 | # Optionally take the name of the server script in an env var. 17 | if [[ ! -v PYTHON_SIMPLEST_SERVER ]]; then 18 | pyserver="$dir/../../util/simplest_server.py" 19 | else 20 | pyserver="$PYTHON_SIMPLEST_SERVER" 21 | fi 22 | echo "Running server from $pyserver" 23 | 24 | # This is set by coproc below. Declaring this to keep the linter happy. 25 | declare server_PID 26 | 27 | # Run the server asynchronously, with an open file descriptor to its stdout, 28 | # which is needed to read the address and port of the server. 29 | coproc server { python3 "$pyserver" 1; } 30 | 31 | echo "Child process in pid $server_PID" 32 | 33 | echo "Waiting for server to start up..." 34 | 35 | # Read the address:port combo that the server itself writes to stdout. This 36 | # also ensures the server has fully started up and is ready to receive incoming 37 | # connections. 38 | IFS= read -r -u "${server[0]}" address 39 | 40 | echo "Server address: $address" 41 | 42 | echo "Now run curl from $(command -v curl)..." 43 | curl "$address" 44 | 45 | echo "Waiting for server shutdown" 46 | wait 47 | echo "Finished." 48 | -------------------------------------------------------------------------------- /detcore/tests/lit/fstat/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Checks that we can observe mod time changes. 10 | 11 | // RUN: %me 12 | 13 | // FIXME: 'hermit run --strict --verify' does not work! This is likely caused by 14 | // the non-determinism bug with st_mtime (see `hermit-run-strict.lit`). 15 | 16 | use std::io::Write; 17 | use std::thread::sleep; 18 | use std::time::Duration; 19 | 20 | use nix::sys::stat::fstat; 21 | use tempfile::NamedTempFile; 22 | 23 | fn main() { 24 | let mut fd = NamedTempFile::new().unwrap(); 25 | 26 | let stat1 = fstat(&fd).unwrap(); 27 | 28 | println!("stat1: {:#?}", stat1); 29 | 30 | // Make sure nothing changes with subsequent fstat calls. 31 | for _ in 0..5 { 32 | assert_eq!(stat1, fstat(&fd).unwrap()); 33 | } 34 | 35 | // Sleep to ensure enough real time elapses such that the mod time changes 36 | // with the write. 37 | sleep(Duration::from_millis(10)); 38 | 39 | // Update the file. There's no buffering, so this should translate to a 40 | // syscall. 41 | fd.write_all(b"hello\n").unwrap(); 42 | 43 | let stat2 = fstat(&fd).unwrap(); 44 | 45 | println!("stat2: {:#?}", stat2); 46 | 47 | assert!( 48 | stat2.st_mtime > stat1.st_mtime || stat2.st_mtime_nsec > stat1.st_mtime_nsec, 49 | "Expected mtime to advance" 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /flaky-tests/bind/bind_same.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | static int tcp4_stream(void) { 15 | return socket(AF_INET, SOCK_STREAM, 0); 16 | } 17 | 18 | static int bind_port(int sockfd, int port) { 19 | struct sockaddr_in sa; 20 | 21 | memset(&sa, 0, sizeof(sa)); 22 | sa.sin_family = AF_INET; 23 | sa.sin_port = port; 24 | sa.sin_addr.s_addr = INADDR_ANY; 25 | 26 | return bind(sockfd, (const struct sockaddr*)&sa, sizeof(sa)); 27 | } 28 | 29 | TEST(BindZero, bindZero) { 30 | int sockfd = tcp4_stream(); 31 | ASSERT_GE(sockfd, 0); 32 | int ret = bind_port(sockfd, 0); 33 | EXPECT_EQ(ret, 0); 34 | close(sockfd); 35 | } 36 | 37 | static void do_some_stuff(void) { 38 | struct timespec tp = { 39 | .tv_sec = 0, 40 | .tv_nsec = 100000000, 41 | }; 42 | clock_nanosleep(CLOCK_MONOTONIC, 0, &tp, nullptr); 43 | } 44 | 45 | // Bind with the same port, this is generally a bad idea for tests because 46 | // the tests would fail when run in parallel -- which is enabled when doing 47 | // stress-runs. 48 | TEST(BindArbitrary, bindConst) { 49 | int sockfd = tcp4_stream(); 50 | ASSERT_GE(sockfd, 0); 51 | int ret = bind_port(sockfd, 1234); 52 | EXPECT_EQ(ret, 0); 53 | do_some_stuff(); 54 | close(sockfd); 55 | } 56 | -------------------------------------------------------------------------------- /tests/shell/par_work.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # Performs substantial work in parallel. Test of slowdown do to loss 9 | # of parallelism under instrumentation/recording. 10 | 11 | # Takes roughly 0.47s natively for 1 work unit, 0.6s under rr. 12 | 13 | # For 10 tasks / 200K loop tripcount: 14 | # Shows perfect parallelism, 0.48s realtime 4.6s user, neglible system. 15 | # - Under rr, perfect non-parallelism: 5.6s real, 5.1 user, 0.6 sys. 16 | # - rr chaos 7.564s real, 6.7 user, 0.7 sys. 17 | # - hermit run: 0.86 real, 5.7 user, 2.2 sys. 18 | # - hermit strict: 5.5s real, 5.2 user, 0.46 sys. 19 | # - strace -cf: 0.53s real, 4.7s user, 0.12 sys. 20 | # Only ~6052 syscalls. 21 | # - taskset 0x1: 4.9s real, 4.8s user, 42ms sys. 22 | 23 | if [[ "$HERMIT_MODE" = "chaosreplay" ]] || 24 | [[ "$HERMIT_MODE" = "tracereplay" ]]; 25 | then 26 | # TODO(T100400409): Reenable after performance improvements 27 | echo "Skipping par_work in unsupported mode. Re-enable when it is fixed." >&2 28 | exit 0 29 | fi 30 | 31 | PARALLELISM=10 32 | 33 | function work() { 34 | name=$1 35 | echo "Start $name" 36 | python3 < 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | void allocateMemory(int size) { 16 | char* ptr; 17 | ptr = (char*)malloc(size); 18 | for (int i = 0; i < size; ++i) { 19 | ptr[i] = 64; 20 | } 21 | } 22 | const int MB = 1024 * 1024; 23 | int main() { 24 | struct sysinfo info; 25 | sleep(5); 26 | 27 | allocateMemory(1 * MB); // allocating 1Mb of memory to check in sysinfo result 28 | 29 | sysinfo(&info); 30 | 31 | setlocale(LC_NUMERIC, ""); // Print large numbers with commas. 32 | printf("uptime: %lu sec\n", info.uptime); 33 | printf("load_time_1: %lu\n", info.loads[0]); 34 | printf("load_time_5: %lu\n", info.loads[1]); 35 | printf("load_time_15: %lu\n", info.loads[2]); 36 | printf("total RAM: %'lu\n", info.totalram); 37 | printf("free RAM: %'lu\n", info.freeram); 38 | printf("shared RAM: %'lu\n", info.sharedram); 39 | printf("buffer RAM: %'lu\n", info.bufferram); 40 | printf("total swap: %lu\n", info.totalswap); 41 | printf("free swap: %'lu\n", info.freeswap); 42 | printf("total high size: %'lu\n", info.totalhigh); 43 | printf("free high: %'lu\n", info.freehigh); 44 | printf("\n"); 45 | printf("mem_unit: %u\n", info.mem_unit); 46 | printf("Total - free = used: %'lu\n", info.totalram - info.freeram); 47 | } 48 | -------------------------------------------------------------------------------- /detcore/src/record_or_replay.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use reverie::Error; 10 | use reverie::Guest; 11 | use reverie::Subscription; 12 | use reverie::Tool; 13 | use reverie::syscalls::Syscall; 14 | use serde::Deserialize; 15 | use serde::Serialize; 16 | 17 | use crate::config::Config; 18 | use crate::tool_global::GlobalState; 19 | 20 | /// Helper trait. 21 | pub trait RecordOrReplay: Tool {} 22 | 23 | impl RecordOrReplay for T where T: Tool {} 24 | 25 | /// A tool that only injects the syscall it receives. 26 | #[derive(Debug, Default, Serialize, Deserialize)] 27 | pub struct NoopTool; 28 | 29 | #[reverie::tool] 30 | impl Tool for NoopTool { 31 | type GlobalState = GlobalState; 32 | type ThreadState = (); 33 | 34 | fn subscriptions(_cfg: &Config) -> Subscription { 35 | // Don't subscribe to anything by default. This noop-tool doesn't care 36 | // about any syscalls and will be ORed with the detcore subscriptions. 37 | Subscription::none() 38 | } 39 | 40 | async fn handle_syscall_event>( 41 | &self, 42 | guest: &mut T, 43 | call: Syscall, 44 | ) -> Result { 45 | // NOTE: Cannot use tail_inject here as that would prevent any detcore 46 | // post-hook code from running. 47 | Ok(guest.inject(call).await?) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /flaky-tests/external_flaky_service/flaky_server_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # First argument is the hermit 9 | if [ "$*" == "" ]; then 10 | hermit="hermit" 11 | else 12 | hermit="$1" 13 | fi 14 | 15 | dir=$(dirname "$0") 16 | 17 | # Second argument is the number of requests 18 | NUMBER_OF_REQUESTS=$2 19 | 20 | # Starting from 1025 as usually 0-1024 are reserved. 21 | INITIAL_PORT=1025 22 | 23 | port=$INITIAL_PORT 24 | # Command to check if the port is free 25 | is_port_free=$(netstat -taln | grep $port) 26 | 27 | # Loop to find first free port 28 | while [[ -n "$is_port_free" ]]; do 29 | port=$(( port+1 )) 30 | is_port_free=$(netstat -taln | grep $port) 31 | done 32 | echo "Usable Port: $port" 33 | 34 | # Launch the flaky server on the specified port. 35 | "$dir"/flaky_server.sh "$NUMBER_OF_REQUESTS" "$port" & 36 | 37 | # Make requets to the flaky server 38 | for (( c=0; c<"$NUMBER_OF_REQUESTS"; c++ )) 39 | do 40 | # We are running `nc localhost $port <<< " " ` in hermit. 41 | # It sends a request with " " to the specified ip port. 42 | # Our ip is localhost and port is $port. 43 | output=$("$hermit" --log=error run --base-env=minimal nc -- localhost $port <<< " ") 44 | echo "$output" 45 | done 46 | 47 | # Kill the application using $port, to make sure we release the port 48 | kill "$(lsof -t -i:"$port")" 49 | wait 50 | echo "End of Program" 51 | exit "$output" 52 | -------------------------------------------------------------------------------- /hermit-cli/src/bin/hermit/container.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use hermit::Context; 10 | use hermit::Error; 11 | use hermit::SerializableError; 12 | use rand::Rng; 13 | use rand::thread_rng; 14 | use reverie::process::Container; 15 | use reverie::process::Mount; 16 | use reverie::process::Namespace; 17 | 18 | pub fn default_container(pin_threads: bool) -> Container { 19 | let mut container = Container::new(); 20 | container 21 | .unshare(Namespace::PID) 22 | .map_root() 23 | .hostname("hermetic-container.local") 24 | .domainname("local") 25 | .mount(Mount::proc()); 26 | 27 | if pin_threads { 28 | let mut rng = thread_rng(); 29 | let rand_core: usize = rng.gen_range(0..num_cpus::get()); 30 | tracing::info!("Pinning tracer and guest threads to core {}", rand_core); 31 | container.affinity(rand_core); 32 | } 33 | container 34 | } 35 | 36 | /// Helper to run a function inside a container, taking care to display any 37 | /// errors and propagate the exit status. 38 | pub fn with_container(container: &mut Container, mut f: F) -> Result 39 | where 40 | F: FnMut() -> Result, 41 | T: serde::Serialize + serde::de::DeserializeOwned, 42 | { 43 | Ok(container 44 | .run(|| f().map_err(SerializableError::from)) 45 | .context("Sandbox container exited unexpectedly")??) 46 | } 47 | -------------------------------------------------------------------------------- /detcore/tests/lit/child_should_inherit_fds/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | // FIXME(T96027871): %hermit run --strict -- %me 12 | 13 | use close_err::Closable; 14 | use nix::sys::wait::WaitStatus; 15 | use nix::sys::wait::waitpid; 16 | use nix::unistd::ForkResult; 17 | use nix::unistd::fork; 18 | use nix::unistd::pipe; 19 | use nix::unistd::read; 20 | use nix::unistd::write; 21 | 22 | fn main() { 23 | let (fdread, fdwrite) = pipe().unwrap(); 24 | let msg: [u8; 8] = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; 25 | match unsafe { fork().unwrap() } { 26 | ForkResult::Parent { child, .. } => { 27 | fdwrite.close().expect("close failed"); 28 | let mut buf: [u8; 8] = [0; 8]; 29 | // XXX: The following SYS_read would timeout due to detcore issue. 30 | // add a 5 seconds timeout to abort. 31 | unsafe { libc::alarm(5) }; 32 | assert_eq!(read(&fdread, &mut buf), Ok(8)); 33 | assert_eq!(buf, msg); 34 | assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0))); 35 | unsafe { libc::syscall(libc::SYS_exit_group, 0) }; 36 | } 37 | ForkResult::Child => { 38 | fdread.close().expect("close failed"); 39 | assert_eq!(write(&fdwrite, &msg), Ok(8)); 40 | unsafe { libc::syscall(libc::SYS_exit_group, 0) }; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/standalone/validate_race_example_properties.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -euo pipefail 9 | 10 | if [ "$*" == "" ]; then 11 | hermit="hermit" 12 | else 13 | hermit="$1" 14 | fi 15 | 16 | dir=$(mktemp -d /tmp/validate_race_example_properties_XXXXX) 17 | log1="$dir/log1" 18 | log2="$dir/log2" 19 | log3="$dir/log3" 20 | log4="$dir/log4" 21 | log5="$dir/log5" 22 | log6="$dir/log6" 23 | 24 | function run() { 25 | local seed="$1" 26 | local schedseed="$2" 27 | local log="$3" 28 | "$hermit" --log=info run \ 29 | --base-env=empty --chaos --preemption-timeout=20000 --summary \ 30 | --seed="$seed" --sched-seed="$schedseed" examples/race.sh 200 2> "$log" 31 | } 32 | 33 | echo ":: Property 1: race.sh is invariant to changing --seed" 34 | run 0 1 "$log1" & 35 | run 10 1 "$log2" & 36 | wait 37 | "$hermit" log-diff --syscall-history=5 "$log1" "$log2" 38 | 39 | echo ":: Property 2: race.sh is sensitive to changing --sched-seed" 40 | run 0 1 "$log3" & 41 | run 0 2 "$log4" & 42 | wait 43 | if "$hermit" log-diff --syscall-history=5 "$log3" "$log4"; then 44 | echo "ERROR: expected difference in $log3 $log4" 45 | fi 46 | 47 | echo ":: Property 3: when both seeds are the same and we should see IDENTICAL runs:" 48 | run 0 1 "$log5" & 49 | run 0 1 "$log6" & 50 | wait 51 | "$hermit" log-diff --syscall-history=5 "$log5" "$log6" 52 | 53 | echo ":: Cleanup." 54 | rm -rf "$dir" 55 | echo ":: Test passed." 56 | -------------------------------------------------------------------------------- /detcore/scripts/get_syscall_support.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | import re 9 | import subprocess 10 | import sys 11 | 12 | hermit_cli = sys.argv[1] 13 | syscaller = sys.argv[2] 14 | 15 | total_syscalls = 332 16 | failures = 0 17 | for syscall_id in range(0, total_syscalls + 1): 18 | try: 19 | res = subprocess.run( 20 | [ 21 | hermit_cli, 22 | "run", 23 | "--panic-on-unsupported-syscalls", 24 | "--", 25 | syscaller, 26 | str(syscall_id), 27 | ], 28 | capture_output=True, 29 | timeout=2, 30 | ) 31 | except subprocess.TimeoutExpired: 32 | print(f"Pass || syscall: {syscall_id}") 33 | continue 34 | 35 | if res.returncode == 0: 36 | print(f"Pass || syscall: {syscall_id}") 37 | else: 38 | failed_at_syscall = None 39 | for line in res.stderr.split(b"\n"): 40 | if b"unsupported syscall:" in line: 41 | m = re.search(b"unsupported syscall: (.+?)\\(", line) 42 | if m: 43 | failed_at_syscall = m.group(1).decode("utf-8") 44 | 45 | if failed_at_syscall: 46 | failures += 1 47 | print(f"Fail || syscall: {syscall_id} -> {failed_at_syscall}") 48 | 49 | print(f"Failed syscalls: {failures}") 50 | print(f"Handled syscalls: {total_syscalls-failures}") 51 | -------------------------------------------------------------------------------- /hermit-cli/src/bin/hermit/record.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use clap::Parser; 10 | use hermit::Error; 11 | use reverie::process::ExitStatus; 12 | 13 | use super::global_opts::GlobalOpts; 14 | use crate::clean::CleanOpts; 15 | use crate::list::ListOpts; 16 | use crate::record_start::StartOpts; 17 | use crate::remove::RemoveOpts; 18 | 19 | /// Command-line options for the "record" subcommand. 20 | #[derive(Debug, Parser)] 21 | pub struct RecordOpts { 22 | ///Subcommands of record 23 | #[clap(subcommand)] 24 | record_commands: RecordCommand, 25 | } 26 | 27 | #[derive(Debug, Parser)] 28 | enum RecordCommand { 29 | /// List the available recordings. 30 | #[clap(name = "list", alias = "ls")] 31 | List(ListOpts), 32 | 33 | /// Delete a specific recording. 34 | #[clap(name = "rm", alias = "remove")] 35 | Remove(RemoveOpts), 36 | 37 | /// Cleans up all past recordings. 38 | #[clap(name = "clean")] 39 | Clean(CleanOpts), 40 | 41 | /// Start recording. 42 | #[clap(name = "start")] 43 | Start(StartOpts), 44 | } 45 | 46 | impl RecordOpts { 47 | pub fn main(&self, global: &GlobalOpts) -> Result { 48 | match &self.record_commands { 49 | RecordCommand::List(x) => x.main(global), 50 | RecordCommand::Remove(x) => x.main(global), 51 | RecordCommand::Clean(x) => x.main(global), 52 | RecordCommand::Start(x) => x.main(global), 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Hermit 2 | 3 | We want to make contributing to this project as easy and transparent as 4 | possible. 5 | 6 | ## Our Development Process 7 | 8 | Hermit is currently developed in Meta's internal repositories and then 9 | exported out to GitHub by a Meta team member; however, we invite you to 10 | submit pull requests as described below. 11 | 12 | ## Pull Requests 13 | 14 | We actively welcome your pull requests. 15 | 16 | 1. Fork the repo and create your branch from `main`. 17 | 2. If you've added code that should be tested, add tests. 18 | 3. If you've changed APIs, update the documentation. 19 | 4. Ensure the test suite passes. 20 | 5. Make sure your code lints. 21 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 22 | 23 | ## Contributor License Agreement ("CLA") 24 | 25 | In order to accept your pull request, we need you to submit a CLA. You only 26 | need to do this once to work on any of Meta's open source projects. 27 | 28 | Complete your CLA here: 29 | 30 | ## Issues 31 | 32 | We use GitHub issues to track public bugs. Please ensure your description is 33 | clear and has sufficient instructions to be able to reproduce the issue. 34 | 35 | Meta has a [bounty program](https://www.facebook.com/whitehat/) for the safe 36 | disclosure of security bugs. In those cases, please go through the process 37 | outlined on that page and do not file a public issue. 38 | 39 | ## Coding Style 40 | 41 | Follow the automatic `rustfmt` configuration. 42 | 43 | ## License 44 | 45 | By contributing to Hermit, you agree that your contributions will be 46 | licensed under the LICENSE file in the root directory of this source tree. 47 | -------------------------------------------------------------------------------- /tests/rust/print_clock_nanosleep_realtime_abs_race.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Race a (parent) thread sleeping, with one spinning/printing. 10 | mod test_utils; 11 | 12 | use crate::test_utils::sleep_race; 13 | 14 | fn run_test() -> u64 { 15 | sleep_race(|| { 16 | unsafe { 17 | let mut tp = libc::timespec { 18 | tv_sec: 0, 19 | tv_nsec: 100_000_000, // 100ms is just a random amount of time that makes the test fast to complete. 20 | }; 21 | // issue a raw clock_gettime syscall to the OS to get the current time 22 | libc::clock_gettime(libc::CLOCK_REALTIME, &mut tp); 23 | tp.tv_nsec += 100_000_000; // 100ms is just a random amount of time that makes the test fast to complete. 24 | if tp.tv_nsec / test_utils::NANOS_PER_SEC > 0 { 25 | tp.tv_sec += tp.tv_nsec / test_utils::NANOS_PER_SEC; 26 | tp.tv_nsec %= test_utils::NANOS_PER_SEC; 27 | } 28 | // issue a raw clock_nanosleep syscall to the OS 29 | libc::clock_nanosleep( 30 | libc::CLOCK_REALTIME, 31 | libc::TIMER_ABSTIME, 32 | &tp, 33 | std::ptr::null_mut(), 34 | ) 35 | }; 36 | }) 37 | } 38 | 39 | /// Raw execution exposes nondeterminism with this race. 40 | fn main() { 41 | let final_count = run_test(); 42 | assert!(final_count > 0); // Safe bet with a 100ms sleep. 43 | } 44 | -------------------------------------------------------------------------------- /docs/Users.md: -------------------------------------------------------------------------------- 1 | # Hermit Users Guide 2 | 3 | **Hermit is a a program sandbox for repeatability & concurrency testing.** 4 | 5 | Hermit, by the Hermetic Infra Team, launches programs in a special sandbox to control their execution. Hermit translates normal, nondeterministic Linux behavior, into deterministic, repeatable behavior. This can be used for various applications, including: 6 | 7 | - record-replay debugging, 8 | - simple reproducibility, 9 | - "chaos mode" to expose concurrency bugs in a controlled and repeatable way. 10 | 11 | Hermit is a middleware (or “sentry”) that sits between the guest process and the OS, accepting guest system call requests on one side, and producing sending system calls to the real Linux kernel on the other side. 12 | 13 | Hermit currently supports x86_64 Linux and can be run via: 14 | 15 | ``` 16 | ~/fbsource/fbcode/hermetic_infra/hermit/hermit 17 | ``` 18 | 19 | And here's a minimal demo of running inside hermit's deterministic environment: 20 | ``` 21 | $ cd ~/fbsource/fbcode/hermetic_infra/hermit/ 22 | $ ./examples/race.sh 23 | bbbbbbaa (... nondeterministic output...) 24 | $ ./hermit run --strict ./examples/race.sh 25 | abababab (... deterministic racing processes...) 26 | ``` 27 | 28 | ## Further reading 29 | 30 | * Find hermit [CLI examples here](https://fb.workplace.com/notes/hermetic-infra-fyi/hermit-tech-preview-a-linux-reproducibility-tool/244656753248444/) 31 | * See demos in [this tech talk](https://fb.workplace.com/groups/591973351138875/permalink/1132872253715646/). 32 | * [This talk](https://fb.workplace.com/groups/591973351138875/posts/1533285603674307) shows concurrency testing with hermit "chaos" mode. 33 | * [This talk](https://fb.workplace.com/groups/hermit.fyi/post 34 | -------------------------------------------------------------------------------- /tests/rust/print_clock_nanosleep_monotonic_abs_race.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Race a (parent) thread sleeping, with one spinning/printing. 10 | mod test_utils; 11 | 12 | use crate::test_utils::sleep_race; 13 | 14 | fn run_test() -> u64 { 15 | sleep_race(|| { 16 | unsafe { 17 | let mut tp = libc::timespec { 18 | tv_sec: 0, 19 | tv_nsec: 100_000_000, // 100ms is just a random amount of time that makes the test fast to complete. 20 | }; 21 | // issue a raw clock_gettime syscall to the OS to get the current time 22 | libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut tp); 23 | tp.tv_nsec += 100_000_000; // 100ms is just a random amount of time that makes the test fast to complete. 24 | if tp.tv_nsec / test_utils::NANOS_PER_SEC > 0 { 25 | tp.tv_sec += tp.tv_nsec / test_utils::NANOS_PER_SEC; 26 | tp.tv_nsec %= test_utils::NANOS_PER_SEC; 27 | } 28 | // issue a raw clock_nanosleep syscall to the OS 29 | libc::clock_nanosleep( 30 | libc::CLOCK_MONOTONIC, 31 | libc::TIMER_ABSTIME, 32 | &tp, 33 | std::ptr::null_mut(), 34 | ) 35 | }; 36 | }) 37 | } 38 | 39 | /// Raw execution exposes nondeterminism with this race. 40 | fn main() { 41 | let final_count = run_test(); 42 | assert!(final_count > 0); // Safe bet with a 100ms sleep. 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright notices are include in each source file. For other files, 2 | the below copyright applies: 3 | 4 | Copyright 2020-2022 (c) Meta Platforms, Inc. and affiliates. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | BSD 3-Clause License 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation and/or 16 | other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its contributors 19 | may be used to endorse or promote products derived from this software without 20 | specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 26 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 29 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /tests/util/chaos_stress_wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -euo pipefail 9 | 10 | hermit="$1" 11 | program="$2" 12 | preempt="$3" 13 | max_iters="${4:-1000}" 14 | 15 | RANDOM=42 # Set random seed 16 | 17 | perf list hardware | grep -i "Hardware event" > /dev/null || { 18 | >&2 echo "It seems that perf is not supported, so this chaos test will be skipped." 19 | >&2 echo "Chaos mode requires hardware performance counters." 20 | exit 0 21 | } 22 | 23 | run_count=1 24 | success_count=0 25 | fail_count=0 26 | 27 | # We're searching for failures in mostly-succeeding programs, but it's possible 28 | # hermit could get a bug which causes all chaos runs to fail. To detect this, we 29 | # run until we get at least one success and one failure. This should occur 30 | # within the max iterations, or within the test timeout 31 | 32 | function run_test_once { 33 | seed=$RANDOM 34 | >&2 echo "=========== Chaos run #$run_count ===========" 35 | if (set -x; time "$hermit" run --base-env=minimal --chaos --seed "$seed" --preemption-timeout "$preempt" -- "$program"); then 36 | ((success_count=success_count+1)) 37 | else 38 | ((fail_count=fail_count+1)) 39 | fi 40 | ((run_count=run_count+1)) 41 | } 42 | 43 | for ((run=1; run <= max_iters; run++)); do 44 | run_test_once 45 | if (( fail_count > 0 )) && (( success_count > 0 )); then 46 | >&2 echo "Found one success and one failure" 47 | exit 0 48 | fi 49 | done 50 | 51 | >&2 echo "Could not find one success and one failure." 52 | exit 1 53 | -------------------------------------------------------------------------------- /tests/c/getpid.c: -------------------------------------------------------------------------------- 1 | // @lint-ignore-every LICENSELINT 2 | /* 3 | Copyright (c) 2017-2019 joint authorship: 4 | 5 | * Baojun Wang 6 | * Joe Devietti 7 | * Kelly Shiptoski 8 | * Nicholas Renner 9 | * Omar Salvador Navarro Leija 10 | * Ryan Rhodes Newton 11 | * Ryan Glenn Scott 12 | 13 | MIT LICENSE: 14 | ------------------------- 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | int main(void) { 39 | pid_t pid = getpid(); 40 | printf("My pid: %d\n", pid); 41 | } 42 | -------------------------------------------------------------------------------- /detcore/tests/lit/dup2_existing_fd/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | use close_err::Closable; 12 | use nix::sys::wait::WaitStatus; 13 | use nix::sys::wait::waitpid; 14 | use nix::unistd::ForkResult; 15 | use nix::unistd::alarm; 16 | use nix::unistd::dup2_stdin; 17 | use nix::unistd::fork; 18 | use nix::unistd::pipe; 19 | use nix::unistd::read; 20 | use nix::unistd::write; 21 | 22 | fn main() { 23 | let (fdread, fdwrite) = pipe().unwrap(); 24 | 25 | let msg = [0x1u8, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; 26 | 27 | match unsafe { fork().unwrap() } { 28 | ForkResult::Parent { child, .. } => { 29 | fdwrite.close().expect("close failed"); 30 | let mut buf = [0u8; 8]; 31 | 32 | // If the file descriptor newfd was previously open, 33 | // it is silently closed before being reused. 34 | assert!(dup2_stdin(&fdread).is_ok()); 35 | 36 | // FIXME: The following SYS_read will timeout due to detcore 37 | // scheduler issue. Abort if we're still going in 5 seconds. 38 | alarm::set(5); 39 | 40 | assert_eq!(read(std::io::stdin(), &mut buf), Ok(msg.len())); 41 | assert_eq!(buf, msg); 42 | 43 | alarm::cancel(); 44 | 45 | assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0))); 46 | } 47 | ForkResult::Child => { 48 | fdread.close().expect("close failed"); 49 | assert_eq!(write(fdwrite, &msg), Ok(msg.len())); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /detcore/tests/lit/fstat/hermit-run-strict.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run -- %me | FileCheck %s 2 | CHECK: stat1: stat { 3 | CHECK-NEXT: st_dev: [[ST_DEV:[0-9]+]], 4 | CHECK-NEXT: st_ino: [[ST_INO:[0-9]+]], 5 | CHECK-NEXT: st_nlink: 1, 6 | CHECK-NEXT: st_mode: 33152, 7 | CHECK-NEXT: st_uid: 0, 8 | CHECK-NEXT: st_gid: 0, 9 | CHECK-NEXT: __pad0: 0, 10 | CHECK-NEXT: st_rdev: 0, 11 | CHECK-NEXT: st_size: 0, 12 | CHECK-NEXT: st_blksize: 4096, 13 | CHECK-NEXT: st_blocks: 0, 14 | CHECK-NEXT: st_atime: 1640995199, 15 | CHECK-NEXT: st_atime_nsec: 0, 16 | CHECK-NEXT: st_mtime: 1640995199, 17 | CHECK-NEXT: st_mtime_nsec: 0, 18 | CHECK-NEXT: st_ctime: 1640995199, 19 | CHECK-NEXT: st_ctime_nsec: 0, 20 | CHECK-NEXT: __unused: [ 21 | CHECK-NEXT: 0, 22 | CHECK-NEXT: 0, 23 | CHECK-NEXT: 0, 24 | CHECK-NEXT: ], 25 | CHECK-NEXT: } 26 | CHECK-NEXT: stat2: stat { 27 | CHECK-NEXT: st_dev: [[ST_DEV]], 28 | CHECK-NEXT: st_ino: [[ST_INO]], 29 | CHECK-NEXT: st_nlink: 1, 30 | CHECK-NEXT: st_mode: 33152, 31 | CHECK-NEXT: st_uid: 0, 32 | CHECK-NEXT: st_gid: 0, 33 | CHECK-NEXT: __pad0: 0, 34 | CHECK-NEXT: st_rdev: 0, 35 | CHECK-NEXT: st_size: 6, 36 | CHECK-NEXT: st_blksize: 4096, 37 | CHECK-NEXT: st_blocks: 8, 38 | CHECK-NEXT: st_atime: 1640995199, 39 | CHECK-NEXT: st_atime_nsec: 0, 40 | // FIXME: st_mtime and st_mtime_nsec should be deterministic, but they're not. 41 | CHECK-NEXT: st_mtime: {{([0-9]+)}}, 42 | CHECK-NEXT: st_mtime_nsec: {{([0-9]+)}}, 43 | CHECK-NEXT: st_ctime: 1640995199, 44 | CHECK-NEXT: st_ctime_nsec: 0, 45 | CHECK-NEXT: __unused: [ 46 | CHECK-NEXT: 0, 47 | CHECK-NEXT: 0, 48 | CHECK-NEXT: 0, 49 | CHECK-NEXT: ], 50 | CHECK-NEXT: } 51 | -------------------------------------------------------------------------------- /tests/standalone/replay_and_print_stacktraces.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -xeuo pipefail 9 | 10 | if [ "$*" == "" ]; then 11 | hermit="hermit" 12 | else 13 | hermit="$1" 14 | fi 15 | 16 | tmpdir=$(mktemp -d) 17 | log1=$(mktemp -p "$tmpdir") 18 | log2=$(mktemp -p "$tmpdir") 19 | 20 | function on_exit { 21 | rm -rf -- "$tmpdir" 22 | } 23 | # trap on_exit EXIT 24 | 25 | RUST_LOG=detcore=trace "$hermit" --log-file="$log1".log run --bind="$tmpdir" --base-env=minimal \ 26 | --record-preemptions-to="$log1".trace \ 27 | -- bash -c 'du -sch /usr/bin' 28 | 29 | stack1=$(mktemp -p "$tmpdir") 30 | stack2=$(mktemp -p "$tmpdir") 31 | 32 | # Print a couple arbitrary points inside this particular guest program. 33 | RUST_LOG=detcore=trace "$hermit" --log-file="$log2".log run --bind="$tmpdir" --base-env=minimal \ 34 | --replay-schedule-from="$log1".trace \ 35 | --stacktrace-event=10,"$stack1" --stacktrace-event=26,"$stack2" \ 36 | --record-preemptions-to="$log2".trace \ 37 | -- bash -c 'du -sch /usr/bin' 38 | 39 | wc "$log1".log "$log2".log "$log1".trace "$log2".trace 40 | 41 | grep "Trace loaded" "$log2".log 42 | 43 | if grep -s DESYNC "$log2".log; then 44 | echo "ERROR: Found DESYNC event on trace replay!" 45 | exit 1 46 | fi 47 | 48 | "$hermit" log-diff "$log1".log "$log2".log 49 | 50 | event1_ip=$(jq -r ".frames[0].frame.ip" "$stack1") 51 | event2_ip=$(jq -r ".frames[0].frame.ip" "$stack2") 52 | 53 | if [ "$event1_ip" -eq "$event2_ip" ]; then 54 | echo "ERROR: event stacktraces point to the same instruction"; 55 | exit 1 56 | fi 57 | 58 | echo "Test passed." 59 | -------------------------------------------------------------------------------- /tests/c/getCpu.c: -------------------------------------------------------------------------------- 1 | // @lint-ignore-every LICENSELINT 2 | /* 3 | Copyright (c) 2017-2019 joint authorship: 4 | 5 | * Baojun Wang 6 | * Joe Devietti 7 | * Kelly Shiptoski 8 | * Nicholas Renner 9 | * Omar Salvador Navarro Leija 10 | * Ryan Rhodes Newton 11 | * Ryan Glenn Scott 12 | 13 | MIT LICENSE: 14 | ------------------------- 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | int main() { 39 | int cpu = 0; 40 | int node = 0; 41 | syscall(SYS_getcpu, &cpu, &node, NULL); 42 | printf("CPU %d, Node %d\n", cpu, node); 43 | } 44 | -------------------------------------------------------------------------------- /detcore/tests/lit/utime/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | use std::ffi::CString; 12 | use std::io::Write; 13 | use std::mem::MaybeUninit; 14 | use std::os::unix::ffi::OsStrExt; 15 | 16 | use nix::sys::stat; 17 | use tempfile::NamedTempFile; 18 | 19 | fn main() { 20 | let mut start: MaybeUninit = MaybeUninit::uninit(); 21 | assert_eq!( 22 | unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, start.as_mut_ptr()) }, 23 | 0 24 | ); 25 | let start: libc::timespec = unsafe { start.assume_init() }; 26 | let mut file = NamedTempFile::new().unwrap(); 27 | assert!(writeln!(file, "hello, world!").is_ok()); 28 | 29 | let path = CString::new(file.path().as_os_str().as_bytes()).unwrap(); 30 | 31 | assert_eq! { 32 | unsafe { 33 | libc::utime(path.as_ptr(), std::ptr::null_mut()) 34 | }, 35 | 0 36 | } 37 | 38 | let statbuf = stat::stat(file.path()).unwrap(); 39 | 40 | assert!( 41 | statbuf.st_atime >= start.tv_sec, 42 | "{} should be >= {}", 43 | statbuf.st_atime, 44 | start.tv_sec 45 | ); 46 | assert!( 47 | statbuf.st_mtime >= start.tv_sec, 48 | "{} should be >= {}", 49 | statbuf.st_mtime, 50 | start.tv_sec 51 | ); 52 | 53 | let utimbuf = libc::utimbuf { 54 | actime: start.tv_sec + 1, 55 | modtime: start.tv_sec + 1, 56 | }; 57 | 58 | assert_eq! { 59 | unsafe { 60 | libc::utime(path.as_ptr(), &utimbuf as *const libc::utimbuf) 61 | }, 62 | 0 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /tests/c/simple/hello_nostdlib.c: -------------------------------------------------------------------------------- 1 | // @lint-ignore LICENSELINT 2 | // This simple test program is from: https://pastebin.com/4e73CAkW 3 | // 4 | // As per the terms of service (https://pastebin.com/doc_terms_of_service), 5 | // other users of pastebin are entitled to a limited license to the content 6 | // which should cover this use. 7 | // 8 | // The code is also posted here, and is probably not sufficiently unique to be 9 | // copyrightable: 10 | // 11 | // https://www.reddit.com/r/C_Programming/comments/se3kgi/hello_world_without_libc/ 12 | 13 | #define STDOUT 1 14 | 15 | #define SYS_write 1 16 | #define SYS_exit 60 17 | 18 | typedef unsigned long long int uint64; 19 | 20 | _Noreturn void exit(int code) { 21 | /* Infinite for-loop since this function can't return */ 22 | for (;;) { 23 | asm("mov %0, %%rax\n\t" 24 | "mov %1, %%rdi\n\t" 25 | "syscall\n\t" 26 | : 27 | : "r"((uint64)SYS_exit), "r"((uint64)code) 28 | : "%rax", "%rdi"); 29 | } 30 | } 31 | 32 | int write(int fd, const char* buf, int length) { 33 | int ret; 34 | 35 | asm("mov %1, %%rax\n\t" 36 | "mov %2, %%rdi\n\t" 37 | "mov %3, %%rsi\n\t" 38 | "mov %4, %%rdx\n\t" 39 | "syscall\n\t" 40 | "mov %%eax, %0" 41 | : "=r"(ret) 42 | : "r"((uint64)SYS_write), 43 | "r"((uint64)fd), 44 | "r"((uint64)buf), 45 | "r"((uint64)length) 46 | : "%rax", "%rdi", "%rsi", "%rdx"); 47 | 48 | return ret; 49 | } 50 | 51 | int strlength(const char* str) { 52 | const char* i = str; 53 | for (; *i; i++) 54 | ; 55 | return i - str; 56 | } 57 | 58 | int main(void) { 59 | const char* msg = "Hello, World!\n"; 60 | write(STDOUT, msg, strlength(msg)); 61 | return 0; 62 | } 63 | 64 | void _start(void) { 65 | int main_ret = main(); 66 | exit(main_ret); 67 | } 68 | -------------------------------------------------------------------------------- /detcore/tests/lit/fstat/hermit-run.lit: -------------------------------------------------------------------------------- 1 | RUN: %hermit run --no-sequentialize-threads --no-deterministic-io -- %me | FileCheck %s 2 | CHECK: stat1: stat { 3 | CHECK-NEXT: st_dev: [[ST_DEV:[0-9]+]], 4 | CHECK-NEXT: st_ino: [[ST_INO:[0-9]+]], 5 | CHECK-NEXT: st_nlink: 1, 6 | CHECK-NEXT: st_mode: 33152, 7 | CHECK-NEXT: st_uid: 0, 8 | CHECK-NEXT: st_gid: 0, 9 | CHECK-NEXT: __pad0: 0, 10 | CHECK-NEXT: st_rdev: 0, 11 | CHECK-NEXT: st_size: 0, 12 | CHECK-NEXT: st_blksize: 4096, 13 | CHECK-NEXT: st_blocks: 0, 14 | CHECK-NEXT: st_atime: 1640995199, 15 | CHECK-NEXT: st_atime_nsec: 0, 16 | CHECK-NEXT: st_mtime: 1640995199, 17 | CHECK-NEXT: st_mtime_nsec: 0, 18 | CHECK-NEXT: st_ctime: 1640995199, 19 | CHECK-NEXT: st_ctime_nsec: 0, 20 | CHECK-NEXT: __unused: [ 21 | CHECK-NEXT: 0, 22 | CHECK-NEXT: 0, 23 | CHECK-NEXT: 0, 24 | CHECK-NEXT: ], 25 | CHECK-NEXT: } 26 | CHECK-NEXT: stat2: stat { 27 | CHECK-NEXT: st_dev: [[ST_DEV]], 28 | CHECK-NEXT: st_ino: [[ST_INO]], 29 | CHECK-NEXT: st_nlink: 1, 30 | CHECK-NEXT: st_mode: 33152, 31 | CHECK-NEXT: st_uid: 0, 32 | CHECK-NEXT: st_gid: 0, 33 | CHECK-NEXT: __pad0: 0, 34 | CHECK-NEXT: st_rdev: 0, 35 | CHECK-NEXT: st_size: 6, 36 | CHECK-NEXT: st_blksize: 4096, 37 | CHECK-NEXT: st_blocks: 8, 38 | CHECK-NEXT: st_atime: 1640995199, 39 | CHECK-NEXT: st_atime_nsec: 0, 40 | // FIXME: st_mtime and st_mtime_nsec should be deterministic, but they're not. 41 | CHECK-NEXT: st_mtime: {{([0-9]+)}}, 42 | CHECK-NEXT: st_mtime_nsec: {{([0-9]+)}}, 43 | CHECK-NEXT: st_ctime: 1640995199, 44 | CHECK-NEXT: st_ctime_nsec: 0, 45 | CHECK-NEXT: __unused: [ 46 | CHECK-NEXT: 0, 47 | CHECK-NEXT: 0, 48 | CHECK-NEXT: 0, 49 | CHECK-NEXT: ], 50 | CHECK-NEXT: } 51 | -------------------------------------------------------------------------------- /tests/standalone/replay_chaos_trace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -euo pipefail 9 | 10 | # An example of running chaos and then relpaying the raw schedule. 11 | 12 | if [ "$*" == "" ]; then 13 | hermit="hermit" 14 | else 15 | hermit="$1" 16 | fi 17 | 18 | tmpdir=$(mktemp -d) 19 | log1=$(mktemp -p "$tmpdir") 20 | log2=$(mktemp -p "$tmpdir") 21 | 22 | function on_exit { 23 | rm -rf -- "$tmpdir" 24 | } 25 | trap on_exit EXIT 26 | 27 | RUST_LOG=detcore=trace "$hermit" --log-file="$log1".log run --bind="$tmpdir" --base-env=minimal --chaos \ 28 | --record-preemptions-to="$log1".trace \ 29 | -- bash -c 'find ./hermetic_infra/hermit/hermit-cli/src' 30 | 31 | RUST_LOG=detcore=trace "$hermit" --log-file="$log2".log run --bind="$tmpdir" --base-env=minimal \ 32 | --replay-schedule-from="$log1".trace \ 33 | --record-preemptions-to="$log2".trace \ 34 | -- bash -c 'find ./hermetic_infra/hermit/hermit-cli/src' 35 | 36 | wc "$log1".log "$log2".log "$log1".trace "$log2".trace 37 | 38 | grep "Trace loaded" "$log2".log 39 | 40 | if grep -s DESYNC "$log2".log; then 41 | echo "ERROR: Found DESYNC event on trace replay!" 42 | exit 1 43 | fi 44 | 45 | grep "finish syscall" "$log1".log | sed 's/.*detcore://' > "${log1}.syscalls" 46 | grep "finish syscall" "$log2".log | sed 's/.*detcore://' > "${log2}.syscalls" 47 | 48 | echo ":: Syscall counts:" 49 | wc -l "${log1}.syscalls" "${log2}.syscalls" 50 | 51 | if ! diff "${log1}.syscalls" "${log2}.syscalls"; then 52 | echo ":: ERROR: difference in linear syscall completion histories between trace/replay runs." 53 | exit 1 54 | fi 55 | 56 | echo "Test passed." 57 | -------------------------------------------------------------------------------- /docs/Developers/Architecture.md: -------------------------------------------------------------------------------- 1 | # Hermit System Achitecture 2 | 3 | Hermit is built out of a number of components found within the hermetic_infrastructure folder, 4 | corresponding to the directories `hermit`, `detcore`, and `reverie`. 5 | 6 | [Reverie](https://github.com/facebookexperimental/reverie) is the program-instrumentation layer, responsible for 7 | intercepting all system calls (and other events) from the guest and allowing injection of syscalls 8 | into the underlying Linux kernel. Each Reverie "tool" (or "plugin") is effectively a custom 9 | operating system, but it is at liberty to use the underlying Linux kernel rather than reimplement 10 | everything from scratch. (This arrangement is elsewhere called a [Sentry](https://gvisor.dev/docs/).) 11 | 12 | {{#plantuml: 13 | @startuml 14 | package "Hermit" { 15 | node Detcore 16 | node Reverie 17 | Detcore - Reverie 18 | } 19 | node "Linux Kernel" as linux 20 | node (Guest Program) as guest 21 | guest --> Reverie 22 | Reverie --> linux 23 | @enduml 24 | }} 25 | 26 | The command-line executable `hermit` is by instantiating Reverie with the Detcore tool. The Hermit 27 | binary has the job of setting up the container, and then Detcore takes over as the guest runs, 28 | intercepting its runtime events, maintaining state between events, and managing interaction with the 29 | underlying kernel. 30 | 31 | ## The global and local components of Detcore 32 | 33 | (Under Construction) 34 | 35 | {{#plantuml: 36 | @startuml 37 | node (Guest Events) as guest 38 | package "Detcore" { 39 | node tool_global 40 | node tool_local 41 | tool_global <-- tool_local: "rpc calls" 42 | } 43 | ' guest --> tool_local : "trap syscalls, etc" 44 | note bottom of guest 45 | trap syscalls, rdtsc/cpuid, signals, 46 | even timer-preemption events 47 | end note 48 | guest --> tool_local 49 | @enduml 50 | }} 51 | -------------------------------------------------------------------------------- /tests/standalone/minimal_hello_backtraces.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | if [[ -z "$HERMIT_BIN" ]]; then 9 | HERMIT_BIN="hermit" 10 | fi 11 | echo Running with minimal_hello binary at path = "$MINIMAL_HELLO" 12 | if [[ -z "$MINIMAL_HELLO" ]]; then 13 | echo "ERROR: needs MINIMAL_HELLO env var set to path of binary." 14 | exit 1 15 | fi 16 | if [[ -z "$TMPDIR" ]]; then 17 | TMPDIR="/tmp" 18 | fi 19 | set -eu 20 | 21 | summary=$(mktemp "$TMPDIR"/summary_XXXXXX.txt) 22 | 23 | echo ":: First run: count the number of events. " 24 | "$HERMIT_BIN" run --record-preemptions --summary "$MINIMAL_HELLO" 2> "$summary" 25 | echo ":: Run complete." 26 | 27 | events=$(grep -Eo -e "recorded [[:digit:]]+ events" "$summary" | sed 's/recorded //' | sed 's/ events//') 28 | echo ":: Counted events ${events}" 29 | 30 | args=() 31 | for ((ix = 0; ix < events; ix++)); do 32 | args+=("--stacktrace-event=${ix}") 33 | done 34 | 35 | echo ":: Now run and print stack traces for all those events" 36 | run2_out=$(mktemp "$TMPDIR"/run2_out_XXXXXX.txt) 37 | "$HERMIT_BIN" --log=info run --record-preemptions --summary "${args[@]}" "$MINIMAL_HELLO" 2> "$run2_out" 38 | echo ":: Run2 produced $(wc -l "$run2_out" | awk '{print $1}') lines of stderr output to ${run2_out}" 39 | 40 | num_stack_traces=$(grep -c "Printing stack trace for scheduled event" "$run2_out") 41 | echo ":: Observed ${num_stack_traces} stack trace print messages." 42 | 43 | if [ "${num_stack_traces}" != "${events}" ]; then 44 | echo "ERROR: ${num_stack_traces} did not match expected ${events}." 45 | exit 1 46 | else 47 | echo "PASSED." 48 | rm "$summary" "$run2_out" 49 | fi 50 | -------------------------------------------------------------------------------- /detcore/tests/lit/utimes/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me 10 | 11 | use std::ffi::CString; 12 | use std::io::Write; 13 | use std::mem::MaybeUninit; 14 | use std::os::unix::ffi::OsStrExt; 15 | 16 | use nix::sys::stat; 17 | use tempfile::NamedTempFile; 18 | 19 | fn main() { 20 | let mut start: MaybeUninit = MaybeUninit::uninit(); 21 | assert_eq!( 22 | unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, start.as_mut_ptr()) }, 23 | 0 24 | ); 25 | let start: libc::timespec = unsafe { start.assume_init() }; 26 | let mut file = NamedTempFile::new().unwrap(); 27 | assert!(writeln!(file, "hello, world!").is_ok()); 28 | 29 | let path = CString::new(file.path().as_os_str().as_bytes()).unwrap(); 30 | 31 | assert_eq! { 32 | unsafe { 33 | libc::utimes(path.as_ptr(), std::ptr::null_mut()) 34 | }, 35 | 0 36 | } 37 | 38 | let statbuf = stat::stat(file.path()).unwrap(); 39 | 40 | assert!( 41 | statbuf.st_atime >= start.tv_sec, 42 | "{} should be >= {}", 43 | statbuf.st_atime, 44 | start.tv_sec 45 | ); 46 | assert!( 47 | statbuf.st_mtime >= start.tv_sec, 48 | "{} should be >= {}", 49 | statbuf.st_mtime, 50 | start.tv_sec 51 | ); 52 | 53 | let next = libc::timeval { 54 | tv_sec: 1 + start.tv_sec, 55 | tv_usec: start.tv_nsec / 1000, 56 | }; 57 | 58 | let tvs = [next, next]; 59 | 60 | assert_eq! { 61 | unsafe { 62 | libc::utimes(path.as_ptr(), &tvs as *const _) 63 | }, 64 | 0 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /detcore/tests/lit/rt_sigprocmask/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | /* 10 | RUN: %me mask | FileCheck --check-prefix=NORMAL1 %s 11 | NORMAL1: SIGHUP is masked 12 | NORMAL1-NEXT: SIGSTKFLT is masked 13 | 14 | RUN: %me block | FileCheck --check-prefix=NORMAL2 %s 15 | NORMAL2: SIGHUP is masked 16 | NORMAL2-NEXT: SIGSTKFLT is masked 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | static void checksig(const char* desc, const sigset_t* set, int sig) { 26 | printf("%s %s masked\n", desc, sigismember(set, sig) ? "is" : "is not"); 27 | } 28 | 29 | static void block(void) { 30 | sigset_t set, oldset; 31 | 32 | sigfillset(&set); 33 | 34 | assert(sigprocmask(SIG_BLOCK, &set, NULL) == 0); 35 | assert(sigprocmask(SIG_BLOCK, NULL, &oldset) == 0); 36 | 37 | checksig("SIGHUP", &oldset, SIGHUP); 38 | checksig("SIGSTKFLT", &oldset, SIGSTKFLT); 39 | } 40 | 41 | static void mask(void) { 42 | sigset_t set, oldset; 43 | 44 | sigfillset(&set); 45 | 46 | assert(sigprocmask(SIG_SETMASK, &set, NULL) == 0); 47 | assert(sigprocmask(SIG_SETMASK, NULL, &oldset) == 0); 48 | 49 | checksig("SIGHUP", &oldset, SIGHUP); 50 | checksig("SIGSTKFLT", &oldset, SIGSTKFLT); 51 | } 52 | 53 | int main(int argc, char* argv[]) { 54 | if (argc != 2) { 55 | fprintf(stderr, "%s [block | mask]", argv[0]); 56 | exit(1); 57 | } 58 | 59 | if (strcmp(argv[1], "block") == 0) { 60 | block(); 61 | } else if (strcmp(argv[1], "mask") == 0) { 62 | mask(); 63 | } else { 64 | fprintf(stderr, "%s [block | mask]", argv[0]); 65 | exit(1); 66 | } 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /hermit-cli/src/replayer/time.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use reverie::Errno; 10 | use reverie::Guest; 11 | use reverie::syscalls::ClockGettime; 12 | use reverie::syscalls::Gettimeofday; 13 | use reverie::syscalls::MemoryAccess; 14 | use reverie::syscalls::Time; 15 | 16 | use super::Replayer; 17 | 18 | impl Replayer { 19 | pub(super) async fn handle_clock_gettime>( 20 | &self, 21 | guest: &mut G, 22 | syscall: ClockGettime, 23 | ) -> Result { 24 | let addr = syscall.tp().ok_or(Errno::EFAULT)?; 25 | 26 | next_event!(guest, Timespec).and_then(|event| { 27 | guest.memory().write_value(addr, &event.timespec)?; 28 | 29 | // clock_gettime always returns 0 on success. 30 | Ok(0) 31 | }) 32 | } 33 | 34 | pub(super) async fn handle_time>( 35 | &self, 36 | guest: &mut G, 37 | syscall: Time, 38 | ) -> Result { 39 | let time = next_event!(guest, Return)?; 40 | 41 | if let Some(addr) = syscall.tloc() { 42 | guest.memory().write_value(addr, &time)?; 43 | } 44 | 45 | Ok(time) 46 | } 47 | 48 | pub(super) async fn handle_gettimeofday>( 49 | &self, 50 | guest: &mut G, 51 | syscall: Gettimeofday, 52 | ) -> Result { 53 | let (tv, tz) = next_event!(guest, Timeofday)?; 54 | 55 | if let Some(addr) = syscall.tv() { 56 | guest.memory().write_value(addr, &tv)?; 57 | } 58 | 59 | if let Some(addr) = syscall.tz() { 60 | guest.memory().write_value(addr, &tz)?; 61 | } 62 | 63 | Ok(0) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/rust/print_nanosleep_race.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Race a (parent) thread sleeping, with one spinning/printing. 10 | 11 | use std::io; 12 | use std::io::Write; 13 | use std::sync::Arc; 14 | use std::sync::atomic::AtomicUsize; 15 | use std::sync::atomic::Ordering; 16 | 17 | fn run_test() -> u64 { 18 | let flag = Arc::new(AtomicUsize::new(0)); 19 | let flag2 = flag.clone(); 20 | // This work will accomplish an arbitrary number of iterations while the parent thread 21 | // sleeps: 22 | let child = std::thread::spawn(move || { 23 | let mut count = 0; 24 | let mut spurious = 0.00000001; 25 | while flag2.load(Ordering::SeqCst) == 0 { 26 | print!("{} ", count); 27 | io::stdout().flush().unwrap(); 28 | for _ in 0..50000 { 29 | count += 1; 30 | spurious += 0.000001; 31 | if flag2.load(Ordering::SeqCst) == 1 { 32 | break; 33 | } 34 | } 35 | } 36 | (count, spurious) 37 | }); 38 | 39 | let tp = libc::timespec { 40 | tv_sec: 0, 41 | tv_nsec: 100_000_000, 42 | }; 43 | unsafe { 44 | // issue a raw nanosleep syscall to the OS 45 | libc::nanosleep(&tp, std::ptr::null_mut()) 46 | }; 47 | 48 | flag.store(1, Ordering::SeqCst); 49 | let (final_count, spurious) = child.join().unwrap(); 50 | println!("\n Final count: {}", final_count); 51 | println!("\n Silly number: {}", spurious); 52 | final_count 53 | } 54 | 55 | /// Raw execution exposes nondeterminism with this race. 56 | fn main() { 57 | let final_count = run_test(); 58 | assert!(final_count > 0); // Safe bet with a 100ms sleep. 59 | } 60 | -------------------------------------------------------------------------------- /tests/chaos/nanosleep-threads-nocrash-rust.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::sync::Arc; 10 | /// Let two child threads race, and arbitrarily define one order as correct and the other as error. 11 | /// This is roughly the same as the C version by the same name, but Rust generates different code 12 | /// provides us another test and in particular another test of stacktrace printing. 13 | use std::sync::atomic::AtomicUsize; 14 | use std::sync::atomic::Ordering::SeqCst; 15 | 16 | fn sleep(milli: i64) { 17 | let tp = libc::timespec { 18 | tv_sec: milli / 1_000, 19 | tv_nsec: milli % 1_000 * 1_000_000, 20 | }; 21 | unsafe { 22 | // issue a raw nanosleep syscall to the OS 23 | libc::nanosleep(&tp, std::ptr::null_mut()) 24 | }; 25 | } 26 | 27 | #[inline(never)] 28 | fn mythread1_fun(var: Arc) { 29 | sleep(1000); 30 | var.store(1, SeqCst); 31 | } 32 | 33 | #[inline(never)] 34 | fn mythread2_fun(var: Arc) { 35 | sleep(1000); 36 | var.store(2, SeqCst); 37 | } 38 | 39 | fn main() { 40 | let d = Arc::new(AtomicUsize::new(0)); 41 | let d1 = Arc::clone(&d); 42 | let d2 = Arc::clone(&d); 43 | 44 | let h1 = std::thread::Builder::new() 45 | .name("mythread1".to_string()) 46 | .spawn(move || mythread1_fun(d1)) 47 | .unwrap(); 48 | 49 | let h2 = std::thread::Builder::new() 50 | .name("mythread2".to_string()) 51 | .spawn(move || mythread2_fun(d2)) 52 | .unwrap(); 53 | 54 | // mythread1(d1); 55 | h1.join().unwrap(); 56 | h2.join().unwrap(); 57 | let final_val = d.load(SeqCst); 58 | println!("Final value: {}", final_val); 59 | if final_val == 2 { 60 | std::process::exit(0); 61 | } else { 62 | std::process::exit(1); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /detcore/src/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! Widely useful small utilities. 10 | 11 | use std::time::Duration; 12 | 13 | use crate::types::NANOS_PER_RCB; 14 | 15 | #[allow(dead_code)] 16 | /// A simple debugging helper function that makes it easy to printf-debug through 17 | /// layers of stdout/stderr caputure, such as when running under buck test/tpx. 18 | pub fn punch_out_print(msg: &str) { 19 | use std::io::Write; 20 | // TODO: if we want this to be more performant, we can have a lazy static 21 | // global file handle for this. This, however, keeps it simple for occasional usage.œ 22 | if let Ok(mut tty) = std::fs::OpenOptions::new() 23 | .read(true) 24 | .write(true) 25 | .open("/dev/tty") 26 | { 27 | writeln!(tty, "{}", msg).unwrap(); 28 | } else { 29 | // If devtty doesn't exist, we just use stderr. 30 | eprintln!("{}", msg); 31 | } 32 | } 33 | /// A helper function to convert a number of Retired Conditional Branches (RCBS) into 34 | /// a `std::time::Duration` via the `NANOS_PER_RCB` defined in ` types.rs`. 35 | pub fn rcbs_to_duration(rcbs: u64) -> Duration { 36 | Duration::from_nanos((rcbs as f64 * NANOS_PER_RCB) as u64) 37 | } 38 | 39 | /// A little better than the builtin string truncation in format strings, because it includes ellipses. 40 | // TODO: There should be some advanced solution for printing potentially huge things that 41 | // doesn't actually render them all... 42 | pub fn truncated(width: usize, mut s: String) -> String { 43 | if s.len() > width { 44 | if width >= 3 { 45 | s.truncate(width - 3); 46 | s.push_str("..."); 47 | s 48 | } else { 49 | s.truncate(width); 50 | s 51 | } 52 | } else { 53 | s 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /flaky-tests/cas_sequence_easy.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! This explicitly checks that many schedules are explored. It only fails 10 | //! if a specific interleaving occurs via a CAS handoff. 11 | 12 | use std::ptr::read_volatile; 13 | use std::sync::Arc; 14 | use std::sync::atomic::AtomicUsize; 15 | use std::sync::atomic::Ordering::SeqCst; 16 | 17 | const WORK_AMT: usize = 10000; 18 | const FINAL_VALUE: usize = 4; 19 | 20 | #[inline(never)] 21 | fn do_work(iters: usize) { 22 | let mut x = iters; 23 | while unsafe { read_volatile(&x as *const _) } > 0usize { 24 | x -= 1; 25 | } 26 | } 27 | 28 | #[inline(never)] 29 | fn take_from(a: &AtomicUsize, from: usize, to: usize) { 30 | a.compare_exchange(from, to, SeqCst, SeqCst).ok(); 31 | } 32 | 33 | #[inline(never)] 34 | fn thread1(var: Arc) { 35 | do_work(5 * WORK_AMT); 36 | take_from(&var, 2, 3); 37 | } 38 | 39 | #[inline(never)] 40 | fn thread2(var: Arc) { 41 | take_from(&var, 1, 2); 42 | do_work(25 * WORK_AMT); // <- preemption must occur here, not just starvation 43 | take_from(&var, 3, FINAL_VALUE); 44 | } 45 | 46 | fn main() { 47 | let d = Arc::new(AtomicUsize::new(1)); 48 | 49 | let d1 = Arc::clone(&d); 50 | let d2 = Arc::clone(&d); 51 | 52 | const FINAL_VALUE: usize = 4; 53 | 54 | let h1 = std::thread::spawn(move || thread1(d1)); 55 | let h2 = std::thread::spawn(move || thread2(d2)); 56 | 57 | h1.join().unwrap(); 58 | h2.join().unwrap(); 59 | 60 | let val = Arc::try_unwrap(d).unwrap().into_inner(); 61 | println!("Final value: {}", val); 62 | if val == FINAL_VALUE { 63 | println!("Antagonistic schedule reached, failing."); 64 | std::process::exit(1); 65 | } 66 | println!("Did not find antagonistic schedule. Succeeding."); 67 | } 68 | -------------------------------------------------------------------------------- /tests/chaos/cas_sequence.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! This explicitly checks that many schedules are explored. It only fails 10 | //! if a specific interleaving occurs via a CAS handoff. 11 | 12 | use std::ptr::read_volatile; 13 | use std::sync::Arc; 14 | use std::sync::atomic::AtomicUsize; 15 | use std::sync::atomic::Ordering::SeqCst; 16 | 17 | const WORK_AMT: f64 = 20000.0; 18 | const FINAL_VALUE: usize = 4; 19 | 20 | #[inline(never)] 21 | fn do_work(iters: f64) { 22 | let mut x = iters as usize; 23 | while unsafe { read_volatile(&x as *const _) } > 0usize { 24 | x -= 1; 25 | } 26 | } 27 | 28 | #[inline(never)] 29 | fn take_from(a: &AtomicUsize, from: usize, to: usize) { 30 | a.compare_exchange(from, to, SeqCst, SeqCst).ok(); 31 | } 32 | 33 | #[inline(never)] 34 | fn thread1(var: Arc) { 35 | do_work(WORK_AMT * 3.0); 36 | take_from(&var, 2, 3); 37 | } 38 | 39 | #[inline(never)] 40 | fn thread2(var: Arc) { 41 | take_from(&var, 1, 2); 42 | do_work(WORK_AMT); // <- preemption must occur here, not just starvation 43 | take_from(&var, 3, FINAL_VALUE); 44 | } 45 | 46 | fn main() { 47 | let d = Arc::new(AtomicUsize::new(1)); 48 | 49 | let d1 = Arc::clone(&d); 50 | let d2 = Arc::clone(&d); 51 | 52 | const FINAL_VALUE: usize = 4; 53 | 54 | let h1 = std::thread::spawn(move || thread1(d1)); 55 | let h2 = std::thread::spawn(move || thread2(d2)); 56 | 57 | h1.join().unwrap(); 58 | h2.join().unwrap(); 59 | 60 | let val = Arc::try_unwrap(d).unwrap().into_inner(); 61 | println!("Final value: {}", val); 62 | if val == FINAL_VALUE { 63 | println!("Antagonistic schedule reached, failing."); 64 | std::process::exit(1); 65 | } 66 | println!("Did not find antagonistic schedule. Succeeding."); 67 | } 68 | -------------------------------------------------------------------------------- /hermit-cli/src/interp.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::ffi::OsStr; 10 | use std::fs; 11 | use std::io::Read; 12 | use std::os::unix::ffi::OsStrExt; 13 | use std::path::Path; 14 | use std::path::PathBuf; 15 | 16 | use goblin::container::Ctx; 17 | use goblin::elf::Elf; 18 | use goblin::elf::ProgramHeader; 19 | use goblin::elf::program_header; 20 | 21 | // minimal size required to parse elf's .interp section. 22 | const ELF_MIN_SIZE: usize = 8192; 23 | 24 | /// Get the right ld.so from elf's interp section. 25 | pub fn elf_get_interp>(elf: P) -> Option { 26 | let mut buffer = Vec::new(); 27 | let nb = fs::File::open(elf) 28 | .and_then(|f| { 29 | let mut handle = f.take(ELF_MIN_SIZE as u64); 30 | handle.read_to_end(&mut buffer) 31 | }) 32 | .ok()?; 33 | 34 | let header = Elf::parse_header(&buffer[..nb]).ok()?; 35 | let mut elf = Elf::lazy_parse(header).ok()?; 36 | let ctx = Ctx { 37 | le: header.endianness().ok()?, 38 | container: header.container().ok()?, 39 | }; 40 | 41 | // parse and assemble the program headers 42 | elf.program_headers = ProgramHeader::parse( 43 | &buffer, 44 | header.e_phoff as usize, 45 | header.e_phnum as usize, 46 | ctx, 47 | ) 48 | .ok()?; 49 | 50 | let mut interp: Option<&OsStr> = None; 51 | for ph in &elf.program_headers { 52 | // read in interpreter segment 53 | if ph.p_type == program_header::PT_INTERP && ph.p_filesz != 0 { 54 | let size = ph.p_filesz as usize; 55 | let offset = ph.p_offset as usize; 56 | // skip trailing '\0'. 57 | interp = Some(OsStr::from_bytes(&buffer[offset..offset + size - 1])); 58 | break; 59 | } 60 | } 61 | 62 | interp.map(PathBuf::from) 63 | } 64 | -------------------------------------------------------------------------------- /tests/chaos/keyvalue.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | //! The second thread expects to have the first's writes to the keyvalue service 10 | //! be visible because it does lots of work first, but this ordering isn't 11 | //! actually enforced. 12 | 13 | use std::sync::mpsc::Receiver; 14 | use std::sync::mpsc::Sender; 15 | 16 | type Key = u64; 17 | type Value = u64; 18 | 19 | fn keyvalue_service(store: Receiver<(Key, Value)>, rx: Receiver, tx: Sender>) { 20 | let mut kv = std::collections::HashMap::new(); 21 | loop { 22 | match store.try_recv() { 23 | Ok((k, v)) => kv.insert(k, v), 24 | _ => None, 25 | }; 26 | match rx.try_recv() { 27 | Ok(k) => tx.send(kv.get(&k).copied()).ok(), 28 | _ => None, 29 | }; 30 | std::thread::sleep(std::time::Duration::from_micros(1)); 31 | } 32 | } 33 | 34 | fn main() { 35 | let (store_tx1, store_rx) = std::sync::mpsc::channel(); 36 | let (req_tx, req_rx) = std::sync::mpsc::channel(); 37 | let (resp_tx, resp_rx) = std::sync::mpsc::channel(); 38 | let store_tx2 = store_tx1.clone(); 39 | 40 | let _h1 = std::thread::spawn(move || { 41 | keyvalue_service(store_rx, req_rx, resp_tx); 42 | }); 43 | 44 | let h2 = std::thread::spawn(move || { 45 | store_tx1.send((1, 10)).unwrap(); 46 | store_tx1.send((2, 20)).unwrap(); 47 | store_tx1.send((3, 30)).unwrap(); 48 | }); 49 | 50 | let h3 = std::thread::spawn(move || { 51 | for i in 10..10000 { 52 | store_tx2.send((i, i * 10)).unwrap(); 53 | } 54 | req_tx.send(2).unwrap(); 55 | let resp = resp_rx.recv().unwrap(); 56 | assert_eq!(resp, Some(20)) 57 | }); 58 | 59 | h2.join().unwrap(); 60 | h3.join().unwrap(); 61 | // Let h1 get cancelled 62 | println!("Success!"); 63 | } 64 | -------------------------------------------------------------------------------- /tests/rust/interrogate_tty.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | /*! 10 | 11 | Run with rust-script if you like: 12 | 13 | ```cargo 14 | [dependencies] 15 | libc = "0.2.94" 16 | ``` 17 | 18 | */ 19 | 20 | use std::fs; 21 | use std::io::Write; 22 | use std::os::unix::io::AsRawFd; 23 | 24 | use nix::sys::stat::fstat; 25 | 26 | fn main() { 27 | println!("Hello stdout."); 28 | eprintln!("Hello stderr."); 29 | 30 | // TODO: all the below println!s were previously writeln!s to this stdout handle (fd 4). 31 | // This currently causes a bug with record/replay where the output is not produced on replay. 32 | // rr is able to replay the output, and dealias the fds to realize it's actually stdout. 33 | // let mut stdout = fs::OpenOptions::new() 34 | // .read(true) 35 | // .write(true) 36 | // .open("/proc/self/fd/1") 37 | // .unwrap(); 38 | 39 | unsafe { 40 | // writeln!(stdout, "stdin isatty = {:?}", libc::isatty(0)).unwrap(); 41 | println!("stdin isatty = {:?}", libc::isatty(0)); 42 | println!("stdout isatty = {:?}", libc::isatty(1)); 43 | println!("stderr isatty = {:?}", libc::isatty(2)); 44 | } 45 | println!("fstat(0) = {:?}", fstat(std::io::stdin())); 46 | println!("fstat(1) = {:?}", fstat(std::io::stdout())); 47 | println!("fstat(2) = {:?}", fstat(std::io::stderr())); 48 | 49 | if let Ok(mut tty) = fs::OpenOptions::new() 50 | .read(true) 51 | .write(true) 52 | .open("/dev/tty") 53 | { 54 | writeln!(tty, "Hello TTY").unwrap(); 55 | unsafe { 56 | println!("/dev/tty isatty = {:?}", libc::isatty(tty.as_raw_fd())); 57 | } 58 | println!("fstat(/dev/tty) = {:?}", fstat(&tty)); 59 | } else { 60 | eprintln!("WARNING: This machine does not have /dev/tty available. Skipping part of test."); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/c/simple/racewrite_nostdlib.c: -------------------------------------------------------------------------------- 1 | // @lint-ignore LICENSELINT 2 | // This test program is based on a simple hello world from: 3 | // https://pastebin.com/4e73CAkW 4 | // 5 | // As per the terms of service (https://pastebin.com/doc_terms_of_service), 6 | // other users of pastebin are entitled to a limited license to the content 7 | // which should cover this use. 8 | // 9 | // The code is also posted here, and is probably not sufficiently unique to be 10 | // copyrightable: 11 | // 12 | // https://www.reddit.com/r/C_Programming/comments/se3kgi/hello_world_without_libc/ 13 | 14 | #define STDOUT 1 15 | #define SYS_write 1 16 | #define SYS_exit 60 17 | 18 | typedef unsigned long long int uint64; 19 | 20 | _Noreturn void exit(int code) { 21 | /* Infinite for-loop since this function can't return */ 22 | for (;;) { 23 | asm("mov %0, %%rax\n\t" 24 | "mov %1, %%rdi\n\t" 25 | "syscall\n\t" 26 | : 27 | : "r"((uint64)SYS_exit), "r"((uint64)code) 28 | : "%rax", "%rdi"); 29 | } 30 | } 31 | 32 | int write(int fd, const char* buf, int length) { 33 | int ret; 34 | 35 | asm("mov %1, %%rax\n\t" 36 | "mov %2, %%rdi\n\t" 37 | "mov %3, %%rsi\n\t" 38 | "mov %4, %%rdx\n\t" 39 | "syscall\n\t" 40 | "mov %%eax, %0" 41 | : "=r"(ret) 42 | : "r"((uint64)SYS_write), 43 | "r"((uint64)fd), 44 | "r"((uint64)buf), 45 | "r"((uint64)length) 46 | : "%rax", "%rdi", "%rsi", "%rdx"); 47 | 48 | return ret; 49 | } 50 | 51 | int fork() { 52 | int ret; 53 | asm("movl $57, %%eax\n\t" // Fork syscall number. 54 | "syscall\n\t" 55 | "mov %%eax, %0" 56 | : "=r"(ret)); 57 | return ret; 58 | } 59 | 60 | int strlength(const char* str) { 61 | const char* i = str; 62 | for (; *i; i++) 63 | ; 64 | return i - str; 65 | } 66 | 67 | int main(void) { 68 | char* msg; 69 | if (fork()) { 70 | msg = "foo"; 71 | } else { 72 | msg = "bar"; 73 | } 74 | write(STDOUT, msg, strlength(msg)); 75 | 76 | return 0; 77 | } 78 | 79 | void _start(void) { 80 | int main_ret = main(); 81 | exit(main_ret); 82 | } 83 | -------------------------------------------------------------------------------- /tests/c/uname.c: -------------------------------------------------------------------------------- 1 | // @lint-ignore-every LICENSELINT 2 | /* 3 | Copyright (c) 2017-2019 joint authorship: 4 | 5 | * Baojun Wang 6 | * Joe Devietti 7 | * Kelly Shiptoski 8 | * Nicholas Renner 9 | * Omar Salvador Navarro Leija 10 | * Ryan Rhodes Newton 11 | * Ryan Glenn Scott 12 | 13 | MIT LICENSE: 14 | ------------------------- 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | int main() { 40 | struct utsname buf; 41 | int ret = uname(&buf); 42 | if (ret == -1) { 43 | printf("Uname failed\nReason: %s\n", strerror(errno)); 44 | } 45 | 46 | printf("Operating name: %s\n", buf.sysname); 47 | printf("Node name: %s\n", buf.nodename); 48 | printf("Operating system release: %s\n", buf.release); 49 | printf("Operating system version: %s\n", buf.version); 50 | printf("Hardware identifier: %s\n", buf.machine); 51 | } 52 | -------------------------------------------------------------------------------- /hermit-verify/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use anyhow::Result; 10 | use clap::Parser; 11 | use clap::Subcommand; 12 | 13 | mod chaos_replay; 14 | mod chaos_stress; 15 | mod cli_wrapper; 16 | mod common; 17 | mod run; 18 | mod schedule_trace; 19 | mod trace_replay; 20 | mod use_case; 21 | 22 | use colored::*; 23 | pub use common::CommonOpts; 24 | use use_case::run_use_case; 25 | 26 | #[derive(Parser, Debug)] 27 | #[clap(author = "oncall+hermit@xmail.facebook.com")] 28 | struct Args { 29 | #[clap(subcommand)] 30 | command: Commands, 31 | 32 | #[clap(flatten)] 33 | common_opts: CommonOpts, 34 | } 35 | 36 | #[derive(Subcommand, Debug)] 37 | pub enum Commands { 38 | Run(run::RunOpts), 39 | TraceReplay(trace_replay::TraceReplayOpts), 40 | ChaosReplay(chaos_replay::ChaosReplayOpts), 41 | /// Schedule Trace 42 | #[clap(subcommand)] 43 | SchedTrace(schedule_trace::SchedTraceOpts), 44 | ChaosStress(chaos_stress::ChaosStressOpts), 45 | } 46 | 47 | #[fbinit::main] 48 | fn main() -> Result<()> { 49 | let Args { 50 | command, 51 | common_opts, 52 | } = Args::from_args(); 53 | 54 | let mut print_success = true; 55 | let result = match command { 56 | Commands::Run(cmd) => run_use_case(cmd, &common_opts)?, 57 | Commands::TraceReplay(cmd) => run_use_case(cmd, &common_opts)?, 58 | Commands::ChaosReplay(cmd) => run_use_case(cmd, &common_opts)?, 59 | Commands::ChaosStress(cmd) => cmd.run(&common_opts)?, 60 | Commands::SchedTrace(cmd) => { 61 | print_success = false; 62 | cmd.main(&common_opts)? 63 | } 64 | }; 65 | 66 | if !result { 67 | println!("{}", "Verification use case failed!".red().bold()); 68 | anyhow::bail!("Verification check failed") 69 | } else if print_success { 70 | println!("{}", "Success!".green().bold()); 71 | }; 72 | 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /flaky-tests/use_configurable_flaky_service.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | const char* FLAKY_SERVICE_APP = "3355159521469756"; 16 | const char* FLAKY_SERVICE_TOKEN = "AeOr7DicKuQPHlMinzo"; 17 | const size_t FLAKY_SERVICE_RESPONSE_BUFFER_SIZE = 1024; 18 | 19 | std::string getFlakyServiceURL(); 20 | size_t writeResponseData(char* ptr, size_t size, size_t nmemb, void* buffer); 21 | 22 | // Talks to a service that may be flaky and return an error with an exception 23 | // trace. The client code does not properly handle large errors due to a low 24 | // buffer size, causing a crash. 25 | TEST(UseConfigurableFlakyService, request) { 26 | auto url = getFlakyServiceURL(); 27 | auto postFields = 28 | folly::sformat("app={}&token={}", FLAKY_SERVICE_APP, FLAKY_SERVICE_TOKEN); 29 | char buffer[FLAKY_SERVICE_RESPONSE_BUFFER_SIZE]; 30 | 31 | curl_global_init(CURL_GLOBAL_DEFAULT); 32 | 33 | auto curl = curl_easy_init(); 34 | EXPECT_NE(curl, nullptr); 35 | 36 | curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); 37 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postFields.c_str()); 38 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeResponseData); 39 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); 40 | 41 | auto result = curl_easy_perform(curl); 42 | EXPECT_EQ(result, CURLE_OK); 43 | 44 | curl_easy_cleanup(curl); 45 | curl_global_cleanup(); 46 | } 47 | 48 | std::string getFlakyServiceURL() { 49 | auto od = std::getenv("OD"); 50 | auto tier = od != nullptr ? od : "intern"; 51 | return folly::sformat( 52 | "https://interngraph.{}.facebook.com/hermetic_infra/flaky_service", tier); 53 | } 54 | 55 | size_t writeResponseData(char* ptr, size_t size, size_t nmemb, void* buffer) { 56 | auto realSize = size * nmemb; 57 | std::memcpy(buffer, ptr, realSize); 58 | return realSize; 59 | } 60 | -------------------------------------------------------------------------------- /detcore-model/src/pid.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::fmt; 10 | use std::str::FromStr; 11 | 12 | use nix::unistd; 13 | use serde::Deserialize; 14 | use serde::Serialize; 15 | 16 | // Deterministic Pids/Tids: 17 | //-------------------------------------------------------------------------------- 18 | 19 | /// Deterministic "virtual" version of `reverie::Pid` 20 | #[derive( 21 | PartialEq, // Silly protection from rustfmt disagreements. 22 | Debug, 23 | Eq, 24 | Clone, 25 | Copy, 26 | Hash, 27 | PartialOrd, 28 | Ord, 29 | Serialize, 30 | Deserialize, 31 | Default, 32 | )] 33 | pub struct DetPid(i32); 34 | 35 | impl fmt::Display for DetPid { 36 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 37 | fmt::Display::fmt(&self.0, f) 38 | } 39 | } 40 | 41 | #[cfg(disabled)] 42 | impl From for DetPid { 43 | fn from(p: reverie_syscalls::Pid) -> Self { 44 | DetPid(p.into()) 45 | } 46 | } 47 | 48 | impl From for DetPid { 49 | fn from(p: unistd::Pid) -> Self { 50 | DetPid(p.into()) 51 | } 52 | } 53 | 54 | // implementing From for unistd::Pid would violate foreign trait rules 55 | #[allow(clippy::from_over_into)] 56 | impl Into for DetPid { 57 | fn into(self) -> unistd::Pid { 58 | unistd::Pid::from_raw(self.0) 59 | } 60 | } 61 | 62 | impl DetPid { 63 | /// Create a DetPid from a raw pid. 64 | pub const fn from_raw(pid: i32) -> DetPid { 65 | DetPid(pid) 66 | } 67 | 68 | /// Convert to a row integer. 69 | pub fn as_raw(&self) -> i32 { 70 | self.0 71 | } 72 | } 73 | 74 | impl FromStr for DetPid { 75 | type Err = ::Err; 76 | 77 | fn from_str(s: &str) -> Result { 78 | Ok(Self::from_raw(s.parse::()?)) 79 | } 80 | } 81 | 82 | /// Deterministic "virtual" version of `reverie::Tid` 83 | pub type DetTid = DetPid; 84 | -------------------------------------------------------------------------------- /hermit-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //hermetic_infra/hermit/hermit-cli:[hermit,libhermit] 2 | 3 | [package] 4 | name = "hermit" 5 | version = "0.0.0" 6 | edition = "2024" 7 | repository = "https://github.com/facebookexperimental/hermit" 8 | license = "BSD-3-Clause" 9 | 10 | [dependencies] 11 | anyhow = "1.0.98" 12 | bincode = "1.3.3" 13 | clap = { version = "3.2.25", features = ["derive", "env", "regex", "unicode", "wrap_help"] } 14 | colored = "2.1.0" 15 | detcore = { version = "0.0.0", path = "../detcore" } 16 | diff = "0.1" 17 | dirs = "6.0" 18 | edit-distance = { version = "0.0.0", path = "../common/edit-distance" } 19 | fbinit = { version = "0.2.0", git = "https://github.com/facebookexperimental/rust-shed.git", branch = "main" } 20 | goblin = { version = "0.10", features = ["elf32", "elf64", "endian_fd"] } 21 | lazy_static = "1.5" 22 | libc = "0.2.139" 23 | nix = { version = "0.30.1", features = ["dir", "event", "hostname", "inotify", "ioctl", "mman", "mount", "net", "poll", "ptrace", "reboot", "resource", "sched", "signal", "term", "time", "user", "zerocopy"] } 24 | num_cpus = "1.16" 25 | once_cell = "1.21" 26 | pretty_assertions = { version = "1.2", features = ["alloc"], default-features = false } 27 | rand = { version = "0.8", features = ["small_rng"] } 28 | rand_pcg = { version = "0.3", features = ["serde1"] } 29 | regex = "1.12.2" 30 | reverie = { version = "0.1.0", git = "https://github.com/facebookexperimental/reverie.git", branch = "main" } 31 | reverie-ptrace = { version = "0.1.0", git = "https://github.com/facebookexperimental/reverie.git", branch = "main" } 32 | serde = { version = "1.0.219", features = ["derive", "rc"] } 33 | serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "raw_value", "unbounded_depth"] } 34 | shell-words = "1.1.0" 35 | tempfile = "3.22" 36 | tokio = { version = "1.47.1", features = ["full", "test-util", "tracing"] } 37 | tracing = { version = "0.1.41", features = ["attributes", "valuable"] } 38 | tracing-appender = "0.2.3" 39 | tracing-subscriber = { version = "0.3.20", features = ["chrono", "env-filter", "json", "local-time", "parking_lot", "registry"] } 40 | uuid = { version = "1.17", features = ["rng-getrandom", "serde", "v4", "v5", "v6", "v7", "v8"] } 41 | -------------------------------------------------------------------------------- /hermit-cli/src/replayer/network.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use reverie::Errno; 10 | use reverie::Guest; 11 | use reverie::syscalls::MemoryAccess; 12 | use reverie::syscalls::Poll; 13 | use reverie::syscalls::Recvfrom; 14 | use reverie::syscalls::family::SockOptFamily; 15 | 16 | use super::Replayer; 17 | 18 | impl Replayer { 19 | pub(super) async fn handle_poll>( 20 | &self, 21 | guest: &mut G, 22 | syscall: Poll, 23 | ) -> Result { 24 | let event = next_event!(guest, Poll)?; 25 | 26 | let nfds = syscall.nfds() as usize; 27 | 28 | assert_eq!(event.fds.len(), nfds); 29 | 30 | // Write out the recorded fds (if any). 31 | if let Some(addr) = syscall.fds() { 32 | guest.memory().write_values(addr, &event.fds)?; 33 | } 34 | 35 | Ok(event.updated as i64) 36 | } 37 | 38 | pub(super) async fn handle_sockopt_family>( 39 | &self, 40 | guest: &mut G, 41 | syscall: SockOptFamily, 42 | ) -> Result { 43 | let event = next_event!(guest, SockOpt)?; 44 | 45 | // Write out the value. 46 | guest.memory().write_exact( 47 | syscall.value().ok_or(Errno::EFAULT)?.cast::(), 48 | &event.value, 49 | )?; 50 | 51 | // Write out the length parameter. 52 | guest 53 | .memory() 54 | .write_value(syscall.value_len().ok_or(Errno::EFAULT)?, &event.length)?; 55 | 56 | Ok(0) 57 | } 58 | 59 | pub(super) async fn handle_recvfrom>( 60 | &self, 61 | guest: &mut G, 62 | syscall: Recvfrom, 63 | ) -> Result { 64 | let buf = next_event!(guest, Bytes)?; 65 | 66 | assert!(buf.len() <= syscall.len()); 67 | 68 | // Write out the buffer. 69 | guest 70 | .memory() 71 | .write_exact(syscall.buf().unwrap(), &buf) 72 | .unwrap(); 73 | Ok(buf.len() as i64) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/rust/futex_wait_child.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | /// Child thread waits on futex. 10 | 11 | fn main() { 12 | let layout = std::alloc::Layout::from_size_align(4, 4).unwrap(); // u32 13 | let ptr: *mut u8 = unsafe { std::alloc::alloc_zeroed(layout) }; 14 | let ptr2 = ptr as usize; 15 | 16 | eprintln!("Parent thread: spawn child."); 17 | let _ = std::thread::spawn(move || { 18 | let ptr: *mut u8 = ptr2 as *mut u8; 19 | eprintln!("Child thread start."); 20 | eprintln!("Child thread: start futex wait ({:?})..", ptr); 21 | unsafe { 22 | libc::syscall( 23 | libc::SYS_futex, 24 | ptr, 25 | libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG, 26 | 0, // val, 27 | 0, // timeout, 28 | 0, // uaddr - ignored 29 | 0, // val3 - ignored 30 | ); 31 | } 32 | eprintln!("Child thread: futex wait returned, done."); 33 | }); 34 | std::thread::sleep(std::time::Duration::from_millis(500)); 35 | 36 | // Note: in a very unfair schedule this wake may go first and fizzle. In that case, the child 37 | // thread will remain blocked when exit_group is called below. 38 | eprintln!("Parent thread: futex wake ({:?})..", ptr); 39 | let woke = unsafe { 40 | // At an application level, this is a bogus wake because the value hasn't actually changed. 41 | libc::syscall( 42 | libc::SYS_futex, 43 | ptr, 44 | libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG, 45 | 1, // val: wake 1 thread 46 | 0, // timeout - ignored 47 | 0, // uaddr - ignored 48 | 0, // val3 - ignored 49 | ) 50 | }; 51 | std::thread::sleep(std::time::Duration::from_millis(500)); 52 | eprintln!("Parent thread: done with futex wake: {}", woke); 53 | std::thread::sleep(std::time::Duration::from_millis(500)); 54 | eprintln!("Parent thread: exiting process."); 55 | let _ = unsafe { libc::syscall(libc::SYS_exit_group, 0) }; 56 | } 57 | -------------------------------------------------------------------------------- /detcore/scripts/local_validate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -Eeuo pipefail 9 | # This script is a "preflight" checklist: a series of tests that should run locally BEFORE publishing a diff. 10 | 11 | # Switch to the Detcore root directory: 12 | cd "$(dirname "$0")/../" 13 | 14 | # For cargo-autocargo: 15 | export PATH="$PATH:$HOME/fbsource/fbcode/common/rust/cargo_from_buck/bin/" 16 | CARGO1="$HOME/fbsource/fbcode/third-party-buck/platform007/build/rust/bin/cargo" 17 | CARGO2="$HOME/.cargo/bin/cargo" 18 | 19 | function banner { 20 | echo 21 | echo "================================================================================" 22 | echo ">>> $*" 23 | echo "================================================================================" 24 | } 25 | 26 | function bail_out { 27 | echo 28 | echo "!!!!! Local validation failed or was interrupted !!!!"; 29 | exit 1; 30 | } 31 | trap bail_out ERR 32 | 33 | function build_n_test() { 34 | local mode=$1 35 | banner "Build and run tests through Buck..." 36 | buck build "$mode" ... 37 | buck test "$mode" ... -- --timeout 60 38 | } 39 | 40 | # Separate functions just to make the --tag look nice: 41 | function opt_mode() { 42 | build_n_test @mode/opt 43 | } 44 | 45 | function dev-nosan_mode() { 46 | build_n_test @mode/dev-nosan 47 | } 48 | 49 | function cargo-fbcode() { 50 | banner "Test FB buck/autocargo setup..." 51 | $CARGO1 autocargo detcore 52 | $CARGO1 fbcode build detcore 53 | $CARGO1 fbcode doc detcore 54 | } 55 | 56 | function cargo-test() { 57 | banner "Test with vanilla Cargo..." 58 | $CARGO2 test 59 | } 60 | 61 | function arc-lint() { 62 | banner "Run linters..." 63 | arc lint 64 | # Disabling due to a problem with #[cfg]-activated dependencies: 65 | # arc lint-rust 66 | } 67 | 68 | # Unlike the analogous Reverie script, here we run these sequentially. 69 | # This is due to additional problems encountered with parallel execution. 70 | cargo-fbcode 71 | opt_mode 72 | dev-nosan_mode 73 | arc-lint 74 | cargo-test 75 | 76 | banner "All done. Local validation of Detcore passed." 77 | -------------------------------------------------------------------------------- /detcore/tests/lit/rt_sigaction/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // RUN: %me | FileCheck %s 10 | // CHECK: SIGHUP is masked 11 | // CHECK-NEXT: SIGALRM is masked 12 | // CHECK-NEXT: SIGSTKFLT is masked 13 | // CHECK-EMPTY: 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | static _Atomic int exit_flag; 24 | 25 | static void checksig(const char* desc, const sigset_t* set, int sig) { 26 | /* use unlocked_stdio here since this function is called in a sighandler */ 27 | fputs_unlocked(desc, stdout); 28 | if (sigismember(set, sig)) { 29 | fputs_unlocked(" is ", stdout); 30 | } else { 31 | fputs_unlocked(" is not ", stdout); 32 | } 33 | fputs_unlocked("masked\n", stdout); 34 | } 35 | 36 | static void sigalrm_handler(int sig, siginfo_t* siginfo, void* ctx) { 37 | sigset_t set; 38 | 39 | sigemptyset(&set); 40 | assert(sigprocmask(SIG_BLOCK, NULL, &set) == 0); 41 | 42 | checksig("SIGHUP", &set, SIGHUP); 43 | checksig("SIGALRM", &set, SIGALRM); 44 | checksig("SIGSTKFLT", &set, SIGSTKFLT); 45 | atomic_store(&exit_flag, 1); 46 | } 47 | 48 | static void mask_signal_via_sigaction(void) { 49 | struct sigaction sa; 50 | sigset_t set; 51 | 52 | memset(&sa, 0, sizeof(sa)); 53 | sigemptyset(&set); 54 | sigaddset(&set, SIGHUP); 55 | sigaddset(&set, SIGSTKFLT); 56 | 57 | sa.sa_flags = SA_RESTART | SA_RESETHAND | SA_SIGINFO; 58 | sa.sa_mask = set; 59 | sa.sa_sigaction = sigalrm_handler; 60 | 61 | assert(sigaction(SIGALRM, &sa, NULL) == 0); 62 | 63 | struct itimerval it = { 64 | .it_interval = 65 | { 66 | .tv_sec = 0, 67 | .tv_usec = 0, 68 | }, 69 | .it_value = 70 | { 71 | .tv_sec = 0, 72 | .tv_usec = 100000, 73 | }, 74 | }; 75 | 76 | assert(setitimer(ITIMER_REAL, &it, NULL) == 0); 77 | } 78 | 79 | int main(int argc, char* argv[]) { 80 | mask_signal_via_sigaction(); 81 | while (atomic_load(&exit_flag) == 0) { 82 | ; 83 | } 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /tests/rust/test_utils/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | use std::io; 10 | use std::io::Write; 11 | use std::sync::Arc; 12 | use std::sync::atomic::AtomicUsize; 13 | use std::sync::atomic::Ordering; 14 | 15 | pub const NANOS_PER_SEC: i64 = 1_000_000_000; 16 | 17 | pub fn sleep_race(sleep_function: F) -> u64 18 | where 19 | F: Fn(), 20 | { 21 | let flag = Arc::new(AtomicUsize::new(0)); 22 | let flag2 = flag.clone(); 23 | // This work will accomplish an arbitrary number of iterations while the parent thread 24 | // sleeps: 25 | let child = std::thread::spawn(move || { 26 | let mut count = 0; 27 | let mut spurious = 0.00000001; 28 | while flag2.load(Ordering::SeqCst) == 0 { 29 | print!("{} ", count); 30 | io::stdout().flush().unwrap(); 31 | for _ in 0..50000 { 32 | count += 1; 33 | spurious += 0.000001; 34 | if flag2.load(Ordering::SeqCst) == 1 { 35 | break; 36 | } 37 | } 38 | } 39 | (count, spurious) 40 | }); 41 | 42 | sleep_function(); 43 | 44 | let mut tp = libc::timespec { 45 | tv_sec: 0, 46 | tv_nsec: 0, 47 | }; 48 | unsafe { 49 | // issue a raw clock_gettime syscall to the OS to get the current time 50 | libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut tp); 51 | tp.tv_nsec += 100_000_000; // 100ms is just a random amount of time that makes the test fast to complete. 52 | if tp.tv_nsec / NANOS_PER_SEC > 0 { 53 | tp.tv_sec += tp.tv_nsec / NANOS_PER_SEC; 54 | tp.tv_nsec %= NANOS_PER_SEC; 55 | } 56 | // issue a raw clock_nanosleep syscall to the OS 57 | libc::clock_nanosleep( 58 | libc::CLOCK_MONOTONIC, 59 | libc::TIMER_ABSTIME, 60 | &tp, 61 | std::ptr::null_mut(), 62 | ) 63 | }; 64 | 65 | flag.store(1, Ordering::SeqCst); 66 | let (final_count, spurious) = child.join().unwrap(); 67 | println!("\n Final count: {}", final_count); 68 | println!("\n Silly number: {}", spurious); 69 | final_count 70 | } 71 | -------------------------------------------------------------------------------- /tests/util/hermit_analyze_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | # This goes along with the chaos/cas_sequence.rs test. 9 | 10 | if [ "$2" == "" ]; then 11 | echo "Script expects two arguments! (hermit path and cas_sequence binary path)" 12 | exit 1; 13 | fi 14 | 15 | # Prereqs: expects the full path to both binaries to be passed as arguments: 16 | HERMIT=$1 17 | TESTBIN=$2 18 | 19 | if [[ -z "$KEEP_LOGS" ]]; then 20 | KEEP_LOGS=0 21 | fi 22 | if [[ "$KEEP_LOGS" != "0" ]]; then 23 | set -x 24 | fi 25 | 26 | # Additional arguments to analyze: 27 | if [[ -z "$ANALYZE_OPTS" ]]; then 28 | ANALYZE_OPTS="" 29 | fi 30 | 31 | if [[ -z "$EXPECTED_OUTPUT" ]]; then 32 | EXPECTED_OUTPUT="" 33 | fi 34 | 35 | set -eu 36 | 37 | # hermit analyze args: 38 | HERMIT_ARGS="--analyze-seed=0 " 39 | HERMIT_ARGS+="--search " 40 | if [[ "$KEEP_LOGS" != "0" ]]; then 41 | HERMIT_ARGS+="--verbose " 42 | fi 43 | HERMIT_ARGS+=" -- " 44 | # hermit run args: 45 | HERMIT_ARGS+="--chaos " 46 | HERMIT_ARGS+="--summary " 47 | HERMIT_ARGS+="--preemption-timeout=400000 " 48 | 49 | TEMP=$(mktemp -d /tmp/analyze_test_XXXXX) 50 | echo ":: [analyze_test] Temporary workspace: $TEMP" 51 | TEMPLOG="${TEMP}/log.txt" 52 | HERMIT_ARGS="--report-file=${TEMP}/report.json $HERMIT_ARGS" 53 | 54 | trap cleanup EXIT 55 | 56 | function cleanup { 57 | if [[ "$KEEP_LOGS" == "0" ]]; then 58 | rm -rf "$TEMP" 59 | fi 60 | } 61 | 62 | echo ":: [analyze_test] Invoking analyze with: $HERMIT analyze $HERMIT_ARGS -- $TESTBIN" 63 | 64 | # shellcheck disable=SC2086 # Intended splitting of args and command: 65 | $HERMIT analyze $ANALYZE_OPTS $HERMIT_ARGS -- "$TESTBIN" > >(tee "$TEMPLOG") \ 66 | || (echo "Analyze failed."; exit 1) 67 | 68 | echo ":: [analyze_test] Searching for printed backtraces in $TEMPLOG" 69 | grep -q 'Stack trace for thread' "$TEMPLOG" \ 70 | || (echo "Stack trace not printed by hermit analyze!"; exit 1) 71 | 72 | if [[ "$EXPECTED_OUTPUT" != "" ]]; then 73 | grep -q "$EXPECTED_OUTPUT" "$TEMPLOG" \ 74 | || (echo "Expected this string in hermit analyze output, but it was missing: '$EXPECTED_OUTPUT'"; exit 1) 75 | fi 76 | 77 | echo ":: [analyze_test] All checks matched, test passed." 78 | -------------------------------------------------------------------------------- /tests/chaos/lock_granularity.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // With the right schedule, one thread can replace the value of global_result 14 | // in-between the others read of the global_data and it's write to 15 | // global_result, because the lock is incorrectly unlocked in-between. 16 | 17 | #define DO_WORK \ 18 | do { \ 19 | volatile int _work_var = 1000; \ 20 | while (_work_var > 0) { \ 21 | _work_var--; \ 22 | } \ 23 | } while (0); 24 | 25 | uint64_t compute(uint64_t u) { 26 | u |= 1; 27 | const uint64_t MULT = 0x4385DF649FCCF645ull; 28 | for (int i = 0; i < 1000; i++) { 29 | u *= MULT; 30 | } 31 | return u; 32 | } 33 | 34 | pthread_mutex_t mtx; // protects global and result 35 | uint64_t global_data; 36 | uint64_t global_result; 37 | 38 | void* Thread1(void* x) { 39 | pthread_mutex_lock(&mtx); 40 | global_data = 42; 41 | pthread_mutex_unlock(&mtx); 42 | 43 | DO_WORK; 44 | 45 | pthread_mutex_lock(&mtx); 46 | global_data = 43; 47 | pthread_mutex_unlock(&mtx); 48 | return NULL; 49 | } 50 | 51 | void* Thread2(void* x) { 52 | for (int i = 0; i < 30; i++) { 53 | DO_WORK; 54 | } 55 | 56 | uint64_t data; 57 | pthread_mutex_lock(&mtx); 58 | data = global_data; 59 | pthread_mutex_unlock(&mtx); 60 | 61 | uint64_t result = compute(data); 62 | 63 | pthread_mutex_lock(&mtx); 64 | global_result = result; 65 | pthread_mutex_unlock(&mtx); 66 | 67 | return NULL; 68 | } 69 | 70 | int main() { 71 | pthread_t t[2]; 72 | pthread_mutex_init(&mtx, 0); 73 | pthread_create(&t[0], NULL, Thread1, NULL); 74 | pthread_create(&t[1], NULL, Thread2, NULL); 75 | pthread_join(t[0], NULL); 76 | pthread_join(t[1], NULL); 77 | pthread_mutex_destroy(&mtx); 78 | 79 | printf( 80 | "Result: %lu, data: %lu, c(data): %lu\n", 81 | global_result, 82 | global_data, 83 | compute(global_data)); 84 | int success = global_result == compute(global_data); 85 | printf("%s\n", success ? "Success" : "Fail"); 86 | return success ? 0 : 1; 87 | } 88 | --------------------------------------------------------------------------------