├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── enarxbot.yml │ ├── lint.yml │ └── test.yml ├── .gitignore ├── BUILD.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README-DEBUG.md ├── README.md ├── README.tpl ├── build.rs ├── deny.toml ├── helper └── parse-trace.sh ├── internal ├── shim-sev │ ├── .cargo │ │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── build.rs │ ├── layout.ld │ └── src │ │ ├── addr.rs │ │ ├── allocator.rs │ │ ├── asm.rs │ │ ├── attestation.rs │ │ ├── gdt.rs │ │ ├── hostcall.rs │ │ ├── hostmap.rs │ │ ├── main.rs │ │ ├── no_std.rs │ │ ├── pagetables.rs │ │ ├── paging.rs │ │ ├── payload.rs │ │ ├── print.rs │ │ ├── random.rs │ │ ├── shim_stack.rs │ │ ├── spin.rs │ │ ├── start.rs │ │ ├── syscall.rs │ │ └── usermode.rs └── shim-sgx │ ├── .cargo │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── build.rs │ ├── layout.ld │ └── src │ ├── entry.rs │ ├── handler │ ├── base.rs │ ├── enarx.rs │ ├── file.rs │ ├── memory.rs │ ├── mod.rs │ ├── other.rs │ └── process.rs │ └── main.rs ├── rust-toolchain.toml ├── src ├── backend │ ├── binary.rs │ ├── kvm │ │ ├── builder.rs │ │ ├── config.rs │ │ ├── data.rs │ │ ├── mem.rs │ │ ├── mod.rs │ │ └── thread.rs │ ├── mod.rs │ ├── probe │ │ ├── mod.rs │ │ └── x86_64.rs │ └── sgx │ │ ├── builder.rs │ │ ├── config.rs │ │ ├── data.rs │ │ ├── hasher.rs │ │ ├── ioctls.rs │ │ ├── mod.rs │ │ └── thread.rs ├── main.rs └── protobuf │ ├── aesm-proto.proto │ └── mod.rs └── tests ├── bin ├── bind.c ├── clock_gettime.c ├── close.c ├── echo.rs ├── enarx.h ├── exit_one.c ├── exit_zero.c ├── get_att.c ├── getegid.c ├── geteuid.c ├── getgid.c ├── getuid.c ├── libc.h ├── listen.c ├── memory_stress_test.rs ├── memspike.rs ├── read.c ├── read_udp.c ├── readv.c ├── sev_get_att_quote.c ├── sgx_get_att_quote.c ├── sgx_get_att_quote_size.c ├── socket.c ├── uname.c ├── unix_echo.rs ├── write_emsgsize.c ├── write_stderr.c └── write_stdout.c └── integration_tests.rs /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # These owners will be the default owners for everything in 5 | # the repo. Unless a later match takes precedence, 6 | # @global-owner1 and @global-owner2 will be requested for 7 | # review when someone opens a pull request. 8 | * @npmccallum @haraldh 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "cargo" 8 | directory: "internal/sallyport" 9 | schedule: 10 | interval: "daily" 11 | - package-ecosystem: "cargo" 12 | directory: "internal/shim-sev" 13 | schedule: 14 | interval: "daily" 15 | - package-ecosystem: "cargo" 16 | directory: "internal/shim-sgx" 17 | schedule: 18 | interval: "daily" 19 | -------------------------------------------------------------------------------- /.github/workflows/enarxbot.yml: -------------------------------------------------------------------------------- 1 | name: enarxbot 2 | 3 | on: 4 | check_run: 5 | check_suite: 6 | create: 7 | delete: 8 | deployment: 9 | deployment_status: 10 | fork: 11 | gollum: 12 | issue_comment: 13 | issues: 14 | label: 15 | milestone: 16 | page_build: 17 | project: 18 | project_card: 19 | project_column: 20 | public: 21 | pull_request_target: 22 | types: 23 | - assigned 24 | - unassigned 25 | - labeled 26 | - unlabeled 27 | - opened 28 | - edited 29 | - closed 30 | - reopened 31 | - synchronize 32 | - ready_for_review 33 | - locked 34 | - unlocked 35 | - review_requested 36 | - review_request_removed 37 | push: 38 | registry_package: 39 | release: 40 | status: 41 | watch: 42 | schedule: 43 | - cron: '*/15 * * * *' 44 | workflow_dispatch: 45 | 46 | jobs: 47 | enarxbot: 48 | runs-on: ubuntu-latest 49 | env: 50 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 51 | name: enarxbot 52 | steps: 53 | - uses: enarx/bot@master 54 | pull-request-responsibility: 55 | runs-on: ubuntu-latest 56 | env: 57 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 58 | name: pull-request-responsibility 59 | steps: 60 | - uses: actions-automation/pull-request-responsibility@main 61 | with: 62 | actions: "request,assign,copy-labels-linked,merge" 63 | reviewers: "reviews" 64 | num_to_request: 3 65 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: lint 3 | jobs: 4 | fmt: 5 | name: cargo fmt (${{ matrix.crate.name }}) 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions-rs/toolchain@v1 10 | with: 11 | components: rustfmt 12 | toolchain: nightly 13 | profile: minimal 14 | override: true 15 | - uses: actions-rs/cargo@v1 16 | with: 17 | command: fmt 18 | args: --manifest-path=${{ matrix.crate.path }} -- --check 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | crate: 23 | - {name: enarx-keepldr, path: Cargo.toml} 24 | - {name: shim-sgx, path: internal/shim-sgx/Cargo.toml} 25 | - {name: shim-sev, path: internal/shim-sev/Cargo.toml} 26 | 27 | clippy: 28 | name: cargo clippy (${{ matrix.crate.name }}) 29 | runs-on: ubuntu-20.04 30 | steps: 31 | - run: sudo apt update 32 | - run: sudo apt install -y musl-tools 33 | - uses: actions/checkout@v2 34 | - uses: actions-rs/toolchain@v1 35 | with: 36 | components: clippy 37 | toolchain: nightly 38 | profile: minimal 39 | target: x86_64-unknown-linux-musl 40 | override: true 41 | - uses: actions-rs/cargo@v1 42 | with: 43 | command: clippy 44 | args: ${{ matrix.crate.target }} --all-features --manifest-path=${{ matrix.crate.path }} -- -D warnings 45 | strategy: 46 | fail-fast: false 47 | matrix: 48 | crate: 49 | - {name: enarx-keepldr, path: Cargo.toml} 50 | - name: shim-sgx 51 | path: internal/shim-sgx/Cargo.toml 52 | target: --target=x86_64-unknown-linux-musl 53 | - name: shim-sev 54 | path: internal/shim-sev/Cargo.toml 55 | target: --target=x86_64-unknown-linux-musl 56 | 57 | clippy-single-backends: 58 | name: cargo clippy (enarx-keepldr ${{ matrix.backend.name }} ${{ matrix.profile.name }}) 59 | runs-on: ubuntu-20.04 60 | steps: 61 | - run: sudo apt update 62 | - run: sudo apt install -y musl-tools 63 | - uses: actions/checkout@v2 64 | - uses: actions-rs/toolchain@v1 65 | with: 66 | components: clippy 67 | toolchain: nightly 68 | profile: minimal 69 | target: x86_64-unknown-linux-musl 70 | override: true 71 | - uses: actions-rs/cargo@v1 72 | with: 73 | command: clippy 74 | args: ${{ matrix.profile.flag }} --no-default-features --features=backend-${{ matrix.backend.name }} -- -D warnings 75 | - uses: actions-rs/cargo@v1 76 | with: 77 | command: clippy 78 | args: ${{ matrix.profile.flag }} --no-default-features --features=backend-${{ matrix.backend.name }} --examples -- -D warnings 79 | strategy: 80 | fail-fast: false 81 | matrix: 82 | backend: 83 | - {name: sgx, host: [self-hosted, linux, sgx]} 84 | - {name: kvm, host: [self-hosted, linux]} 85 | profile: 86 | - name: debug 87 | - name: release 88 | flag: --release 89 | 90 | readme: 91 | name: cargo readme 92 | runs-on: ubuntu-latest 93 | steps: 94 | - uses: actions/checkout@v2 95 | - uses: actions-rs/toolchain@v1 96 | with: 97 | toolchain: nightly 98 | profile: minimal 99 | override: true 100 | - run: cargo install cargo-readme 101 | - run: cargo readme > README.md && git diff --exit-code 102 | 103 | deny: 104 | name: cargo deny 105 | runs-on: ubuntu-latest 106 | steps: 107 | - uses: actions/checkout@v2 108 | - uses: EmbarkStudios/cargo-deny-action@v1 109 | with: 110 | arguments: --manifest-path=${{ matrix.crate.path }} 111 | strategy: 112 | fail-fast: false 113 | matrix: 114 | crate: 115 | - {name: enarx-keepldr, path: ./Cargo.toml} 116 | - {name: shim-sgx, path: internal/shim-sgx/Cargo.toml} 117 | - {name: shim-sev, path: internal/shim-sev/Cargo.toml} 118 | 119 | check-spdx-headers: 120 | runs-on: ubuntu-latest 121 | steps: 122 | - name: checkout 123 | uses: actions/checkout@v2 124 | - uses: enarx/spdx@master 125 | with: 126 | licenses: Apache-2.0 BSD-3-Clause 127 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: test 3 | jobs: 4 | main: 5 | name: enarx-keepldr ${{ matrix.backend.name }} nightly ${{ matrix.profile.name }} 6 | runs-on: ${{ matrix.backend.host }} 7 | env: 8 | ENARX_BACKEND: ${{ matrix.backend.name }} 9 | steps: 10 | - run: sudo apt update 11 | - run: sudo apt install -y musl-tools 12 | - uses: actions/checkout@v2 13 | - uses: actions-rs/toolchain@v1 14 | with: 15 | target: x86_64-unknown-linux-musl 16 | toolchain: nightly 17 | override: true 18 | - uses: actions-rs/cargo@v1 19 | with: 20 | command: test 21 | args: ${{ matrix.profile.flag }} --no-default-features --features=backend-${{ matrix.backend.name }} 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | backend: 26 | - {name: sgx, host: [self-hosted, linux, sgx]} 27 | - {name: kvm, host: [self-hosted, linux]} 28 | profile: 29 | - name: debug 30 | - name: release 31 | flag: --release 32 | 33 | internal: 34 | name: ${{ matrix.crate }} nightly ${{ matrix.profile.name }} 35 | runs-on: ubuntu-20.04 36 | steps: 37 | - run: sudo apt update 38 | - run: sudo apt install -y musl-tools 39 | - uses: actions/checkout@v2 40 | - uses: actions-rs/toolchain@v1 41 | with: 42 | target: x86_64-unknown-linux-musl 43 | toolchain: nightly 44 | override: true 45 | - run: cargo test ${{ matrix.profile.flag }} 46 | working-directory: internal/${{ matrix.crate }} 47 | strategy: 48 | fail-fast: false 49 | matrix: 50 | crate: 51 | - shim-sgx 52 | - shim-sev 53 | profile: 54 | - name: debug 55 | - name: release 56 | flag: --release 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | ## Install Dependencies 4 | 5 | ### Fedora 6 | 7 | $ sudo dnf install git curl gcc pkg-config openssl-devel musl-gcc 8 | 9 | ### Disclaimer 10 | 11 | Please note that most (all) Enarx developers use Fedora, so that is the 12 | distribution where we'll be able to offer most support, if any. 13 | 14 | The following configurations are unlikely to be exercised with any 15 | frequency and as a result, may not work for you. However, they have 16 | worked at some point in the past and therefore they are listed here 17 | in the hopes that they might be useful to you. 18 | 19 | Please feel free to file a pull request to add your favorite distribution 20 | if you're able to build and run the `enarx-keepldr` test suite. 21 | 22 | ### A note on gcc 23 | 24 | The minimum required `gcc` version is version 9. Something older _might_ build 25 | binaries (such as integration test binaries), but may silently drop required 26 | compiler flags. Please ensure you're using the minimum required version of `gcc`. 27 | Failure to do so might result in weird failures at runtime. 28 | 29 | ### CentOS 8 / Stream 30 | 31 | $ sudo dnf copr enable ngompa/musl-libc 32 | $ sudo dnf install git curl gcc-toolset-9 openssl-devel musl-gcc 33 | $ source "/opt/rh/gcc-toolset-9/enable" 34 | 35 | Note: you may want to add that final `source` command to a `~/.profile`, 36 | `~/.bashrc` / or `~/.bash_profile` equivalent, otherwise you must remember 37 | to source that file prior to building `enarx-keepldr`. 38 | 39 | ### Debian / Ubuntu 40 | 41 | $ sudo apt update 42 | $ sudo apt install git curl gcc pkg-config libssl-dev musl-tools python3-minimal 43 | 44 | ## Install Rust, Nightly and the MUSL target 45 | 46 | $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 47 | $ source $HOME/.cargo/env 48 | $ rustup toolchain install nightly --allow-downgrade -t x86_64-unknown-linux-musl 49 | 50 | ## Build 51 | 52 | $ git clone https://github.com/enarx/enarx-keepldr 53 | $ cd enarx-keepldr/ 54 | $ cargo build 55 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enarx-keepldr" 3 | version = "0.1.0" 4 | authors = ["Nathaniel McCallum "] 5 | license = "Apache-2.0" 6 | edition = "2018" 7 | build = "build.rs" 8 | homepage = "https://github.com/enarx/enarx-keepldr" 9 | repository = "https://github.com/enarx/enarx-keepldr" 10 | description = "Enarx Keep Loader" 11 | readme = "README.md" 12 | keywords = ["sgx", "sev", "kvm", "tee"] 13 | categories = ["os", "os::linux-apis", "network-programming", "hardware-support"] 14 | exclude = [ ".gitignore", ".github/*" ] 15 | include = [ "src", "internal" ] 16 | 17 | [badges] 18 | # See https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section 19 | github = { repository = "enarx/enarx-keepldr", workflow = "test" } 20 | #github = { repository = "enarx/enarx-keepldr", workflow = "lint" } 21 | maintenance = { status = "actively-developed" } 22 | is-it-maintained-issue-resolution = { repository = "enarx/enarx-keepldr" } 23 | is-it-maintained-open-issues = { repository = "enarx/enarx-keepldr" } 24 | 25 | [features] 26 | default = ["backend-kvm", "backend-sgx"] 27 | 28 | backend-kvm = ["x86_64", "kvm-bindings", "kvm-ioctls"] 29 | backend-sgx = ["x86_64", "sgx"] 30 | 31 | [dependencies] 32 | sgx = { git = "https://github.com/enarx/sgx", rev = "57df3753a0ea1777963dbf3023452993df2edb8c", features = ["openssl"], optional = true } 33 | sallyport = { git = "https://github.com/enarx/sallyport", rev = "a567a22665c7e5ba88a8c4acd64ab43ee32b4681", features = [ "asm" ] } 34 | x86_64 = { version = "^0.14.6", default-features = false, optional = true } 35 | koine = { git = "https://github.com/enarx/koine", optional = true } 36 | primordial = { version = "0.3", features = ["alloc"] } 37 | kvm-bindings = { version = "0.5", optional = true } 38 | kvm-ioctls = { version = "0.10", optional = true } 39 | itertools = "0.10" 40 | protobuf = "2.22" 41 | structopt = "0.3" 42 | openssl = "0.10" 43 | iocuddle = "0.1" 44 | ciborium = "0.1" 45 | colorful = "0.2" 46 | mmarinus = "0.2" 47 | nbytes = "0.1" 48 | anyhow = "1.0" 49 | semver = "1.0" 50 | goblin = "0.4" 51 | libc = "0.2" 52 | lset = "0.2" 53 | vdso = "0.1" 54 | 55 | [build-dependencies] 56 | cc = "1.0" 57 | walkdir = "2" 58 | protobuf-codegen-pure = "2.25" 59 | sallyport = { git = "https://github.com/enarx/sallyport", rev = "a567a22665c7e5ba88a8c4acd64ab43ee32b4681", features = [ "asm" ] } 60 | 61 | [dev-dependencies] 62 | process_control = "3.0" 63 | serial_test = "0.5" 64 | tempdir = "0.3.7" 65 | 66 | [[example]] 67 | name="echo" 68 | path="tests/bin/echo.rs" 69 | 70 | [[example]] 71 | name="memory_stress_test" 72 | path="tests/bin/memory_stress_test.rs" 73 | 74 | [[example]] 75 | name="memspike" 76 | path="tests/bin/memspike.rs" 77 | 78 | [[example]] 79 | name="unix_echo" 80 | path="tests/bin/unix_echo.rs" 81 | -------------------------------------------------------------------------------- /README-DEBUG.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | ## Stack Trace 4 | 5 | ### KVM / SEV 6 | 7 | If you encounter unexpected shutdowns or panics like: 8 | 9 | ``` 10 | panicked at 'explicit panic', src/syscall.rs:167:9 11 | TRACE: 12 | 0x000000000000f876 13 | 0x0000000000039d10 14 | 0x0000000000007189 15 | 0x0000000000008d3e 16 | 0x0000000000008b58 17 | P 0x0000000000001279 18 | P 0x000000000000102c 19 | ``` 20 | 21 | or 22 | 23 | ``` 24 | Error: Shutdown Ok( 25 | kvm_regs { 26 | rax: 0x29f47, 27 | rbx: 0x2a014, 28 | rcx: 0x15475, 29 | rdx: 0x246e8, 30 | rsi: 0x269b0, 31 | rdi: 0x1e2db, 32 | rsp: 0xffffff8000433900, 33 | rbp: 0xffffff8000433a70, 34 | r8: 0x129cb, 35 | r9: 0x29ed7, 36 | r10: 0x2a074, 37 | r11: 0x154f5, 38 | r12: 0x259b0, 39 | r13: 0x268f0, 40 | r14: 0x109ac, 41 | r15: 0x30889, 42 | rip: 0xffffff8000230662, 43 | rflags: 0x10046, 44 | }, 45 | ) 46 | ``` 47 | 48 | you might get a meaningful stack backtrace with the `helper/parse-trace.sh` script: 49 | 50 | ```console 51 | $ ./helper/parse-trace.sh [] 52 | ``` 53 | 54 | `parse-trace.sh` needs `addr2line` from `binutils`, so make sure that is installed. 55 | 56 | To find the shim with the debug info and not stripped run this: 57 | 58 | ```console 59 | $ find target -wholename '*linux-musl/*/shim-sev' 60 | ``` 61 | 62 | Then choose either the `debug` or `release`, depending with which version the panic occurred. 63 | 64 | ## Examples 65 | 66 | ### From a File 67 | ```console 68 | $ ./helper/parse-trace.sh \ 69 | target/debug/build/*/out/internal/shim-sev/x86_64-unknown-linux-musl/debug/shim-sev \ 70 | < traceback.txt 71 | ``` 72 | 73 | ### Pipe 74 | 75 | ```console 76 | $ cargo run -- exec |& ./helper/parse-trace.sh \ 77 | target/debug/build/*/out/internal/shim-sev/x86_64-unknown-linux-musl/debug/shim-sev 78 | ``` 79 | 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![lint](https://github.com/enarx/enarx-keepldr/workflows/lint/badge.svg) 2 | ![enarxbot](https://github.com/enarx/enarx-keepldr/workflows/enarxbot/badge.svg) 3 | [![Workflow Status](https://github.com/enarx/enarx-keepldr/workflows/test/badge.svg)](https://github.com/enarx/enarx-keepldr/actions?query=workflow%3A%22test%22) 4 | [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/enarx/enarx-keepldr.svg)](https://isitmaintained.com/project/enarx/enarx-keepldr "Average time to resolve an issue") 5 | [![Percentage of issues still open](https://isitmaintained.com/badge/open/enarx/enarx-keepldr.svg)](https://isitmaintained.com/project/enarx/enarx-keepldr "Percentage of issues still open") 6 | ![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg) 7 | 8 | # enarx-keepldr 9 | 10 | This crate provides the `enarx-keepldr` executable which loads `static-pie` 11 | binaries into an Enarx Keep - that is a hardware isolated environment using 12 | technologies such as Intel SGX or AMD SEV. 13 | 14 | ## Building 15 | 16 | Please see **BUILD.md** for instructions. 17 | 18 | ## Run Tests 19 | 20 | $ cargo test 21 | 22 | ## Build and Run an Application 23 | 24 | $ cat > test.c < 26 | 27 | int main() { 28 | printf("Hello World!\n"); 29 | return 0; 30 | } 31 | EOF 32 | 33 | $ musl-gcc -static-pie -fPIC -o test test.c 34 | $ target/debug/enarx-keepldr exec ./test 35 | Hello World! 36 | 37 | ## Select a Different Backend 38 | 39 | `enarx-keepldr exec` will probe the machine it is running on 40 | in an attempt to deduce an appropriate deployment backend unless 41 | that target is already specified in an environment variable 42 | called `ENARX_BACKEND`. 43 | 44 | To see what backends are supported on your system, run: 45 | 46 | $ target/debug/enarx-keepldr info 47 | 48 | To manually select a backend, set the `ENARX_BACKEND` environment 49 | variable: 50 | 51 | $ ENARX_BACKEND=sgx target/debug/enarx-keepldr exec ./test 52 | 53 | Note that some backends are conditionally compiled. They can all 54 | be compiled in like so: 55 | 56 | $ cargo build --all-features 57 | 58 | Or specific backends can be compiled in: 59 | 60 | $ cargo build --features=backend-sgx,backend-kvm 61 | 62 | License: Apache-2.0 63 | -------------------------------------------------------------------------------- /README.tpl: -------------------------------------------------------------------------------- 1 | ![lint](https://github.com/enarx/enarx-keepldr/workflows/lint/badge.svg) 2 | ![enarxbot](https://github.com/enarx/enarx-keepldr/workflows/enarxbot/badge.svg) 3 | {{badges}} 4 | 5 | # {{crate}} 6 | 7 | {{readme}} 8 | 9 | License: {{license}} 10 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use std::collections::HashMap; 4 | use std::ffi::OsStr; 5 | use std::fs::OpenOptions; 6 | use std::path::{Path, PathBuf}; 7 | use std::process::{Command, Stdio}; 8 | use walkdir::WalkDir; 9 | 10 | const CRATE: &str = env!("CARGO_MANIFEST_DIR"); 11 | const TEST_BINS_IN: &str = "tests/bin"; 12 | 13 | fn find_files_with_extensions<'a>( 14 | exts: &'a [&'a str], 15 | path: impl AsRef, 16 | ) -> impl Iterator + 'a { 17 | WalkDir::new(&path) 18 | .into_iter() 19 | .filter_map(|e| e.ok()) 20 | .filter(move |e| { 21 | e.path() 22 | .extension() 23 | .and_then(OsStr::to_str) 24 | .map(|ext| exts.contains(&ext)) 25 | .unwrap_or(false) 26 | }) 27 | .map(|x| x.path().to_owned()) 28 | } 29 | 30 | fn rerun_src(path: impl AsRef) { 31 | for entry in find_files_with_extensions(&["rs", "s", "S"], &path) { 32 | if let Some(path) = entry.to_str() { 33 | println!("cargo:rerun-if-changed={}", path) 34 | } 35 | } 36 | } 37 | 38 | fn build_rs_tests(in_path: &Path, out_path: &Path) { 39 | let filtered_env: HashMap = std::env::vars() 40 | .filter(|&(ref k, _)| { 41 | k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH" || k == "RUSTUP_HOME" 42 | }) 43 | .collect(); 44 | 45 | let target_name = "x86_64-unknown-linux-musl"; 46 | 47 | for in_source in find_files_with_extensions(&["rs"], &in_path) { 48 | let stdout: Stdio = OpenOptions::new() 49 | .write(true) 50 | .open("/dev/tty") 51 | .map(Stdio::from) 52 | .unwrap_or_else(|_| Stdio::inherit()); 53 | 54 | let stderr: Stdio = OpenOptions::new() 55 | .write(true) 56 | .open("/dev/tty") 57 | .map(Stdio::from) 58 | .unwrap_or_else(|_| Stdio::inherit()); 59 | 60 | let output = in_source.file_stem().unwrap(); 61 | 62 | let status = Command::new("rustc") 63 | .current_dir(&out_path) 64 | .env_clear() 65 | .envs(&filtered_env) 66 | .stdout(stdout) 67 | .stderr(stderr) 68 | .arg("-C") 69 | .arg("force-frame-pointers=yes") 70 | .arg("-C") 71 | .arg("debuginfo=2") 72 | .arg("--target") 73 | .arg(target_name) 74 | .arg(&in_source) 75 | .arg("-o") 76 | .arg(output) 77 | .status() 78 | .unwrap_or_else(|_| panic!("failed to compile {:#?}", &in_source)); 79 | 80 | assert!(status.success(), "Failed to compile {:?}", &in_source); 81 | } 82 | } 83 | 84 | fn build_cc_tests(in_path: &Path, out_path: &Path) { 85 | for in_source in find_files_with_extensions(&["c", "s", "S"], &in_path) { 86 | let output = in_source.file_stem().unwrap(); 87 | 88 | let mut cmd = cc::Build::new() 89 | .no_default_flags(true) 90 | .get_compiler() 91 | .to_command(); 92 | 93 | let status = cmd 94 | .current_dir(&out_path) 95 | .arg("-nostdlib") 96 | .arg("-static-pie") 97 | .arg("-fPIC") 98 | .arg("-fno-omit-frame-pointer") 99 | .arg("-g") 100 | .arg("-o") 101 | .arg(output) 102 | .arg(&in_source) 103 | .status() 104 | .unwrap_or_else(|_| panic!("failed to compile {:#?}", &in_source)); 105 | 106 | assert!(status.success(), "Failed to compile {:?}", &in_source); 107 | } 108 | } 109 | 110 | fn create(path: &Path) { 111 | match std::fs::create_dir(&path) { 112 | Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {} 113 | Err(e) => { 114 | eprintln!("Can't create {:#?} : {:#?}", path, e); 115 | std::process::exit(1); 116 | } 117 | Ok(_) => {} 118 | } 119 | } 120 | 121 | fn main() { 122 | println!("cargo:rerun-if-env-changed=OUT_DIR"); 123 | println!("cargo:rerun-if-env-changed=PROFILE"); 124 | 125 | let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); 126 | let out_dir_proto = out_dir.join("protos"); 127 | create(&out_dir_proto); 128 | 129 | protobuf_codegen_pure::Codegen::new() 130 | .out_dir(&out_dir_proto) 131 | .inputs(&["src/protobuf/aesm-proto.proto"]) 132 | .include("src/protobuf") 133 | .customize(protobuf_codegen_pure::Customize { 134 | gen_mod_rs: Some(true), 135 | ..Default::default() 136 | }) 137 | .run() 138 | .expect("Protobuf codegen failed"); 139 | 140 | let out_dir_bin = out_dir.join("bin"); 141 | create(&out_dir_bin); 142 | 143 | build_cc_tests(&Path::new(CRATE).join(TEST_BINS_IN), &out_dir_bin); 144 | build_rs_tests(&Path::new(CRATE).join(TEST_BINS_IN), &out_dir_bin); 145 | 146 | let profile: &[&str] = match std::env::var("PROFILE").unwrap().as_str() { 147 | "release" => &["--release"], 148 | _ => &[], 149 | }; 150 | 151 | let target_name = "x86_64-unknown-linux-musl"; 152 | 153 | let filtered_env: HashMap = std::env::vars() 154 | .filter(|&(ref k, _)| { 155 | k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH" || k == "RUSTUP_HOME" 156 | }) 157 | .collect(); 158 | 159 | // internal crates are not included, if there is a `Cargo.toml` file 160 | // trick cargo by renaming the `Cargo.toml` to `Cargo.tml` before 161 | // publishing and rename it back here. 162 | for entry in std::fs::read_dir("internal").unwrap() { 163 | let path = entry.unwrap().path(); 164 | 165 | let cargo_toml = path.join("Cargo.toml"); 166 | let cargo_tml = path.join("Cargo.tml"); 167 | 168 | if cargo_tml.exists() { 169 | std::fs::copy(cargo_tml, cargo_toml).unwrap(); 170 | } 171 | } 172 | 173 | for entry in std::fs::read_dir("internal").unwrap() { 174 | let path_buf = entry.unwrap().path(); 175 | 176 | let shim_name = path_buf.clone(); 177 | let shim_name = shim_name 178 | .file_name() 179 | .unwrap() 180 | .to_os_string() 181 | .into_string() 182 | .unwrap(); 183 | 184 | let shim_out_dir = out_dir.join(&path_buf); 185 | 186 | let path: String = path_buf.into_os_string().into_string().unwrap(); 187 | 188 | println!("cargo:rerun-if-changed={}/Cargo.tml", path); 189 | println!("cargo:rerun-if-changed={}/Cargo.toml", path); 190 | println!("cargo:rerun-if-changed={}/Cargo.lock", path); 191 | println!("cargo:rerun-if-changed={}/layout.ld", path); 192 | println!("cargo:rerun-if-changed={}/.cargo/config", path); 193 | 194 | rerun_src(&path); 195 | 196 | if !shim_name.starts_with("shim-") { 197 | continue; 198 | } 199 | 200 | #[cfg(not(any(feature = "backend-kvm")))] 201 | if shim_name.starts_with("shim-sev") { 202 | continue; 203 | } 204 | 205 | #[cfg(not(feature = "backend-sgx"))] 206 | if shim_name.starts_with("shim-sgx") { 207 | continue; 208 | } 209 | 210 | let target_dir = shim_out_dir.clone().into_os_string().into_string().unwrap(); 211 | 212 | let stdout: Stdio = OpenOptions::new() 213 | .write(true) 214 | .open("/dev/tty") 215 | .map(Stdio::from) 216 | .unwrap_or_else(|_| Stdio::inherit()); 217 | 218 | let stderr: Stdio = OpenOptions::new() 219 | .write(true) 220 | .open("/dev/tty") 221 | .map(Stdio::from) 222 | .unwrap_or_else(|_| Stdio::inherit()); 223 | 224 | let status = Command::new("cargo") 225 | .current_dir(&path) 226 | .env_clear() 227 | .envs(&filtered_env) 228 | .stdout(stdout) 229 | .stderr(stderr) 230 | .arg("+nightly") 231 | .arg("build") 232 | .args(profile) 233 | .arg("--target-dir") 234 | .arg(&target_dir) 235 | .arg("--target") 236 | .arg(target_name) 237 | .arg("--bin") 238 | .arg(&shim_name) 239 | .status() 240 | .expect("failed to build shim"); 241 | 242 | if !status.success() { 243 | eprintln!("Failed to build shim {}", path); 244 | std::process::exit(1); 245 | } 246 | 247 | let out_bin = out_dir_bin.join(&shim_name); 248 | 249 | let shim_out_bin = shim_out_dir 250 | .join(&target_name) 251 | .join(&std::env::var("PROFILE").unwrap()) 252 | .join(&shim_name); 253 | 254 | let status = Command::new("strip") 255 | .arg("--strip-unneeded") 256 | .arg("-o") 257 | .arg(&out_bin) 258 | .arg(&shim_out_bin) 259 | .status(); 260 | 261 | match status { 262 | Ok(status) if status.success() => {} 263 | _ => { 264 | println!("cargo:warning=Failed to run `strip` on {:?}", &shim_out_bin); 265 | std::fs::rename(&shim_out_bin, &out_bin).expect("move failed") 266 | } 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | # This section is considered when running `cargo deny check licenses` 2 | # More documentation for the licenses section can be found here: 3 | # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html 4 | [licenses] 5 | # The lint level for crates which do not have a detectable license 6 | unlicensed = "deny" 7 | # List of explictly allowed licenses 8 | # See https://spdx.org/licenses/ for list of possible licenses 9 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. 10 | allow = [ 11 | "Apache-2.0", 12 | "Apache-2.0 WITH LLVM-exception", 13 | "BSD-2-Clause", 14 | "BSD-3-Clause", 15 | "CC0-1.0", 16 | "ISC", 17 | "MIT", 18 | "OpenSSL", 19 | "Zlib", 20 | ] 21 | # Lint level for licenses considered copyleft 22 | copyleft = "deny" 23 | # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses 24 | # * both - The license will be approved if it is both OSI-approved *AND* FSF 25 | # * either - The license will be approved if it is either OSI-approved *OR* FSF 26 | # * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF 27 | # * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved 28 | # * neither - This predicate is ignored and the default lint level is used 29 | allow-osi-fsf-free = "either" 30 | # Lint level used when no other predicates are matched 31 | # 1. License isn't in the allow or deny lists 32 | # 2. License isn't copyleft 33 | # 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" 34 | default = "deny" 35 | # The confidence threshold for detecting a license from license text. 36 | # The higher the value, the more closely the license text must be to the 37 | # canonical license text of a valid SPDX license file. 38 | # [possible values: any between 0.0 and 1.0]. 39 | confidence-threshold = 0.8 40 | 41 | [licenses.private] 42 | # If true, ignores workspace crates that aren't published, or are only 43 | # published to private registries 44 | ignore = false 45 | 46 | [[licenses.clarify]] 47 | name = "ring" 48 | version = "*" 49 | expression = "MIT AND ISC AND OpenSSL" 50 | license-files = [ 51 | { path = "LICENSE", hash = 0xbd0eed23 } 52 | ] 53 | -------------------------------------------------------------------------------- /helper/parse-trace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SHIM="$1" 4 | PAYLOAD="$2" 5 | 6 | if [[ -z $SHIM ]]; then 7 | echo "Usage: $0 []" 8 | fi 9 | 10 | strstr() { [[ $1 = *"$2"* ]]; } 11 | 12 | unset ADDR2LINE 13 | 14 | while read line; do 15 | if [[ $line = "panicked at"* ]]; then 16 | printf -- "%s\n" "$line" 17 | continue 18 | fi 19 | 20 | if [[ $line = "TRACE:"* ]]; then 21 | printf -- "%s\n" "$line" 22 | ADDR2LINE="TRACE" 23 | continue 24 | fi 25 | 26 | if [[ $line = "Error: Shutdown"* ]] || [[ $line = "Error: MmioRead"* ]]; then 27 | ADDR2LINE="REGS" 28 | continue 29 | fi 30 | 31 | if ! [[ $ADDR2LINE ]]; then 32 | printf -- "%s\n" "$line" 33 | continue 34 | fi 35 | 36 | if ! [[ $line ]]; then 37 | continue 38 | fi 39 | 40 | if [[ $line = ")" ]]; then 41 | unset ADDR2LINE 42 | fi 43 | 44 | if [[ $ADDR2LINE = "REGS" ]]; then 45 | if [[ $line = *"rflags:"* ]] || [[ $line = *"rsp:"* ]] || [[ $line = *"rbp:"* ]] || [[ $line = *"rbx:"* ]]; then 46 | continue 47 | fi 48 | line=${line%,} 49 | read _ line <<< "$line" 50 | line=${line/ffffff8} 51 | fi 52 | 53 | if [[ $ADDR2LINE = "TRACE" ]] && [[ $line = "P "* ]]; then 54 | if [[ $PAYLOAD ]]; then 55 | addr2line -apiCf -e "$PAYLOAD" $line | grep -F -v '??' | \ 56 | while read line; do 57 | printf -- "Payl: %s\n" "$line" 58 | done 59 | continue 60 | else 61 | printf -- "Payl: %s\n" "$line" 62 | continue 63 | fi 64 | fi 65 | 66 | if [[ $SHIM ]]; then 67 | addr2line -apiCf -e "$SHIM" $line | grep -F -v '??' | \ 68 | while read line; do 69 | printf -- "Shim: %s\n" "$line" 70 | done 71 | continue 72 | else 73 | printf -- "Shim: %s\n" "$line" 74 | continue 75 | fi 76 | 77 | done 78 | -------------------------------------------------------------------------------- /internal/shim-sev/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-linux-musl" 3 | rustflags = [ 4 | "-C", "relocation-model=pic", 5 | "-C", "link-args=-Wl,--sort-section=alignment,-Tlayout.ld -nostartfiles", 6 | "-C", "link-self-contained=no", 7 | "-C", "force-frame-pointers=yes", 8 | ] 9 | -------------------------------------------------------------------------------- /internal/shim-sev/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "array-const-fn-init" 7 | version = "0.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8bcb85e548c05d407fa6faff46b750ba287714ef32afc0f5e15b4641ffd6affb" 10 | 11 | [[package]] 12 | name = "bit_field" 13 | version = "0.10.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 16 | 17 | [[package]] 18 | name = "bitflags" 19 | version = "1.3.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 22 | 23 | [[package]] 24 | name = "compiler_builtins" 25 | version = "0.1.50" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "c4fd27448c11cdc03f9be9babc79e2aba19789a1db6fe0a1390d53f99f3f8fb1" 28 | 29 | [[package]] 30 | name = "crt0stack" 31 | version = "0.1.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "9274b445ee572d50bdeb17a1101be829becc565b5c12b21a697af4d360b48e8d" 34 | 35 | [[package]] 36 | name = "goblin" 37 | version = "0.4.3" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "32401e89c6446dcd28185931a01b1093726d0356820ac744023e6850689bf926" 40 | dependencies = [ 41 | "plain", 42 | "scroll", 43 | ] 44 | 45 | [[package]] 46 | name = "libc" 47 | version = "0.2.103" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" 50 | 51 | [[package]] 52 | name = "linked_list_allocator" 53 | version = "0.9.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "d0b725207570aa16096962d0b20c79f8a543df2280bd3c903022b9b0b4d7ea68" 56 | 57 | [[package]] 58 | name = "lock_api" 59 | version = "0.4.5" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 62 | dependencies = [ 63 | "scopeguard", 64 | ] 65 | 66 | [[package]] 67 | name = "lset" 68 | version = "0.2.0" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "efeae5282702b072b5e21cf8f430ccd3c5031c1e346321a28429523266c4a9b0" 71 | 72 | [[package]] 73 | name = "nbytes" 74 | version = "0.1.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "c619aa76dbb3f67970c7cf10fc3efa81da412be26d4dda1726af76b25260dc66" 77 | 78 | [[package]] 79 | name = "noted" 80 | version = "1.0.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "c9ed3de88cf5f12b1b461eb59a5b85f60d84216207bda41bf0f0eb2d7d51d397" 83 | 84 | [[package]] 85 | name = "plain" 86 | version = "0.2.3" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 89 | 90 | [[package]] 91 | name = "primordial" 92 | version = "0.3.0" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "55d6312462222758b3fb6c7e84d819ce87c315c446e0e2c11b0b9258dedd3f25" 95 | 96 | [[package]] 97 | name = "rcrt1" 98 | version = "0.1.0" 99 | source = "git+https://github.com/enarx/rcrt1?rev=b28f711#b28f711b4de0236053021878d83f11b5e48c4035" 100 | dependencies = [ 101 | "goblin", 102 | "libc", 103 | ] 104 | 105 | [[package]] 106 | name = "sallyport" 107 | version = "0.1.0" 108 | source = "git+https://github.com/enarx/sallyport?rev=a567a22665c7e5ba88a8c4acd64ab43ee32b4681#a567a22665c7e5ba88a8c4acd64ab43ee32b4681" 109 | dependencies = [ 110 | "goblin", 111 | "libc", 112 | "primordial", 113 | ] 114 | 115 | [[package]] 116 | name = "scopeguard" 117 | version = "1.1.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 120 | 121 | [[package]] 122 | name = "scroll" 123 | version = "0.10.2" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" 126 | 127 | [[package]] 128 | name = "shim-sev" 129 | version = "0.1.0" 130 | dependencies = [ 131 | "array-const-fn-init", 132 | "compiler_builtins", 133 | "crt0stack", 134 | "goblin", 135 | "libc", 136 | "linked_list_allocator", 137 | "lset", 138 | "nbytes", 139 | "noted", 140 | "primordial", 141 | "rcrt1", 142 | "sallyport", 143 | "spinning", 144 | "x86_64", 145 | ] 146 | 147 | [[package]] 148 | name = "spinning" 149 | version = "0.1.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" 152 | dependencies = [ 153 | "lock_api", 154 | ] 155 | 156 | [[package]] 157 | name = "volatile" 158 | version = "0.4.4" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "e4c2dbd44eb8b53973357e6e207e370f0c1059990df850aca1eca8947cf464f0" 161 | 162 | [[package]] 163 | name = "x86_64" 164 | version = "0.14.6" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "bbc6ed1ed2cd4536b083c34041aff7b84448ee25ac4aa5e9d54802ce226f9815" 167 | dependencies = [ 168 | "bit_field", 169 | "bitflags", 170 | "volatile", 171 | ] 172 | -------------------------------------------------------------------------------- /internal/shim-sev/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "shim-sev" 3 | version = "0.1.0" 4 | authors = ["Harald Hoyer "] 5 | edition = "2018" 6 | license = "Apache-2.0" 7 | 8 | [[bin]] 9 | name = "shim-sev" 10 | test = false 11 | 12 | [dependencies] 13 | sallyport = { git = "https://github.com/enarx/sallyport", rev = "a567a22665c7e5ba88a8c4acd64ab43ee32b4681", features = [ "asm" ] } 14 | rcrt1 = { git = "https://github.com/enarx/rcrt1", rev = "b28f711" } 15 | compiler_builtins = { version = "0.1", default-features = false, features = [ "mem" ] } 16 | x86_64 = { version = "^0.14.6", default-features = false, features = ["instructions", "inline_asm"] } 17 | goblin = { version = "0.4", default-features = false, features = [ "elf64" ] } 18 | crt0stack = { version = "0.1", default-features = false } 19 | spinning = { version = "0.1", default-features = false } 20 | libc = { version = "0.2", default-features = false } 21 | primordial = "0.3" 22 | noted = "^1.0.0" 23 | nbytes = "0.1" 24 | lset = "0.2" 25 | array-const-fn-init = "0.1" 26 | linked_list_allocator = { version = "0.9.0", default-features = false } 27 | 28 | [profile.dev.package.rcrt1] 29 | opt-level = 3 30 | 31 | [profile.dev] 32 | panic = "abort" 33 | 34 | [profile.release] 35 | panic = "abort" 36 | codegen-units = 1 37 | incremental = false 38 | lto = true 39 | debug = 1 40 | opt-level = "s" 41 | -------------------------------------------------------------------------------- /internal/shim-sev/build.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | fn main() { 4 | println!("cargo:rerun-if-changed=layout.ld"); 5 | } 6 | -------------------------------------------------------------------------------- /internal/shim-sev/layout.ld: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Apache-2.0 */ 2 | 3 | ENTRY(_start) 4 | EXTERN(__SALLYPORT_ABI_) 5 | 6 | PHDRS { 7 | sallyport PT_LOAD FLAGS(1 << 22); /* sallyport::elf::pf::kvm::SALLYPORT */ 8 | 9 | PML3 PT_LOAD; 10 | PML4 PT_LOAD; 11 | 12 | boot_text PT_LOAD; 13 | boot_data PT_LOAD; 14 | 15 | text PT_LOAD; 16 | rodata PT_LOAD; 17 | data PT_LOAD; 18 | 19 | dynamic PT_DYNAMIC; 20 | note PT_NOTE; 21 | 22 | exec 0x63400000 FLAGS(0); 23 | } 24 | 25 | /* Configure this to a reasonable size 26 | Current block size is 69632, which gives a maximum count of 15420 27 | for the 3GB - 4GB range. 28 | */ 29 | _ENARX_SALLYPORT_BLOCK_COUNT = 2; 30 | _ENARX_SALLYPORT_BLOCK_SIZE = 69632; 31 | 32 | _ENARX_SALLYPORT_SIZE = ALIGN(_ENARX_SALLYPORT_BLOCK_COUNT * _ENARX_SALLYPORT_BLOCK_SIZE, CONSTANT(COMMONPAGESIZE)); 33 | 34 | ASSERT((_ENARX_SALLYPORT_SIZE < (0x40000000 - 2 * CONSTANT(COMMONPAGESIZE))), "_ENARX_SALLYPORT_SIZE too big") 35 | 36 | reset_vector = 0xFFFFF000; 37 | _ENARX_SHIM_START = reset_vector; 38 | _ENARX_SALLYPORT_START = _ENARX_SHIM_START - _ENARX_SALLYPORT_SIZE - 2 * CONSTANT(COMMONPAGESIZE); 39 | _ENARX_SALLYPORT_END = _ENARX_SALLYPORT_START + _ENARX_SALLYPORT_SIZE; 40 | _ENARX_EXEC_LEN = 128M; 41 | 42 | ASSERT((_ENARX_SHIM_START >= (3 * 0x40000000)), "SHIM_START is too low for current initial identity page table") 43 | ASSERT((_ENARX_EXEC_START < (6 * 0x40000000)), "SHIM is too large for current initial identity page table") 44 | 45 | ASSERT((pml4t_ident == (reset_vector - CONSTANT(COMMONPAGESIZE))), "pml4t_ident not at 0xFFFFE000") 46 | ASSERT((pml3t_ident == (reset_vector - 2*CONSTANT(COMMONPAGESIZE))), "pml3t_ident not at 0xFFFFD000") 47 | 48 | SECTIONS { 49 | . = _ENARX_SALLYPORT_START; 50 | _ENARX_MEM_START = ABSOLUTE(.); 51 | 52 | .sallyport (NOLOAD) : ALIGN(CONSTANT(COMMONPAGESIZE)) { 53 | . += _ENARX_SALLYPORT_SIZE; 54 | } :sallyport 55 | 56 | .pml3 : ALIGN(CONSTANT(COMMONPAGESIZE)) { 57 | PROVIDE_HIDDEN(pml3t_ident = ABSOLUTE(.)); 58 | QUAD(0); 59 | QUAD(0); 60 | QUAD(0); 61 | QUAD(3 * 0x40000000 + 0x83); /* Flags::HUGE_PAGE | Flags::WRITABLE | Flags::PRESENT */ 62 | QUAD(4 * 0x40000000 + 0x83); /* Flags::HUGE_PAGE | Flags::WRITABLE | Flags::PRESENT */ 63 | QUAD(5 * 0x40000000 + 0x83); /* Flags::HUGE_PAGE | Flags::WRITABLE | Flags::PRESENT */ 64 | FILL(0); 65 | . = CONSTANT(COMMONPAGESIZE); 66 | } :PML3 67 | 68 | .pml4 : ALIGN(CONSTANT(COMMONPAGESIZE)) { 69 | PROVIDE_HIDDEN(pml4t_ident = ABSOLUTE(.)); 70 | QUAD(pml3t_ident + 0x3); /* Flags::WRITABLE | Flags::PRESENT */ 71 | FILL(0); 72 | . = CONSTANT(COMMONPAGESIZE); 73 | } :PML4 74 | 75 | . = _ENARX_SHIM_START; 76 | 77 | .reset_text : { KEEP(*(.reset)) } :boot_text 78 | .rcrt1_text : { rcrt1*.text rcrt1*.text.* } :boot_text 79 | 80 | . = ALIGN(CONSTANT(COMMONPAGESIZE)); 81 | .dynamic : { *(.dynamic) } :boot_data :dynamic 82 | .boot_data : { KEEP(*(.entry64_data)) rcrt1*.data rcrt1*.data.* } :boot_data 83 | .data.rel.ro : { *(.data.rel.ro .data.rel.ro.*) } :boot_data 84 | .got : { *(.got) } :boot_data 85 | 86 | . = ALIGN(CONSTANT(COMMONPAGESIZE)); 87 | .text : { *(.text .text.*) } :text 88 | .rela.dyn : { *(.rela.*) } :text 89 | 90 | . = ALIGN(CONSTANT(COMMONPAGESIZE)); 91 | .rodata : { *(.rodata .rodata.*) } :rodata 92 | .note : { *(.note) } :rodata :note 93 | .dynsym : { *(.dynsym) } :rodata 94 | .dynstr : { *(.dynstr) } :rodata 95 | .gnu.hash : { *(.gnu.hash) } :rodata 96 | 97 | . = ALIGN(CONSTANT(COMMONPAGESIZE)); 98 | .data : { *(.data .data.*) } :data 99 | .bss : { *(.bss .bss.*) } :data 100 | 101 | .code : ALIGN(CONSTANT(COMMONPAGESIZE)) { 102 | _ENARX_EXEC_START = ABSOLUTE(.); 103 | FILL(0); 104 | . += _ENARX_EXEC_LEN; 105 | } :exec 106 | _ENARX_EXEC_END = .; 107 | 108 | /DISCARD/ : { 109 | *(.eh_frame*) 110 | *(.note.GNU-stack) 111 | *(.gnu_debuglink) 112 | *(.interp) 113 | *(.gnu.hash) 114 | *(.hash) 115 | *(.comment) 116 | *(COMMON) 117 | *(.note.gnu.build-id) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /internal/shim-sev/src/addr.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Some basic address operations 4 | 5 | /// The offset of shim virtual address space to the physical address 6 | /// 7 | /// physical address + `SHIM_VIRT_OFFSET` = shim virtual address 8 | /// 9 | /// FIXME: change to dynamic offset with ASLR https://github.com/enarx/enarx/issues/624 10 | pub const SHIM_VIRT_OFFSET: u64 = 0xFFFF_FF80_0000_0000; 11 | 12 | /// 2 MiB 13 | #[allow(clippy::integer_arithmetic)] 14 | pub const BYTES_2_MIB: u64 = bytes![2; MiB]; 15 | 16 | /// 1 GiB 17 | #[allow(clippy::integer_arithmetic)] 18 | pub const BYTES_1_GIB: u64 = bytes![1; GiB]; 19 | 20 | use crate::get_cbit_mask; 21 | use crate::hostmap::HOSTMAP; 22 | use core::convert::TryFrom; 23 | use core::ops::Range; 24 | use nbytes::bytes; 25 | use primordial::{Address, Register}; 26 | use x86_64::PhysAddr; 27 | 28 | /// Address in the host virtual address space 29 | pub struct HostVirtAddr(Address); 30 | 31 | impl HostVirtAddr { 32 | /// Create a new HostVirtAddr 33 | /// 34 | /// # Safety 35 | /// The caller has to ensure, that the address is in the hosts virtual address space 36 | pub unsafe fn new(val: Address) -> Self { 37 | Self(val) 38 | } 39 | } 40 | 41 | impl From> for HostVirtAddr { 42 | #[inline(always)] 43 | fn from(val: ShimPhysUnencryptedAddr) -> Self { 44 | HOSTMAP.shim_phys_to_host_virt(val) 45 | } 46 | } 47 | 48 | impl From> for Register 49 | where 50 | Register: From>, 51 | { 52 | #[inline(always)] 53 | fn from(val: HostVirtAddr) -> Self { 54 | Register::::from(val.0).into() 55 | } 56 | } 57 | 58 | /// Address in the shim physical address space 59 | pub struct ShimPhysAddr(Address); 60 | 61 | impl From> for ShimPhysAddr 62 | where 63 | Address: Into>, 64 | { 65 | #[inline(always)] 66 | fn from(val: Address) -> Self { 67 | let mut val: Address = val.into(); 68 | val = unsafe { Address::unchecked(val.raw() | get_cbit_mask()) }; 69 | Self(val) 70 | } 71 | } 72 | 73 | impl TryFrom for ShimPhysAddr { 74 | type Error = (); 75 | 76 | fn try_from(value: PhysAddr) -> Result { 77 | Address::::from(value.as_u64()) 78 | .try_cast::() 79 | .map(|val| { 80 | ShimPhysAddr::from(unsafe { Address::unchecked(val.raw() | get_cbit_mask()) }) 81 | }) 82 | .map_err(|_| ()) 83 | } 84 | } 85 | 86 | impl ShimPhysAddr { 87 | /// Get the raw address 88 | pub fn raw(self) -> Address { 89 | self.0 90 | } 91 | } 92 | 93 | /// Address in the shim virtual address space 94 | #[derive(Clone)] 95 | pub struct ShimVirtAddr(Address); 96 | 97 | impl TryFrom> for ShimVirtAddr { 98 | type Error = (); 99 | 100 | #[inline(always)] 101 | fn try_from(value: Address) -> Result { 102 | let value = value.raw(); 103 | 104 | if value < SHIM_VIRT_OFFSET { 105 | return Err(()); 106 | } 107 | 108 | Ok(Self(unsafe { Address::unchecked(value) })) 109 | } 110 | } 111 | 112 | impl From> for *const U { 113 | #[inline(always)] 114 | fn from(shim_virt_addr: ShimVirtAddr) -> Self { 115 | shim_virt_addr.0.raw() as _ 116 | } 117 | } 118 | 119 | impl From> for *mut U { 120 | #[inline(always)] 121 | fn from(shim_virt_addr: ShimVirtAddr) -> Self { 122 | shim_virt_addr.0.raw() as _ 123 | } 124 | } 125 | 126 | impl From> for ShimVirtAddr { 127 | #[inline(always)] 128 | fn from(shim_phys_addr: ShimPhysAddr) -> Self { 129 | // Safety: checked, that it is in the shim virtual address space earlier 130 | #[allow(clippy::integer_arithmetic)] 131 | ShimVirtAddr(unsafe { 132 | Address::unchecked((shim_phys_addr.0.raw() & (!get_cbit_mask())) + SHIM_VIRT_OFFSET) 133 | }) 134 | } 135 | } 136 | 137 | impl TryFrom> for ShimPhysAddr { 138 | type Error = (); 139 | 140 | #[inline(always)] 141 | fn try_from(value: ShimVirtAddr) -> Result { 142 | #[allow(clippy::integer_arithmetic)] 143 | let value = value.0.raw(); 144 | let value = value.checked_sub(SHIM_VIRT_OFFSET).ok_or(())?; 145 | 146 | Ok(Self(unsafe { Address::unchecked(value) })) 147 | } 148 | } 149 | 150 | /// Address in the shim virtual address space 151 | pub struct ShimPhysUnencryptedAddr(Address); 152 | 153 | impl ShimPhysUnencryptedAddr { 154 | /// Get the raw address 155 | pub fn raw(self) -> Address { 156 | self.0 157 | } 158 | 159 | /// convert to mutable reference 160 | pub fn into_mut<'a>(self) -> &'a mut U { 161 | unsafe { &mut *(self.0.raw() as *mut U) } 162 | } 163 | } 164 | 165 | impl TryFrom> for ShimPhysUnencryptedAddr { 166 | type Error = (); 167 | 168 | #[inline(always)] 169 | fn try_from(value: ShimVirtAddr) -> Result { 170 | #[allow(clippy::integer_arithmetic)] 171 | let value = value.0.raw(); 172 | 173 | if !(Range { 174 | start: unsafe { &crate::_ENARX_SALLYPORT_START as *const _ as u64 }, 175 | end: unsafe { &crate::_ENARX_SALLYPORT_END as *const _ as u64 }, 176 | }) 177 | .contains(&value) 178 | { 179 | return Err(()); 180 | } 181 | 182 | let value = value.checked_sub(SHIM_VIRT_OFFSET).ok_or(())? & (!get_cbit_mask()); 183 | 184 | Ok(Self(unsafe { Address::unchecked(value) })) 185 | } 186 | } 187 | 188 | impl TryFrom> for ShimPhysUnencryptedAddr { 189 | type Error = (); 190 | 191 | #[inline(always)] 192 | fn try_from(value: Address) -> Result { 193 | let value = value.raw(); 194 | 195 | if (value & get_cbit_mask()) != 0 { 196 | return Err(()); 197 | } 198 | 199 | Ok(Self(unsafe { Address::unchecked(value) })) 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /internal/shim-sev/src/asm.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Functions needing `asm!` blocks 4 | 5 | use crate::addr::SHIM_VIRT_OFFSET; 6 | use core::mem::size_of; 7 | use x86_64::instructions::tables::lidt; 8 | use x86_64::structures::DescriptorTablePointer; 9 | use x86_64::VirtAddr; 10 | 11 | /// Debug helper function for the early boot 12 | /// 13 | /// # Safety 14 | /// 15 | /// This function causes a triple fault! 16 | #[inline(never)] 17 | pub unsafe fn _early_debug_panic(reason: u64, value: u64) -> ! { 18 | let mut rbp: u64; 19 | 20 | asm!("mov {}, rbp", out(reg) rbp); 21 | 22 | load_invalid_idt(); 23 | 24 | let frames = backtrace(rbp); 25 | 26 | // Provoke an #UD, which will lead to a triple fault, because of the invalid IDT 27 | asm!("ud2", 28 | in("rax") frames[0], 29 | in("rcx") frames[1], 30 | in("rdx") frames[2], 31 | in("rsi") frames[3], 32 | in("rdi") frames[4], 33 | in("r8") frames[5], 34 | in("r9") frames[6], 35 | in("r10") frames[7], 36 | in("r11") frames[8], 37 | in("r12") frames[9], 38 | in("r13") frames[10], 39 | in("r14") reason, 40 | in("r15") value, 41 | options(nomem, nostack) 42 | ); 43 | 44 | // Extra hlt loop, in case hell freezes 45 | loop { 46 | x86_64::instructions::hlt() 47 | } 48 | } 49 | 50 | /// Provoke a triple fault to shutdown the machine 51 | /// 52 | /// An illegal IDT is loaded with limit=0 and an #UD is produced 53 | /// 54 | /// Fun read: http://www.rcollins.org/Productivity/TripleFault.html 55 | /// 56 | /// # Safety 57 | /// 58 | /// This function causes a triple fault! 59 | #[inline(never)] 60 | pub unsafe fn _enarx_asm_triple_fault() -> ! { 61 | let mut rbp: u64; 62 | 63 | asm!("mov {}, rbp", out(reg) rbp); 64 | 65 | let frames = backtrace(rbp); 66 | 67 | load_invalid_idt(); 68 | 69 | // Provoke an #UD, which will lead to a triple fault, because of the invalid IDT 70 | asm!("ud2", 71 | in("rax") frames[0], 72 | in("rcx") frames[1], 73 | in("rdx") frames[2], 74 | in("rsi") frames[3], 75 | in("rdi") frames[4], 76 | in("r8") frames[5], 77 | in("r9") frames[6], 78 | in("r10") frames[7], 79 | in("r11") frames[8], 80 | in("r12") frames[9], 81 | in("r13") frames[10], 82 | in("r14") frames[11], 83 | in("r15") frames[12], 84 | options(nomem, nostack) 85 | ); 86 | 87 | // Extra hlt loop, in case hell freezes 88 | loop { 89 | x86_64::instructions::hlt() 90 | } 91 | } 92 | 93 | /// Load an invalid DescriptorTablePointer with no base and limit 94 | #[inline(always)] 95 | unsafe fn load_invalid_idt() { 96 | let dtp = DescriptorTablePointer { 97 | limit: 0, 98 | base: VirtAddr::new(0), 99 | }; 100 | // Load the invalid IDT 101 | lidt(&dtp); 102 | } 103 | 104 | /// Produce a backtrace from a frame pointer 105 | #[inline(always)] 106 | unsafe fn backtrace(mut rbp: u64) -> [u64; 16] { 107 | let mut frames = [0u64; 16]; 108 | 109 | for ele in frames.iter_mut() { 110 | if let Some(rip_rbp) = rbp.checked_add(size_of::() as _) { 111 | let rip = *(rip_rbp as *const u64); 112 | if let Some(rip) = rip 113 | .checked_sub(SHIM_VIRT_OFFSET) 114 | .and_then(|v| v.checked_sub(1)) 115 | { 116 | *ele = rip; 117 | rbp = *(rbp as *const u64); 118 | } else { 119 | // Not a shim virtual address 120 | break; 121 | } 122 | } else { 123 | // RBP OVERFLOW 124 | break; 125 | } 126 | } 127 | frames 128 | } 129 | -------------------------------------------------------------------------------- /internal/shim-sev/src/attestation.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! SEV attestation handling 4 | 5 | use core::hint::unreachable_unchecked; 6 | use core::mem::MaybeUninit; 7 | use nbytes::bytes; 8 | 9 | /// The maximum size of the injected secret for SEV keeps 10 | #[allow(clippy::integer_arithmetic)] 11 | pub const SEV_SECRET_MAX_SIZE: usize = bytes!(16; KiB); 12 | 13 | /// A 16 byte aligned SevSecret with unknown content 14 | #[repr(C, align(16))] 15 | #[derive(Copy, Clone, Debug)] 16 | pub struct SevSecret { 17 | /// the secret byte blob 18 | pub data: MaybeUninit<[u8; SEV_SECRET_MAX_SIZE]>, 19 | } 20 | 21 | impl Default for SevSecret { 22 | fn default() -> Self { 23 | Self { 24 | data: MaybeUninit::uninit(), 25 | } 26 | } 27 | } 28 | 29 | impl SevSecret { 30 | #[allow(clippy::integer_arithmetic)] 31 | unsafe fn cbor_len(data: *const u8) -> Option { 32 | let prefix = data.read(); 33 | 34 | // only accept CBOR BYTES type 35 | if (prefix >> 5) != 2 { 36 | return None; 37 | } 38 | 39 | // mask the minor 40 | match prefix & 0b00011111 { 41 | x @ 0..=23 => Some(1 + x as usize), 42 | 24 => Some(1 + 1 + data.add(1).read() as usize), 43 | 25 => { 44 | let data = data.add(1) as *const [u8; 2]; 45 | Some(1 + 2 + u16::from_be_bytes(data.read()) as usize) 46 | } 47 | 26 => { 48 | let data = data.add(1) as *const [u8; 4]; 49 | Some(1 + 4 + u32::from_be_bytes(data.read()) as usize) 50 | } 51 | 27 => { 52 | let data = data.add(1) as *const [u8; 8]; 53 | Some(1 + 8 + u64::from_be_bytes(data.read()) as usize) 54 | } 55 | 28 => None, 56 | 29 => None, 57 | 30 => None, 58 | 31 => None, 59 | 32..=255 => unreachable_unchecked(), 60 | } 61 | } 62 | 63 | /// get the length of the secret 64 | #[allow(dead_code)] 65 | pub fn try_len(&self) -> Option { 66 | let len = unsafe { SevSecret::cbor_len(self.data.as_ptr() as _) }; 67 | len.filter(|len| *len <= SEV_SECRET_MAX_SIZE) 68 | } 69 | 70 | /// Get a slice of the secret 71 | #[allow(dead_code)] 72 | pub fn try_as_slice(&self) -> Option<&[u8]> { 73 | self.try_len() 74 | .map(|len| &unsafe { &*self.data.as_ptr() }[..len]) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /internal/shim-sev/src/gdt.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Global Descriptor Table init 4 | 5 | use crate::shim_stack::{init_stack_with_guard, GuardedStack}; 6 | use crate::syscall::_syscall_enter; 7 | use core::ops::Deref; 8 | use nbytes::bytes; 9 | use spinning::Lazy; 10 | use x86_64::instructions::segmentation::{Segment, CS, DS, ES, FS, GS, SS}; 11 | use x86_64::instructions::tables::load_tss; 12 | use x86_64::registers::model_specific::{KernelGsBase, LStar, SFMask, Star}; 13 | use x86_64::registers::rflags::RFlags; 14 | use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; 15 | use x86_64::structures::paging::{Page, PageTableFlags, Size2MiB, Size4KiB}; 16 | use x86_64::structures::tss::TaskStateSegment; 17 | use x86_64::{align_up, PrivilegeLevel, VirtAddr}; 18 | 19 | /// The virtual address of the main kernel stack 20 | pub const SHIM_STACK_START: u64 = 0xFFFF_FF48_4800_0000; 21 | 22 | /// The size of the main kernel stack 23 | #[allow(clippy::integer_arithmetic)] 24 | pub const SHIM_STACK_SIZE: u64 = bytes![2; MiB]; 25 | 26 | /// The virtual address of the exception kernel stacks 27 | pub const SHIM_EX_STACK_START: u64 = 0xFFFF_FF48_F000_0000; 28 | 29 | /// The size of the main kernel stack for exceptions 30 | #[allow(clippy::integer_arithmetic)] 31 | pub const SHIM_EX_STACK_SIZE: u64 = bytes![32; KiB]; 32 | 33 | /// The initial shim stack 34 | pub static INITIAL_STACK: Lazy = Lazy::new(|| { 35 | init_stack_with_guard( 36 | VirtAddr::new(SHIM_STACK_START), 37 | SHIM_STACK_SIZE, 38 | PageTableFlags::empty(), 39 | ) 40 | }); 41 | 42 | /// The global TSS 43 | pub static TSS: Lazy = Lazy::new(|| { 44 | let mut tss = TaskStateSegment::new(); 45 | 46 | tss.privilege_stack_table[0] = INITIAL_STACK.pointer; 47 | 48 | let ptr_interrupt_stack_table = core::ptr::addr_of_mut!(tss.interrupt_stack_table); 49 | let mut interrupt_stack_table = unsafe { ptr_interrupt_stack_table.read_unaligned() }; 50 | 51 | // Assign the stacks for the exceptions and interrupts 52 | interrupt_stack_table 53 | .iter_mut() 54 | .enumerate() 55 | .for_each(|(idx, p)| { 56 | let offset: u64 = align_up( 57 | SHIM_EX_STACK_SIZE 58 | .checked_add(Page::::SIZE.checked_mul(2).unwrap()) 59 | .unwrap(), 60 | Page::::SIZE, 61 | ); 62 | 63 | let stack_offset = offset.checked_mul(idx as _).unwrap(); 64 | let start = VirtAddr::new(SHIM_EX_STACK_START.checked_add(stack_offset).unwrap()); 65 | 66 | *p = init_stack_with_guard(start, SHIM_EX_STACK_SIZE, PageTableFlags::empty()).pointer; 67 | }); 68 | 69 | unsafe { 70 | ptr_interrupt_stack_table.write_unaligned(interrupt_stack_table); 71 | } 72 | 73 | tss 74 | }); 75 | 76 | /// The Selectors used in the GDT setup 77 | pub struct Selectors { 78 | /// shim code selector 79 | pub code: SegmentSelector, 80 | /// shim data selector 81 | pub data: SegmentSelector, 82 | /// payload data selector 83 | pub user_data: SegmentSelector, 84 | /// payload code selector 85 | pub user_code: SegmentSelector, 86 | /// TSS selector 87 | pub tss: SegmentSelector, 88 | } 89 | 90 | /// The global GDT 91 | pub static GDT: Lazy<(GlobalDescriptorTable, Selectors)> = Lazy::new(|| { 92 | let mut gdt = GlobalDescriptorTable::new(); 93 | 94 | // `syscall` loads segments from STAR MSR assuming a data_segment follows `kernel_code_segment` 95 | // so the ordering is crucial here. Star::write() will panic otherwise later. 96 | let code = gdt.add_entry(Descriptor::kernel_code_segment()); 97 | 98 | let data = gdt.add_entry(Descriptor::kernel_data_segment()); 99 | 100 | // `sysret` loads segments from STAR MSR assuming `user_code_segment` follows `user_data_segment` 101 | // so the ordering is crucial here. Star::write() will panic otherwise later. 102 | let user_data = gdt.add_entry(Descriptor::user_data_segment()); 103 | debug_assert_eq!(USER_DATA_SEGMENT, user_data.0 as u64); 104 | 105 | let user_code = gdt.add_entry(Descriptor::user_code_segment()); 106 | debug_assert_eq!(USER_CODE_SEGMENT, user_code.0 as u64); 107 | 108 | // Important: TSS.deref() != &TSS because of lazy_static 109 | let tss = gdt.add_entry(Descriptor::tss_segment(TSS.deref())); 110 | 111 | let selectors = Selectors { 112 | code, 113 | data, 114 | user_data, 115 | user_code, 116 | tss, 117 | }; 118 | 119 | (gdt, selectors) 120 | }); 121 | 122 | /// The user data segment 123 | /// 124 | /// For performance and simplicity reasons, this is a constant 125 | /// in the assembler code and here for debug_assert!() 126 | const USER_DATA_SEGMENT_INDEX: u64 = 3; 127 | /// The User Data Segment as a constant to be used in asm!() blocks 128 | pub const USER_DATA_SEGMENT: u64 = (USER_DATA_SEGMENT_INDEX << 3) | (PrivilegeLevel::Ring3 as u64); 129 | 130 | /// The user code segment 131 | /// 132 | /// For performance and simplicity reasons, this is a constant 133 | /// in the assembler code and here for debug_assert!() 134 | const USER_CODE_SEGMENT_INDEX: u64 = 4; 135 | /// The User Code Segment as a constant to be used in asm!() blocks 136 | pub const USER_CODE_SEGMENT: u64 = (USER_CODE_SEGMENT_INDEX << 3) | (PrivilegeLevel::Ring3 as u64); 137 | 138 | /// Initialize the GDT 139 | /// 140 | /// # Safety 141 | /// 142 | /// `unsafe` because the caller has to ensure it is only called once 143 | /// and in a single-threaded context. 144 | pub unsafe fn init() { 145 | #[cfg(debug_assertions)] 146 | crate::eprintln!("init_gdt"); 147 | 148 | GDT.0.load(); 149 | 150 | // Setup the segment registers with the corresponding selectors 151 | CS::set_reg(GDT.1.code); 152 | SS::set_reg(GDT.1.data); 153 | load_tss(GDT.1.tss); 154 | 155 | // Clear the other segment registers 156 | SS::set_reg(SegmentSelector(0)); 157 | DS::set_reg(SegmentSelector(0)); 158 | ES::set_reg(SegmentSelector(0)); 159 | FS::set_reg(SegmentSelector(0)); 160 | GS::set_reg(SegmentSelector(0)); 161 | 162 | // Set the selectors to be set when userspace uses `syscall` 163 | Star::write(GDT.1.user_code, GDT.1.user_data, GDT.1.code, GDT.1.data).unwrap(); 164 | 165 | // Set the pointer to the function to be called when userspace uses `syscall` 166 | LStar::write(VirtAddr::new(_syscall_enter as usize as u64)); 167 | 168 | // Clear trap flag and interrupt enable 169 | SFMask::write(RFlags::INTERRUPT_FLAG | RFlags::TRAP_FLAG); 170 | 171 | // Set the kernel gs base to the TSS to be used in `_syscall_enter` 172 | // Important: TSS.deref() != &TSS because of lazy_static 173 | KernelGsBase::write(VirtAddr::new(TSS.deref() as *const _ as u64)); 174 | } 175 | -------------------------------------------------------------------------------- /internal/shim-sev/src/hostcall.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Host <-> Shim Communication 4 | 5 | use crate::addr::{HostVirtAddr, ShimPhysUnencryptedAddr, ShimVirtAddr}; 6 | use crate::asm::_enarx_asm_triple_fault; 7 | use crate::spin::RwLocked; 8 | use array_const_fn_init::array_const_fn_init; 9 | use core::convert::TryFrom; 10 | use core::mem::size_of; 11 | use primordial::{Address, Register}; 12 | use sallyport::syscall::enarx::MemInfo; 13 | use sallyport::syscall::{SYS_ENARX_BALLOON_MEMORY, SYS_ENARX_MEM_INFO}; 14 | use sallyport::KVM_SYSCALL_TRIGGER_PORT; 15 | use sallyport::{request, Block}; 16 | use spinning::Lazy; 17 | use x86_64::instructions::port::Port; 18 | use x86_64::PhysAddr; 19 | 20 | /// Host file descriptor 21 | #[derive(Copy, Clone)] 22 | pub struct HostFd(libc::c_int); 23 | 24 | impl HostFd { 25 | /// Extracts the raw file descriptor. 26 | /// 27 | /// This method does **not** pass ownership of the raw file descriptor 28 | /// to the caller. The descriptor is only guaranteed to be valid while 29 | /// the original object has not yet been destroyed. 30 | pub fn as_raw_fd(self) -> libc::c_int { 31 | self.0 32 | } 33 | 34 | /// Constructs a new instance of `Self` from the given raw file 35 | /// descriptor. 36 | /// 37 | /// # Safety 38 | /// 39 | /// This function is unsafe as the primitives currently returned 40 | /// have the contract that they are the sole owner of the file 41 | /// descriptor they are wrapping. Usage of this function could 42 | /// accidentally allow violating this contract which can cause memory 43 | /// unsafety in code that relies on it being true. 44 | pub unsafe fn from_raw_fd(fd: libc::c_int) -> Self { 45 | Self(fd) 46 | } 47 | } 48 | 49 | const MAX_BLOCK_NR: usize = 512; 50 | 51 | fn return_empty_option(_i: usize) -> Option<&'static mut Block> { 52 | None 53 | } 54 | 55 | /// The static HostCall Mutex 56 | pub static HOST_CALL_ALLOC: Lazy> = Lazy::new(|| { 57 | let block_mut: *mut Block = unsafe { 58 | let address = 59 | Address::::from(&crate::_ENARX_SALLYPORT_START as *const _ as *const Block); 60 | let shim_virt = ShimVirtAddr::::try_from(address).unwrap(); 61 | 62 | ShimPhysUnencryptedAddr::::try_from(shim_virt) 63 | .unwrap() 64 | .into_mut() as *mut _ 65 | }; 66 | 67 | let nr_syscall_blocks = unsafe { 68 | (&crate::_ENARX_SALLYPORT_END as *const _ as usize 69 | - &crate::_ENARX_SALLYPORT_START as *const _ as usize) 70 | / size_of::() 71 | }; 72 | 73 | assert!(nr_syscall_blocks <= 512); 74 | 75 | let mut hostcall_allocator = HostCallAllocator(array_const_fn_init![return_empty_option; 512]); 76 | 77 | for i in 0..nr_syscall_blocks { 78 | (hostcall_allocator.0)[i].replace(unsafe { &mut *block_mut.add(i) }); 79 | } 80 | 81 | RwLocked::::new(hostcall_allocator) 82 | }); 83 | 84 | /// Allocator for all `sallyport::Block` 85 | pub struct HostCallAllocator([Option<&'static mut Block>; MAX_BLOCK_NR]); 86 | 87 | impl RwLocked { 88 | /// Try to allocate a `HostCall` object to use a `sallyport::Block` 89 | pub fn try_alloc(&self) -> Option { 90 | let mut this = self.write(); 91 | this.0 92 | .iter_mut() 93 | .enumerate() 94 | .find(|(_i, x)| x.is_some()) 95 | .map(|(i, ele)| HostCall { 96 | block_index: i as _, 97 | block: ele.take(), 98 | }) 99 | } 100 | } 101 | 102 | /// Communication with the Host 103 | pub struct HostCall { 104 | block_index: u16, 105 | block: Option<&'static mut Block>, 106 | } 107 | 108 | impl Drop for HostCall { 109 | fn drop(&mut self) { 110 | HOST_CALL_ALLOC.write().0[self.block_index as usize] = self.block.take(); 111 | } 112 | } 113 | 114 | impl HostCall { 115 | /// Causes a `#VMEXIT` for the host to process the data in the shared memory 116 | /// 117 | /// Returns the contents of the shared memory reply status, the host might have 118 | /// written. 119 | /// 120 | /// # Safety 121 | /// 122 | /// The parameters returned can't be trusted. 123 | #[inline(always)] 124 | pub unsafe fn hostcall(&mut self) -> sallyport::Result { 125 | let mut port = Port::::new(KVM_SYSCALL_TRIGGER_PORT); 126 | 127 | // prevent earlier writes from being moved beyond this point 128 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::Release); 129 | 130 | port.write(self.block_index); 131 | 132 | // prevent later reads from being moved before this point 133 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::Acquire); 134 | 135 | self.block.as_mut().unwrap().msg.rep.into() 136 | } 137 | 138 | /// Return reference to the inner `Block` 139 | pub fn as_block(&self) -> &Block { 140 | self.block.as_ref().unwrap() 141 | } 142 | 143 | /// Return mutable reference to the inner `Block` 144 | pub fn as_mut_block(&mut self) -> &mut Block { 145 | self.block.as_mut().unwrap() 146 | } 147 | 148 | /// Write `bytes` to a host file descriptor `fd` 149 | /// 150 | /// Write at most `Block::buf_capacity()` bytes. 151 | /// Handle it like write(2) and call it in a loop until all bytes are written. 152 | /// 153 | /// # Safety 154 | /// 155 | /// The parameters returned can't be trusted. 156 | pub unsafe fn write(&mut self, fd: libc::c_int, bytes: &[u8]) -> sallyport::Result { 157 | let cursor = self.block.as_mut().unwrap().cursor(); 158 | let (_, buf) = cursor.copy_from_slice(bytes).or(Err(libc::EMSGSIZE))?; 159 | 160 | let buf_address = Address::from(buf.as_ptr()); 161 | let phys_unencrypted = ShimPhysUnencryptedAddr::try_from(buf_address).unwrap(); 162 | let host_virt: HostVirtAddr<_> = phys_unencrypted.into(); 163 | 164 | self.block.as_mut().unwrap().msg.req = 165 | request!(libc::SYS_write => fd, host_virt, buf.len()); 166 | self.hostcall() 167 | } 168 | 169 | /// Balloon the memory 170 | pub fn balloon(&mut self, pages: usize, gpa: PhysAddr) -> Result { 171 | self.block.as_mut().unwrap().msg.req = 172 | request!(SYS_ENARX_BALLOON_MEMORY => 12, pages, gpa.as_u64()); 173 | Ok(unsafe { self.hostcall() }?[0].into()) 174 | } 175 | 176 | /// Get host memory info 177 | pub fn mem_info(&mut self) -> Result { 178 | self.block.as_mut().unwrap().msg.req = request!(SYS_ENARX_MEM_INFO); 179 | 180 | let _result = unsafe { self.hostcall() }?; 181 | 182 | let block = self.as_mut_block(); 183 | let c = block.cursor(); 184 | 185 | let (_, mem_info) = unsafe { c.read::() }.or(Err(libc::EMSGSIZE))?; 186 | 187 | Ok(mem_info) 188 | } 189 | 190 | /// Exit the shim with a `status` code 191 | /// 192 | /// # Panics 193 | /// 194 | /// Panics, if the shim resumes to run. 195 | #[inline(always)] 196 | pub fn exit_group(&mut self, status: i32) -> ! { 197 | unsafe { 198 | let request = request!(libc::SYS_exit_group => status); 199 | self.block.as_mut().unwrap().msg.req = request; 200 | 201 | let _ = self.hostcall(); 202 | 203 | unreachable!() 204 | } 205 | } 206 | } 207 | 208 | /// Write all `bytes` to a host file descriptor `fd` 209 | #[inline(always)] 210 | pub fn shim_write_all(fd: HostFd, bytes: &[u8]) -> Result<(), libc::c_int> { 211 | let bytes_len = bytes.len(); 212 | let mut to_write = bytes_len; 213 | 214 | let mut host_call = HOST_CALL_ALLOC.try_alloc().ok_or(libc::EIO)?; 215 | 216 | loop { 217 | let written = unsafe { 218 | let next = bytes_len.checked_sub(to_write).ok_or(libc::EFAULT)?; 219 | host_call 220 | .write(fd.as_raw_fd(), &bytes[next..]) 221 | .map(|regs| usize::from(regs[0])) 222 | }?; 223 | // be careful with `written` as it is untrusted 224 | to_write = to_write.checked_sub(written).ok_or(libc::EIO)?; 225 | if to_write == 0 { 226 | break; 227 | } 228 | } 229 | 230 | Ok(()) 231 | } 232 | 233 | /// Exit the shim with a `status` code 234 | /// 235 | /// Reverts to a triple fault, which causes a `#VMEXIT` and a KVM shutdown, 236 | /// if it cannot talk to the host. 237 | pub fn shim_exit(status: i32) -> ! { 238 | if let Some(mut host_call) = HOST_CALL_ALLOC.try_alloc() { 239 | host_call.exit_group(status) 240 | } 241 | 242 | // provoke triple fault, causing a VM shutdown 243 | unsafe { _enarx_asm_triple_fault() } 244 | } 245 | -------------------------------------------------------------------------------- /internal/shim-sev/src/main.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! The SEV shim 4 | //! 5 | //! This crate contains the system/kernel that handles the syscalls (and cpuid instructions) 6 | //! from the enclave code and might proxy them to the host. 7 | 8 | #![no_std] 9 | #![deny(clippy::all)] 10 | #![deny(clippy::integer_arithmetic)] 11 | #![deny(missing_docs)] 12 | #![no_main] 13 | #![feature(asm, naked_functions)] 14 | 15 | extern crate compiler_builtins; 16 | extern crate rcrt1; 17 | 18 | #[macro_use] 19 | pub mod print; 20 | 21 | pub mod addr; 22 | pub mod allocator; 23 | pub mod asm; 24 | pub mod attestation; 25 | pub mod gdt; 26 | pub mod hostcall; 27 | pub mod hostmap; 28 | pub mod no_std; 29 | pub mod pagetables; 30 | pub mod paging; 31 | pub mod payload; 32 | pub mod random; 33 | pub mod shim_stack; 34 | pub mod spin; 35 | mod start; 36 | pub mod syscall; 37 | pub mod usermode; 38 | 39 | use crate::attestation::SevSecret; 40 | use crate::pagetables::switch_sallyport_to_unencrypted; 41 | use crate::paging::SHIM_PAGETABLE; 42 | use crate::payload::PAYLOAD_VIRT_ADDR; 43 | use crate::print::{enable_printing, is_printing_enabled}; 44 | use core::mem::size_of; 45 | use core::sync::atomic::{AtomicBool, AtomicU64, Ordering}; 46 | use goblin::elf::header::header64::Header; 47 | use noted::noted; 48 | use primordial::Page as Page4KiB; 49 | use sallyport::{elf::note, Block, REQUIRES}; 50 | use spinning::RwLock; 51 | use x86_64::structures::paging::Translate; 52 | use x86_64::VirtAddr; 53 | 54 | noted! { 55 | static NOTE_ENARX_SALLYPORT = REQUIRES; 56 | } 57 | 58 | static C_BIT_MASK: AtomicU64 = AtomicU64::new(0); 59 | 60 | static SEV_SECRET: RwLock> = 61 | RwLock::>::const_new(spinning::RawRwLock::const_new(), None); 62 | 63 | static PAYLOAD_READY: AtomicBool = AtomicBool::new(false); 64 | 65 | extern "C" { 66 | /// Extern 67 | pub static _ENARX_SALLYPORT_START: Block; 68 | /// Extern 69 | pub static _ENARX_SALLYPORT_END: Page4KiB; 70 | /// Extern 71 | pub static _ENARX_MEM_START: Page4KiB; 72 | /// Extern 73 | pub static _ENARX_SHIM_START: Page4KiB; 74 | /// Extern 75 | pub static _ENARX_EXEC_START: Header; 76 | /// Extern 77 | pub static _ENARX_EXEC_END: Page4KiB; 78 | } 79 | 80 | /// Get the SEV C-Bit mask 81 | #[inline(always)] 82 | pub fn get_cbit_mask() -> u64 { 83 | C_BIT_MASK.load(Ordering::Relaxed) 84 | } 85 | 86 | /// Switch the stack and jump to a function 87 | /// 88 | /// # Safety 89 | /// 90 | /// This function is unsafe, because the caller has to ensure a 16 byte 91 | /// aligned usable stack. 92 | #[allow(clippy::integer_arithmetic)] 93 | pub unsafe fn switch_shim_stack(ip: extern "C" fn() -> !, sp: u64) -> ! { 94 | assert_eq!(sp % 16, 0); 95 | asm!(" 96 | mov rsp, {SP} 97 | sub rsp, 8 98 | push rbp 99 | call {IP} 100 | ", 101 | SP = in(reg) sp, 102 | IP = in(reg) ip, 103 | options(noreturn, nomem) 104 | ) 105 | } 106 | 107 | /// Defines the entry point function. 108 | /// 109 | /// # Safety 110 | /// Do not call from Rust. 111 | pub unsafe extern "sysv64" fn _start_main(c_bit_mask: u64) -> ! { 112 | C_BIT_MASK.store(c_bit_mask, Ordering::Relaxed); 113 | 114 | // make a local copy of boot_info, before the shared page gets overwritten 115 | SEV_SECRET 116 | .write() 117 | .replace((&_ENARX_SALLYPORT_START as *const _ as *const SevSecret).read()); 118 | 119 | switch_sallyport_to_unencrypted(c_bit_mask); 120 | 121 | // Everything setup, so print works 122 | enable_printing(); 123 | 124 | // Switch the stack to a guarded stack 125 | switch_shim_stack(shim_main, gdt::INITIAL_STACK.pointer.as_u64()) 126 | } 127 | 128 | /// The entry point for the shim 129 | extern "C" fn shim_main() -> ! { 130 | unsafe { gdt::init() }; 131 | payload::execute_payload() 132 | } 133 | 134 | /// The panic function 135 | /// 136 | /// Called, whenever somethings panics. 137 | /// 138 | /// Reverts to a triple fault, which causes a `#VMEXIT` and a KVM shutdown, 139 | /// if it can't print the panic and exit normally with an error code. 140 | #[panic_handler] 141 | pub fn panic(info: &core::panic::PanicInfo) -> ! { 142 | use asm::_enarx_asm_triple_fault; 143 | 144 | static mut ALREADY_IN_PANIC: AtomicBool = AtomicBool::new(false); 145 | 146 | // Don't print anything, if the FRAME_ALLOCATOR is not yet initialized 147 | unsafe { 148 | if is_printing_enabled() 149 | && ALREADY_IN_PANIC 150 | .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) 151 | .is_ok() 152 | { 153 | print::_eprint(format_args!("{}\n", info)); 154 | stack_trace(); 155 | // FIXME: might want to have a custom panic hostcall 156 | hostcall::shim_exit(255); 157 | } 158 | } 159 | 160 | // provoke triple fault, causing a VM shutdown 161 | unsafe { _enarx_asm_triple_fault() } 162 | } 163 | 164 | #[inline(never)] 165 | unsafe fn stack_trace() { 166 | let mut rbp: usize; 167 | 168 | asm!("mov {}, rbp", out(reg) rbp); 169 | 170 | print::_eprint(format_args!("TRACE:\n")); 171 | 172 | if SHIM_PAGETABLE.try_read().is_none() { 173 | SHIM_PAGETABLE.force_unlock_write() 174 | } 175 | 176 | let shim_offset = crate::addr::SHIM_VIRT_OFFSET as usize; 177 | 178 | let active_table = SHIM_PAGETABLE.read(); 179 | 180 | //Maximum 64 frames 181 | for _frame in 0..64 { 182 | if rbp == 0 183 | || active_table 184 | .translate_addr(VirtAddr::new(rbp as _)) 185 | .is_none() 186 | { 187 | break; 188 | } 189 | 190 | if let Some(rip_rbp) = rbp.checked_add(size_of::() as _) { 191 | if active_table 192 | .translate_addr(VirtAddr::new(rip_rbp as _)) 193 | .is_none() 194 | { 195 | break; 196 | } 197 | 198 | let rip = *(rip_rbp as *const usize); 199 | if let Some(rip) = rip.checked_sub(1) { 200 | if rip == 0 { 201 | break; 202 | } 203 | 204 | if let Some(rip) = rip.checked_sub(shim_offset) { 205 | print::_eprint(format_args!(" 0x{:>016x}\n", rip)); 206 | rbp = *(rbp as *const usize); 207 | } else if PAYLOAD_READY.load(Ordering::Relaxed) { 208 | if let Some(rip) = rip.checked_sub(PAYLOAD_VIRT_ADDR.read().as_u64() as _) { 209 | print::_eprint(format_args!("P 0x{:>016x}\n", rip)); 210 | rbp = *(rbp as *const usize); 211 | } else { 212 | break; 213 | } 214 | } 215 | } else { 216 | // RIP zero 217 | break; 218 | } 219 | } else { 220 | // RBP OVERFLOW 221 | break; 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /internal/shim-sev/src/no_std.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Stubs to satisfy the compiler in `no_std` 4 | 5 | /// _Unwind_Resume is only needed in the `debug` profile 6 | /// 7 | /// even though this project has `panic=abort` 8 | /// it seems like the debug libc.rlib has some references 9 | /// with unwinding 10 | /// See also: https://github.com/rust-lang/rust/issues/47493 11 | #[cfg(debug_assertions)] 12 | #[no_mangle] 13 | extern "C" fn _Unwind_Resume() { 14 | unimplemented!(); 15 | } 16 | 17 | /// rust_eh_personality is only needed in the `debug` profile 18 | /// 19 | /// even though this project has `panic=abort` 20 | /// it seems like the debug libc.rlib has some references 21 | /// with unwinding 22 | /// See also: https://github.com/rust-lang/rust/issues/47493 23 | #[cfg(debug_assertions)] 24 | #[no_mangle] 25 | pub extern "C" fn rust_eh_personality() { 26 | unimplemented!(); 27 | } 28 | 29 | #[cfg(not(target_feature = "crt-static"))] 30 | #[allow(missing_docs)] 31 | fn __check_for_static_linking() { 32 | compile_error!("shim is not statically linked"); 33 | } 34 | -------------------------------------------------------------------------------- /internal/shim-sev/src/paging.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Paging 4 | 5 | use crate::addr::SHIM_VIRT_OFFSET; 6 | use crate::get_cbit_mask; 7 | 8 | use spinning::{Lazy, RwLock}; 9 | use x86_64::registers::control::Cr3; 10 | use x86_64::structures::paging::mapper::PageTableFrameMapping; 11 | use x86_64::structures::paging::{MappedPageTable, PageTable, PhysFrame}; 12 | use x86_64::VirtAddr; 13 | 14 | /// A `PageTableFrameMapping` specialized to encrypted physical pages. 15 | #[derive(Debug)] 16 | pub struct EncPhysOffset { 17 | /// The virtual address of physical address `0` (without the C-Bit) 18 | pub offset: VirtAddr, 19 | /// The C-Bit mask indicating encrypted physical pages 20 | pub c_bit_mask: u64, 21 | } 22 | 23 | impl Default for EncPhysOffset { 24 | fn default() -> Self { 25 | EncPhysOffset { 26 | offset: VirtAddr::new(SHIM_VIRT_OFFSET as u64), 27 | c_bit_mask: get_cbit_mask(), 28 | } 29 | } 30 | } 31 | 32 | unsafe impl PageTableFrameMapping for EncPhysOffset { 33 | fn frame_to_pointer(&self, frame: PhysFrame) -> *mut PageTable { 34 | let phys_start_address = frame.start_address().as_u64(); 35 | assert_eq!(phys_start_address & self.c_bit_mask, self.c_bit_mask); 36 | let virt = self.offset + (phys_start_address & !self.c_bit_mask); 37 | virt.as_mut_ptr() 38 | } 39 | } 40 | 41 | /// The global `MappedPageTable` of the shim for encrypted pages. 42 | pub static SHIM_PAGETABLE: Lazy>> = 43 | Lazy::new(|| { 44 | let enc_phys_offset = EncPhysOffset::default(); 45 | 46 | let enc_offset_page_table = unsafe { 47 | let level_4_table_ptr = enc_phys_offset.frame_to_pointer(Cr3::read().0); 48 | 49 | MappedPageTable::new(&mut *level_4_table_ptr, enc_phys_offset) 50 | }; 51 | 52 | RwLock::>::const_new( 53 | spinning::RawRwLock::const_new(), 54 | enc_offset_page_table, 55 | ) 56 | }); 57 | -------------------------------------------------------------------------------- /internal/shim-sev/src/payload.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Functions dealing with the payload 4 | use crate::addr::{ShimPhysAddr, ShimVirtAddr}; 5 | use crate::allocator::ALLOCATOR; 6 | use crate::paging::SHIM_PAGETABLE; 7 | use crate::random::random; 8 | use crate::shim_stack::init_stack_with_guard; 9 | use crate::usermode::usermode; 10 | use crate::{get_cbit_mask, PAYLOAD_READY}; 11 | 12 | use core::convert::TryFrom; 13 | use core::ops::DerefMut; 14 | use core::sync::atomic::Ordering; 15 | use crt0stack::{self, Builder, Entry}; 16 | use goblin::elf::header::header64::Header; 17 | use goblin::elf::header::ELFMAG; 18 | use goblin::elf::program_header::program_header64::*; 19 | use nbytes::bytes; 20 | use primordial::Address; 21 | use spinning::{Lazy, RwLock}; 22 | use x86_64::structures::paging::{Page, PageTableFlags, Size4KiB}; 23 | use x86_64::{PhysAddr, VirtAddr}; 24 | 25 | /// Payload virtual address, where the elf binary is mapped to, plus a random offset 26 | const PAYLOAD_ELF_VIRT_ADDR_BASE: VirtAddr = VirtAddr::new_truncate(0x7f00_0000_0000); 27 | 28 | /// The first brk virtual address the payload gets, plus a random offset 29 | const PAYLOAD_BRK_VIRT_ADDR_BASE: VirtAddr = VirtAddr::new_truncate(0x5555_0000_0000); 30 | 31 | /// Payload stack virtual address 32 | const PAYLOAD_STACK_VIRT_ADDR_BASE: VirtAddr = VirtAddr::new_truncate(0x7ff0_0000_0000); 33 | 34 | /// Initial payload stack size 35 | #[allow(clippy::integer_arithmetic)] 36 | const PAYLOAD_STACK_SIZE: u64 = bytes![8; MiB]; 37 | 38 | /// The randomized virtual address of the payload 39 | pub static PAYLOAD_VIRT_ADDR: Lazy> = Lazy::new(|| { 40 | RwLock::::const_new( 41 | spinning::RawRwLock::const_new(), 42 | PAYLOAD_ELF_VIRT_ADDR_BASE + (random() & 0x7F_FFFF_F000), 43 | ) 44 | }); 45 | 46 | /// Actual brk virtual address the payload gets, when calling brk 47 | pub static NEXT_BRK_RWLOCK: Lazy> = Lazy::new(|| { 48 | RwLock::::const_new( 49 | spinning::RawRwLock::const_new(), 50 | PAYLOAD_BRK_VIRT_ADDR_BASE + (random() & 0xFFFF_F000), 51 | ) 52 | }); 53 | 54 | /// The global NextMMap RwLock 55 | pub static NEXT_MMAP_RWLOCK: Lazy> = Lazy::new(|| { 56 | RwLock::::const_new(spinning::RawRwLock::const_new(), *PAYLOAD_VIRT_ADDR.read()) 57 | }); 58 | 59 | /// load the elf binary 60 | fn map_elf(app_virt_start: VirtAddr) -> &'static Header { 61 | let code_start = unsafe { &crate::_ENARX_EXEC_START }; 62 | 63 | let app_load_addr = Address::::from(code_start); 64 | let app_load_addr_virt = ShimVirtAddr::try_from(app_load_addr).unwrap(); 65 | 66 | let header_ptr: *const Header = app_load_addr_virt.into(); 67 | let header: &Header = unsafe { &*header_ptr }; 68 | 69 | if !header.e_ident[..ELFMAG.len()].eq(ELFMAG) { 70 | panic!("Not valid ELF"); 71 | } 72 | 73 | let headers: &[ProgramHeader] = unsafe { 74 | #[allow(clippy::cast_ptr_alignment)] 75 | core::slice::from_raw_parts( 76 | (header_ptr as usize as *const u8).offset(header.e_phoff as _) as *const ProgramHeader, 77 | header.e_phnum as _, 78 | ) 79 | }; 80 | 81 | // Convert to shim physical addresses with potential SEV C-Bit set 82 | let code_start_addr_virt = ShimVirtAddr::try_from(app_load_addr).unwrap(); 83 | 84 | let code_start_phys = ShimPhysAddr::try_from(code_start_addr_virt) 85 | .unwrap() 86 | .raw() 87 | .raw() 88 | | get_cbit_mask(); 89 | 90 | for ph in headers 91 | .iter() 92 | .filter(|ph| ph.p_type == PT_LOAD && ph.p_memsz > 0) 93 | { 94 | let map_from = PhysAddr::new(code_start_phys.checked_add(ph.p_paddr).unwrap()); 95 | 96 | let map_to = app_virt_start + ph.p_vaddr; 97 | 98 | let mut page_table_flags = PageTableFlags::PRESENT | PageTableFlags::USER_ACCESSIBLE; 99 | if (ph.p_flags & PF_X) == 0 { 100 | page_table_flags |= PageTableFlags::NO_EXECUTE 101 | }; 102 | if (ph.p_flags & PF_W) != 0 { 103 | page_table_flags |= PageTableFlags::WRITABLE 104 | }; 105 | 106 | debug_assert_eq!(ph.p_align, Page::::SIZE); 107 | 108 | ALLOCATOR 109 | .write() 110 | .map_memory( 111 | SHIM_PAGETABLE.write().deref_mut(), 112 | map_from, 113 | map_to, 114 | ph.p_memsz as _, 115 | page_table_flags, 116 | PageTableFlags::PRESENT 117 | | PageTableFlags::USER_ACCESSIBLE 118 | | PageTableFlags::WRITABLE, 119 | ) 120 | .expect("Map payload elf failed!"); 121 | } 122 | 123 | header 124 | } 125 | 126 | fn crt0setup( 127 | app_virt_start: VirtAddr, 128 | stack_slice: &'static mut [u8], 129 | header: &Header, 130 | ) -> (VirtAddr, u64) { 131 | let mut builder = Builder::new(stack_slice); 132 | builder.push("/init").unwrap(); 133 | let mut builder = builder.done().unwrap(); 134 | builder.push("LANG=C").unwrap(); 135 | let mut builder = builder.done().unwrap(); 136 | 137 | let ph_header = app_virt_start + header.e_phoff; 138 | let ph_entry = app_virt_start + header.e_entry; 139 | 140 | let hwcap = unsafe { core::arch::x86_64::__cpuid(1) }.edx; 141 | let rand = unsafe { core::mem::transmute([random(), random()]) }; 142 | 143 | for aux in &[ 144 | //Entry::SysInfoEHdr(0x7FD735C0E000), 145 | Entry::ExecFilename("/init"), 146 | Entry::Platform("x86_64"), 147 | Entry::Uid(1000), 148 | Entry::EUid(1000), 149 | Entry::Gid(1000), 150 | Entry::EGid(1000), 151 | Entry::PageSize(4096), 152 | Entry::Secure(false), 153 | Entry::ClockTick(100), 154 | Entry::Flags(0), 155 | //Entry::Base(0), 156 | Entry::PHdr(ph_header.as_u64() as _), 157 | Entry::PHent(header.e_phentsize as _), 158 | Entry::PHnum(header.e_phnum as _), 159 | Entry::HwCap(hwcap as _), 160 | Entry::HwCap2(0), 161 | Entry::Random(rand), 162 | Entry::Entry(ph_entry.as_u64() as _), 163 | ] { 164 | builder.push(aux).unwrap(); 165 | } 166 | let handle = builder.done().unwrap(); 167 | let sp = &*handle as *const _ as u64; 168 | 169 | (ph_entry, sp) 170 | } 171 | 172 | /// execute the payload 173 | pub fn execute_payload() -> ! { 174 | let header = map_elf(*PAYLOAD_VIRT_ADDR.read()); 175 | 176 | let stack = init_stack_with_guard( 177 | PAYLOAD_STACK_VIRT_ADDR_BASE + (random() & 0xFFFF_F000), 178 | PAYLOAD_STACK_SIZE, 179 | PageTableFlags::USER_ACCESSIBLE, 180 | ); 181 | 182 | let (entry, sp_handle) = crt0setup(*PAYLOAD_VIRT_ADDR.read(), stack.slice, header); 183 | 184 | unsafe { 185 | PAYLOAD_READY.store(true, Ordering::Relaxed); 186 | usermode(entry.as_u64(), sp_handle) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /internal/shim-sev/src/print.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Functions and macros to output text on the host 4 | 5 | use crate::hostcall::{self, HostFd}; 6 | 7 | struct HostWrite(HostFd); 8 | 9 | use core::fmt; 10 | use core::sync::atomic::{AtomicUsize, Ordering}; 11 | 12 | // FIXME: remove, if https://github.com/enarx/enarx/issues/831 is fleshed out 13 | /// Global flag allowing debug output. 14 | pub const TRACE: bool = false; 15 | 16 | /// start with printing disabled 17 | static mut PRINT_INHIBITOR: AtomicUsize = AtomicUsize::new(1); 18 | 19 | /// Unconditionally enable printing 20 | /// 21 | /// See also [`PrintBarrier`] 22 | #[inline] 23 | pub fn enable_printing() { 24 | unsafe { PRINT_INHIBITOR.store(0, Ordering::Release) } 25 | } 26 | 27 | /// Returns true, if shim can (debug) print 28 | /// 29 | /// See also [`PrintBarrier`] 30 | #[inline] 31 | pub fn is_printing_enabled() -> bool { 32 | unsafe { PRINT_INHIBITOR.load(Ordering::Acquire) == 0 } 33 | } 34 | 35 | /// Temporarily disable (debug) printing 36 | /// 37 | /// Creating a `PrintBarrier` will prevent printing, until the object is dropped. 38 | /// This helps to avoid dead locks with debug printing, which has to be temporarily 39 | /// disabled to avoid dead locks with other Mutexes and RwLocks. 40 | pub struct PrintBarrier; 41 | 42 | impl Default for PrintBarrier { 43 | fn default() -> Self { 44 | unsafe { 45 | PRINT_INHIBITOR.fetch_add(1, Ordering::Release); 46 | } 47 | Self 48 | } 49 | } 50 | 51 | impl Drop for PrintBarrier { 52 | fn drop(&mut self) { 53 | unsafe { 54 | PRINT_INHIBITOR.fetch_sub(1, Ordering::Release); 55 | } 56 | } 57 | } 58 | 59 | impl fmt::Write for HostWrite { 60 | #[inline(always)] 61 | fn write_str(&mut self, s: &str) -> fmt::Result { 62 | hostcall::shim_write_all(self.0, s.as_bytes()).map_err(|_| fmt::Error)?; 63 | Ok(()) 64 | } 65 | } 66 | 67 | #[doc(hidden)] 68 | #[inline(always)] 69 | pub fn _print(args: fmt::Arguments) { 70 | use fmt::Write; 71 | 72 | if !is_printing_enabled() { 73 | // Early return to prevent dead locks. 74 | return; 75 | } 76 | 77 | HostWrite(unsafe { HostFd::from_raw_fd(libc::STDOUT_FILENO) }) 78 | .write_fmt(args) 79 | .expect("Printing via Host fd 1 failed"); 80 | } 81 | 82 | #[doc(hidden)] 83 | #[inline(always)] 84 | pub fn _eprint(args: fmt::Arguments) { 85 | use fmt::Write; 86 | 87 | if !is_printing_enabled() { 88 | // Early return to prevent dead locks. 89 | return; 90 | } 91 | 92 | HostWrite(unsafe { HostFd::from_raw_fd(libc::STDERR_FILENO) }) 93 | .write_fmt(args) 94 | .expect("Printing via Host fd 2 failed"); 95 | } 96 | 97 | /// Prints to the standard output of the host. 98 | /// 99 | /// Equivalent to the [`println!`] macro except that a newline is not printed at 100 | /// the end of the message. 101 | /// 102 | /// [`println!`]: macro.println.html 103 | /// 104 | /// # Panics 105 | /// 106 | /// Panics if writing to the host fails. 107 | #[macro_export] 108 | macro_rules! print { 109 | ($($arg:tt)*) => { 110 | if $crate::print::TRACE { $crate::print::_print(format_args!($($arg)*)); } 111 | }; 112 | } 113 | 114 | /// Prints to the standard output of the host, with a newline. 115 | /// 116 | /// Use the `format!` syntax to write data to the standard output. 117 | /// See `core::fmt` for more information. 118 | /// 119 | /// Use `println!` only for the primary output of your program. Use 120 | /// [`eprintln!`] instead to print error and progress messages. 121 | /// 122 | /// [`eprintln!`]: macro.eprintln.html 123 | /// 124 | /// # Panics 125 | /// 126 | /// Panics if writing to the host fails. 127 | #[macro_export] 128 | macro_rules! println { 129 | () => ($crate::print!("\n")); 130 | ($fmt:expr) => ($crate::print!(concat!($fmt, "\n"))); 131 | ($fmt:expr, $($arg:tt)*) => ($crate::print!(concat!($fmt, "\n"), $($arg)*)); 132 | } 133 | 134 | /// Prints to the standard error. 135 | /// 136 | /// Equivalent to the [`print!`] macro, except that output goes to 137 | /// `stderr` of the host instead of `stdout`. 138 | /// 139 | /// Use `eprint!` only for error and progress messages. Use [`print!`] 140 | /// instead for the primary output of your program. 141 | /// 142 | /// [`print!`]: macro.print.html 143 | /// 144 | /// # Panics 145 | /// 146 | /// Panics if writing to the host fails. 147 | #[macro_export] 148 | macro_rules! eprint { 149 | ($($arg:tt)*) => { 150 | if $crate::print::TRACE { $crate::print::_eprint(format_args!($($arg)*)) }; 151 | }; 152 | } 153 | 154 | /// Prints to the standard error of the host, with a newline. 155 | /// 156 | /// Equivalent to the [`println!`] macro, except that output goes to 157 | /// `stderr` of the host instead of `stdout`. See [`println!`] for 158 | /// example usage. 159 | /// 160 | /// Use `eprintln!` only for error and progress messages. Use [`println!`] 161 | /// instead for the primary output of your program. 162 | /// 163 | /// [`println!`]: macro.println.html 164 | /// 165 | /// # Panics 166 | /// 167 | /// Panics if writing to the host fails. 168 | #[macro_export] 169 | macro_rules! eprintln { 170 | () => ($crate::eprint!("\n")); 171 | ($fmt:expr) => ($crate::eprint!(concat!($fmt, "\n"))); 172 | ($fmt:expr, $($arg:tt)*) => ($crate::eprint!(concat!($fmt, "\n"), $($arg)*)); 173 | } 174 | 175 | /// Prints and returns the value of a given expression for quick and dirty 176 | /// debugging. 177 | #[macro_export] 178 | macro_rules! dbg { 179 | () => { 180 | $crate::eprintln!("[{}:{}]", file!(), line!()); 181 | }; 182 | ($val:expr $(,)?) => { 183 | // Use of `match` here is intentional because it affects the lifetimes 184 | // of temporaries - https://stackoverflow.com/a/48732525/1063961 185 | match $val { 186 | tmp => { 187 | $crate::eprintln!("[{}:{}] {} = {:#?}", 188 | file!(), line!(), stringify!($val), &tmp); 189 | tmp 190 | } 191 | } 192 | }; 193 | ($($val:expr),+ $(,)?) => { 194 | ($($crate::dbg!($val)),+,) 195 | }; 196 | } 197 | -------------------------------------------------------------------------------- /internal/shim-sev/src/random.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Random functions 4 | 5 | /// Get a random number 6 | pub fn random() -> u64 { 7 | let mut r: u64 = 0; 8 | 9 | for _ in 0..1024 { 10 | if unsafe { core::arch::x86_64::_rdrand64_step(&mut r) } == 1 { 11 | return r; 12 | } 13 | } 14 | 15 | panic!("Could not get random!") 16 | } 17 | -------------------------------------------------------------------------------- /internal/shim-sev/src/shim_stack.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Helper functions for the shim stack 4 | 5 | use crate::allocator::ALLOCATOR; 6 | use crate::paging::SHIM_PAGETABLE; 7 | use core::ops::DerefMut; 8 | use x86_64::structures::paging::{Page, PageTableFlags, Size4KiB}; 9 | use x86_64::VirtAddr; 10 | 11 | /// A guarded stack 12 | pub struct GuardedStack { 13 | /// the stack pointer 14 | pub pointer: VirtAddr, 15 | /// the usable stack memory slice 16 | pub slice: &'static mut [u8], 17 | } 18 | 19 | /// Allocate a stack with guard pages 20 | pub fn init_stack_with_guard( 21 | start: VirtAddr, 22 | stack_size: u64, 23 | extra_flags: PageTableFlags, 24 | ) -> GuardedStack { 25 | // guard page 26 | ALLOCATOR 27 | .write() 28 | .allocate_and_map_memory( 29 | SHIM_PAGETABLE.write().deref_mut(), 30 | start - Page::::SIZE, 31 | Page::::SIZE as _, 32 | PageTableFlags::empty(), 33 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE, 34 | ) 35 | .expect("Stack guard page allocation failed"); 36 | 37 | let mem_slice = ALLOCATOR 38 | .write() 39 | .allocate_and_map_memory( 40 | SHIM_PAGETABLE.write().deref_mut(), 41 | start, 42 | stack_size as _, 43 | PageTableFlags::PRESENT 44 | | PageTableFlags::WRITABLE 45 | | PageTableFlags::NO_EXECUTE 46 | | extra_flags, 47 | PageTableFlags::PRESENT 48 | | PageTableFlags::WRITABLE 49 | | PageTableFlags::NO_EXECUTE 50 | | extra_flags, 51 | ) 52 | .expect("Stack allocation failed"); 53 | 54 | // guard page 55 | ALLOCATOR 56 | .write() 57 | .allocate_and_map_memory( 58 | SHIM_PAGETABLE.write().deref_mut(), 59 | start + stack_size, 60 | Page::::SIZE as _, 61 | PageTableFlags::empty(), 62 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE, 63 | ) 64 | .expect("Stack guard page allocation failed"); 65 | 66 | // Point to the end of the stack 67 | let stack_ptr = unsafe { mem_slice.as_ptr().add(mem_slice.len()) }; 68 | 69 | // We know it's aligned to 16, so no need to manually align 70 | debug_assert_eq!((stack_ptr as u64).checked_rem(16), Some(0)); 71 | 72 | GuardedStack { 73 | pointer: VirtAddr::from_ptr(stack_ptr), 74 | slice: mem_slice, 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /internal/shim-sev/src/spin.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! wrapper around spinning types to permit trait implementations. 4 | 5 | use spinning::{Mutex, MutexGuard, RawMutex, RawRwLock, RwLock, RwLockReadGuard, RwLockWriteGuard}; 6 | 7 | /// A wrapper around spinning::Mutex to permit trait implementations. 8 | pub struct Locked { 9 | inner: Mutex, 10 | } 11 | 12 | impl Locked { 13 | /// Constructor 14 | #[inline] 15 | pub const fn new(inner: A) -> Self { 16 | Self { 17 | inner: Mutex::const_new(RawMutex::const_new(), inner), 18 | } 19 | } 20 | 21 | /// get a [`MutexGuard`](spinning::MutexGuard) 22 | #[inline] 23 | pub fn lock(&self) -> MutexGuard { 24 | self.inner.lock() 25 | } 26 | } 27 | 28 | /// A wrapper around spinning::RwLock to permit trait implementations. 29 | pub struct RwLocked { 30 | inner: RwLock, 31 | } 32 | 33 | impl RwLocked { 34 | /// Constructor 35 | #[inline] 36 | pub const fn new(inner: A) -> Self { 37 | Self { 38 | inner: RwLock::const_new(RawRwLock::const_new(), inner), 39 | } 40 | } 41 | 42 | /// get a [`RwLockReadGuard`](spinning::RwLockReadGuard) 43 | #[inline] 44 | pub fn read(&self) -> RwLockReadGuard { 45 | self.inner.read() 46 | } 47 | 48 | /// get a [`RwLockWriteGuard`](spinning::RwLockWriteGuard) 49 | #[inline] 50 | pub fn write(&self) -> RwLockWriteGuard { 51 | self.inner.write() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /internal/shim-sev/src/usermode.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! switch to Ring 3 aka usermode 4 | 5 | use crate::gdt::{USER_CODE_SEGMENT, USER_DATA_SEGMENT}; 6 | use x86_64::registers::rflags::RFlags; 7 | 8 | /// Enter Ring 3 9 | /// 10 | /// # Safety 11 | /// 12 | /// Because the caller can give any `entry_point` and `stack_pointer` 13 | /// including 0, this function is unsafe. 14 | pub unsafe fn usermode(ip: u64, sp: u64) -> ! { 15 | asm!(" 16 | push {USER_DATA_SEGMENT} 17 | push {SP} 18 | push {RFLAGS} 19 | push {USER_CODE_SEGMENT} 20 | push {IP} 21 | xor rax, rax 22 | xor rbx, rbx 23 | xor rcx, rcx 24 | xor rdx, rdx 25 | xor rsi, rsi 26 | xor rdi, rdi 27 | xor rbp, rbp 28 | xor r8, r8 29 | xor r9, r9 30 | xor r10, r10 31 | xor r11, r11 32 | xor r12, r12 33 | xor r13, r13 34 | xor r14, r14 35 | xor r15, r15 36 | 37 | # clear all segment selectors 38 | mov ds, r11 39 | mov es, r11 40 | mov fs, r11 41 | mov gs, r11 42 | 43 | # clear the FPU 44 | fninit 45 | 46 | # do a simulated return from interrupt 47 | # this sets the segments and rip from the stack 48 | iretq 49 | ", 50 | USER_DATA_SEGMENT = const USER_DATA_SEGMENT, 51 | SP = in(reg) sp, 52 | RFLAGS = const RFlags::INTERRUPT_FLAG.bits(), 53 | USER_CODE_SEGMENT = const USER_CODE_SEGMENT, 54 | IP = in(reg) ip, 55 | options(noreturn, nomem) 56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /internal/shim-sgx/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-linux-musl" 3 | rustflags = [ 4 | "-C", "relocation-model=pic", 5 | "-C", "link-args=-Wl,--sort-section=alignment,-Tlayout.ld -nostartfiles", 6 | "-C", "link-self-contained=no", 7 | ] 8 | -------------------------------------------------------------------------------- /internal/shim-sgx/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bit_field" 7 | version = "0.10.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.3.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 16 | 17 | [[package]] 18 | name = "compiler_builtins" 19 | version = "0.1.50" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "c4fd27448c11cdc03f9be9babc79e2aba19789a1db6fe0a1390d53f99f3f8fb1" 22 | 23 | [[package]] 24 | name = "const-default" 25 | version = "0.2.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "3a9e8347466efbc524900ef2cddbbfc70c32f66d0e93ba62df93e4eef3055ee5" 28 | dependencies = [ 29 | "const-default-derive", 30 | ] 31 | 32 | [[package]] 33 | name = "const-default-derive" 34 | version = "0.1.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "67a575ae520bba956e5f9d1d7bdfac9ca7530d8a4dfa34ad1976244096a8fa04" 37 | dependencies = [ 38 | "proc-macro2", 39 | "quote", 40 | "syn", 41 | ] 42 | 43 | [[package]] 44 | name = "crt0stack" 45 | version = "0.1.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "9274b445ee572d50bdeb17a1101be829becc565b5c12b21a697af4d360b48e8d" 48 | 49 | [[package]] 50 | name = "enarx-heap" 51 | version = "0.1.0" 52 | source = "git+https://github.com/enarx/enarx-heap?rev=9cbfb3367edd4aa17f4a7409ea0c0f7d83fa8ce3#9cbfb3367edd4aa17f4a7409ea0c0f7d83fa8ce3" 53 | dependencies = [ 54 | "libc", 55 | "lset", 56 | "primordial 0.1.0", 57 | ] 58 | 59 | [[package]] 60 | name = "flagset" 61 | version = "0.4.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "1207393e01e20804589a3fc9781c9df2a70687cd81362ca58e33b2a726ec83cf" 64 | 65 | [[package]] 66 | name = "goblin" 67 | version = "0.4.3" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "32401e89c6446dcd28185931a01b1093726d0356820ac744023e6850689bf926" 70 | dependencies = [ 71 | "plain", 72 | "scroll", 73 | ] 74 | 75 | [[package]] 76 | name = "libc" 77 | version = "0.2.103" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" 80 | 81 | [[package]] 82 | name = "lset" 83 | version = "0.2.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "efeae5282702b072b5e21cf8f430ccd3c5031c1e346321a28429523266c4a9b0" 86 | 87 | [[package]] 88 | name = "nbytes" 89 | version = "0.1.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "c619aa76dbb3f67970c7cf10fc3efa81da412be26d4dda1726af76b25260dc66" 92 | 93 | [[package]] 94 | name = "noted" 95 | version = "1.0.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "c9ed3de88cf5f12b1b461eb59a5b85f60d84216207bda41bf0f0eb2d7d51d397" 98 | 99 | [[package]] 100 | name = "plain" 101 | version = "0.2.3" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 104 | 105 | [[package]] 106 | name = "primordial" 107 | version = "0.1.0" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "979d94833957a6485c5cac4b71d552a9cd0b9f3f6fd1c7c5dc8096b3ee2bcd13" 110 | 111 | [[package]] 112 | name = "primordial" 113 | version = "0.3.0" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "55d6312462222758b3fb6c7e84d819ce87c315c446e0e2c11b0b9258dedd3f25" 116 | 117 | [[package]] 118 | name = "proc-macro2" 119 | version = "1.0.29" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" 122 | dependencies = [ 123 | "unicode-xid", 124 | ] 125 | 126 | [[package]] 127 | name = "quote" 128 | version = "1.0.9" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 131 | dependencies = [ 132 | "proc-macro2", 133 | ] 134 | 135 | [[package]] 136 | name = "rcrt1" 137 | version = "0.1.0" 138 | source = "git+https://github.com/enarx/rcrt1?rev=b28f711#b28f711b4de0236053021878d83f11b5e48c4035" 139 | dependencies = [ 140 | "goblin", 141 | "libc", 142 | ] 143 | 144 | [[package]] 145 | name = "sallyport" 146 | version = "0.1.0" 147 | source = "git+https://github.com/enarx/sallyport?rev=a567a22665c7e5ba88a8c4acd64ab43ee32b4681#a567a22665c7e5ba88a8c4acd64ab43ee32b4681" 148 | dependencies = [ 149 | "goblin", 150 | "libc", 151 | "primordial 0.3.0", 152 | ] 153 | 154 | [[package]] 155 | name = "scroll" 156 | version = "0.10.2" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" 159 | 160 | [[package]] 161 | name = "sgx" 162 | version = "0.1.0" 163 | source = "git+https://github.com/enarx/sgx?rev=57df3753a0ea1777963dbf3023452993df2edb8c#57df3753a0ea1777963dbf3023452993df2edb8c" 164 | dependencies = [ 165 | "bitflags", 166 | "x86_64", 167 | "xsave", 168 | ] 169 | 170 | [[package]] 171 | name = "shim-sgx" 172 | version = "0.1.0" 173 | dependencies = [ 174 | "compiler_builtins", 175 | "const-default", 176 | "crt0stack", 177 | "enarx-heap", 178 | "flagset", 179 | "goblin", 180 | "libc", 181 | "lset", 182 | "nbytes", 183 | "noted", 184 | "primordial 0.3.0", 185 | "rcrt1", 186 | "sallyport", 187 | "sgx", 188 | "x86_64", 189 | "xsave", 190 | ] 191 | 192 | [[package]] 193 | name = "syn" 194 | version = "1.0.76" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" 197 | dependencies = [ 198 | "proc-macro2", 199 | "quote", 200 | "unicode-xid", 201 | ] 202 | 203 | [[package]] 204 | name = "unicode-xid" 205 | version = "0.2.2" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 208 | 209 | [[package]] 210 | name = "volatile" 211 | version = "0.4.4" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "e4c2dbd44eb8b53973357e6e207e370f0c1059990df850aca1eca8947cf464f0" 214 | 215 | [[package]] 216 | name = "x86_64" 217 | version = "0.14.6" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "bbc6ed1ed2cd4536b083c34041aff7b84448ee25ac4aa5e9d54802ce226f9815" 220 | dependencies = [ 221 | "bit_field", 222 | "bitflags", 223 | "volatile", 224 | ] 225 | 226 | [[package]] 227 | name = "xsave" 228 | version = "1.0.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "0f980b1e0ed898c5b60ac780eae2786ebb8b7a3ea8d9eea9592718159fc26ad8" 231 | dependencies = [ 232 | "bitflags", 233 | "const-default", 234 | ] 235 | -------------------------------------------------------------------------------- /internal/shim-sgx/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "shim-sgx" 3 | version = "0.1.0" 4 | authors = ["Nathaniel McCallum "] 5 | edition = "2018" 6 | license = "Apache-2.0" 7 | 8 | [[bin]] 9 | name = "shim-sgx" 10 | test = false 11 | 12 | [dependencies] 13 | sallyport = { git = "https://github.com/enarx/sallyport", rev = "a567a22665c7e5ba88a8c4acd64ab43ee32b4681", features = [ "asm" ] } 14 | enarx-heap = { git = "https://github.com/enarx/enarx-heap", rev = "9cbfb3367edd4aa17f4a7409ea0c0f7d83fa8ce3" } 15 | sgx = { git = "https://github.com/enarx/sgx", rev = "57df3753a0ea1777963dbf3023452993df2edb8c" } 16 | rcrt1 = { git = "https://github.com/enarx/rcrt1", rev = "b28f711" } 17 | compiler_builtins = { version = "0.1", default-features = false, features = [ "mem" ] } 18 | goblin = { version = "0.4", default-features = false, features = [ "elf64" ] } 19 | x86_64 = { version = "^0.14.6", default-features = false } 20 | crt0stack = { version = "0.1", default-features = false } 21 | libc = { version = "0.2", default-features = false } 22 | const-default = "0.2" 23 | primordial = "0.3.0" 24 | noted = "^1.0.0" 25 | xsave = "^1.0.0" 26 | flagset = "0.4" 27 | nbytes = "0.1" 28 | lset = "0.2" 29 | 30 | [profile.dev.package.rcrt1] 31 | opt-level = 3 32 | 33 | [profile.dev] 34 | panic = "abort" 35 | 36 | [profile.release] 37 | panic = "abort" 38 | codegen-units = 1 39 | incremental = false 40 | lto = true 41 | opt-level = "s" 42 | -------------------------------------------------------------------------------- /internal/shim-sgx/build.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | fn main() { 4 | println!("cargo:rerun-if-changed=layout.ld"); 5 | } 6 | -------------------------------------------------------------------------------- /internal/shim-sgx/layout.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | PHDRS { 4 | rodata PT_LOAD FILEHDR PHDRS; 5 | data PT_LOAD; 6 | text PT_LOAD; 7 | dynamic PT_DYNAMIC; 8 | note PT_NOTE; 9 | 10 | stk0 PT_LOAD; 11 | tcs0 PT_LOAD FLAGS(1 << 20); /* sallyport::elf::pf::sgx::TCS */ 12 | ssa0 PT_LOAD; 13 | 14 | exec 0x63400000 FLAGS(0); /* sallyport::elf::pt::EXEC */ 15 | heap PT_LOAD FLAGS(7); 16 | } 17 | 18 | SECTIONS { 19 | . = SIZEOF_HEADERS; 20 | 21 | . = ALIGN(4K); 22 | .rodata : { *(.rodata .rodata.*) } :rodata 23 | .dynsym : { *(.dynsym) } :rodata 24 | .dynstr : { *(.dynstr) } :rodata 25 | .gnu.hash : { *(.gnu.hash) } :rodata 26 | .note : { *(.note) } :rodata :note 27 | 28 | . = ALIGN(4K); 29 | .data.rel.ro : { *(.data.rel.ro .data.rel.ro.*) } :data 30 | .dynamic : { *(.dynamic) } :data :dynamic 31 | .data : { *(.data .data.*) } :data 32 | .rela.dyn : { *(.rela.*) } :data 33 | .got : { *(.got) } :data 34 | .bss : { *(.bss .bss.*) } :data 35 | 36 | . = ALIGN(4K); 37 | .text : { *(.text .text.*) } :text 38 | 39 | /DISCARD/ : { 40 | *(.eh_frame*) 41 | *(.note.GNU-stack) 42 | *(.gnu_debuglink) 43 | *(.interp) 44 | *(.gnu.hash) 45 | *(.hash) 46 | *(.comment) 47 | *(COMMON) 48 | *(.note.gnu.build-id) 49 | } 50 | 51 | /* THREAD */ 52 | . = ALIGN(2M); 53 | . += 4K; /* Guard Page */ 54 | .enarx.stk0 (NOLOAD) : { . += 2M - 4K * 5; } :stk0 =0 55 | .enarx.tcs0 : { 56 | . += 16; 57 | QUAD(. + 4K - 16) /* OSSA */ 58 | LONG(0) /* CSSA */ 59 | LONG(3) /* NSSA */ 60 | QUAD(_start) /* OENTRY */ 61 | . = ALIGN(4K); 62 | } :tcs0 =0 63 | .enarx.ssa0 (NOLOAD) : { . += 4K * 3; } :ssa0 =0 64 | 65 | /* EXEC */ 66 | . = ALIGN(1M); 67 | HIDDEN(ENARX_EXEC_START = .); 68 | .enarx.exec (NOLOAD) : { . = ALIGN(128M); } :exec =0 69 | HIDDEN(ENARX_EXEC_END = .); 70 | 71 | /* HEAP */ 72 | . = ALIGN(128M); 73 | HIDDEN(ENARX_HEAP_START = .); 74 | .enarx.heap (NOLOAD) : { . += 128M; } :heap =0 75 | HIDDEN(ENARX_HEAP_END = .); 76 | } 77 | -------------------------------------------------------------------------------- /internal/shim-sgx/src/entry.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use crt0stack::{Builder, Entry, Handle, OutOfSpace}; 4 | use goblin::elf::header::{header64::Header, ELFMAG}; 5 | 6 | fn exit(code: usize) -> ! { 7 | loop { 8 | unsafe { 9 | asm!( 10 | "syscall", 11 | in("rax") libc::SYS_exit, 12 | in("rdi") code 13 | ); 14 | } 15 | } 16 | } 17 | 18 | fn random() -> u64 { 19 | let mut r: u64 = 0; 20 | 21 | for _ in 0..1024 { 22 | if unsafe { core::arch::x86_64::_rdrand64_step(&mut r) } == 1 { 23 | return r; 24 | } 25 | } 26 | 27 | exit(1) 28 | } 29 | 30 | fn crt0setup<'a>( 31 | hdr: &Header, 32 | crt0: &'a mut [u8], 33 | off: *const (), 34 | ) -> Result, OutOfSpace> { 35 | let rand = unsafe { core::mem::transmute([random(), random()]) }; 36 | let phdr = off as u64 + hdr.e_phoff; 37 | 38 | // Set the arguments 39 | let mut builder = Builder::new(crt0); 40 | builder.push("/init")?; 41 | 42 | // Set the environment 43 | let mut builder = builder.done()?; 44 | builder.push("LANG=C")?; 45 | 46 | // Set the aux vector 47 | let mut builder = builder.done()?; 48 | builder.push(&Entry::ExecFilename("/init"))?; 49 | builder.push(&Entry::Platform("x86_64"))?; 50 | builder.push(&Entry::Uid(1000))?; 51 | builder.push(&Entry::EUid(1000))?; 52 | builder.push(&Entry::Gid(1000))?; 53 | builder.push(&Entry::EGid(1000))?; 54 | builder.push(&Entry::PageSize(4096))?; 55 | builder.push(&Entry::Secure(false))?; 56 | builder.push(&Entry::ClockTick(100))?; 57 | builder.push(&Entry::Flags(0))?; // TODO: https://github.com/enarx/enarx/issues/386 58 | builder.push(&Entry::HwCap(0))?; // TODO: https://github.com/enarx/enarx/issues/386 59 | builder.push(&Entry::HwCap2(0))?; // TODO: https://github.com/enarx/enarx/issues/386 60 | builder.push(&Entry::PHdr(phdr as _))?; 61 | builder.push(&Entry::PHent(hdr.e_phentsize as _))?; 62 | builder.push(&Entry::PHnum(hdr.e_phnum as _))?; 63 | builder.push(&Entry::Random(rand))?; 64 | 65 | builder.done() 66 | } 67 | 68 | pub unsafe fn entry(offset: *const ()) -> ! { 69 | // Validate the ELF header. 70 | let hdr = &*(offset as *const Header); 71 | if !hdr.e_ident[..ELFMAG.len()].eq(ELFMAG) { 72 | exit(1); 73 | } 74 | 75 | // Prepare the crt0 stack. 76 | let mut crt0 = [0u8; 1024]; 77 | let space = random() as usize & 0xf0; 78 | let handle = match crt0setup(hdr, &mut crt0[space..], offset) { 79 | Err(OutOfSpace) => exit(1), 80 | Ok(handle) => handle, 81 | }; 82 | 83 | asm!( 84 | "mov rsp, {SP}", 85 | "jmp {START}", 86 | SP = in(reg) &*handle, 87 | START = in(reg) offset as u64 + hdr.e_entry, 88 | options(noreturn) 89 | ) 90 | } 91 | -------------------------------------------------------------------------------- /internal/shim-sgx/src/handler/base.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use primordial::Register; 4 | use sallyport::syscall::{BaseSyscallHandler, ProcessSyscallHandler}; 5 | use sallyport::{Cursor, Request}; 6 | 7 | impl<'a> BaseSyscallHandler for super::Handler<'a> { 8 | fn translate_shim_to_host_addr(buf: *const T) -> usize { 9 | buf as _ 10 | } 11 | 12 | fn new_cursor(&mut self) -> Cursor { 13 | self.block.cursor() 14 | } 15 | 16 | unsafe fn proxy(&mut self, req: Request) -> sallyport::Result { 17 | self.block.msg.req = req; 18 | 19 | // prevent earlier writes from being moved beyond this point 20 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::Release); 21 | 22 | asm!("syscall"); 23 | 24 | // prevent later reads from being moved before this point 25 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::Acquire); 26 | 27 | self.block.msg.rep.into() 28 | } 29 | 30 | /// When we are under attack, we trip this circuit breaker and 31 | /// exit the enclave. Any attempt to re-enter the enclave after 32 | /// tripping the circuit breaker causes the enclave to immediately 33 | /// EEXIT. 34 | fn attacked(&mut self) -> ! { 35 | self.exit(1) 36 | } 37 | 38 | #[inline] 39 | fn unknown_syscall( 40 | &mut self, 41 | _a: Register, 42 | _b: Register, 43 | _c: Register, 44 | _d: Register, 45 | _e: Register, 46 | _f: Register, 47 | nr: usize, 48 | ) { 49 | debugln!(self, "unsupported syscall: {}", nr); 50 | } 51 | 52 | fn trace(&mut self, name: &str, argc: usize) { 53 | let argv = [ 54 | self.ssa.gpr.rdi, 55 | self.ssa.gpr.rsi, 56 | self.ssa.gpr.rdx, 57 | self.ssa.gpr.r10, 58 | self.ssa.gpr.r8, 59 | self.ssa.gpr.r9, 60 | ]; 61 | 62 | debug!(self, "{}(", name); 63 | for (i, arg) in argv[..argc].iter().copied().enumerate() { 64 | let prefix = if i > 0 { ", " } else { "" }; 65 | debug!(self, "{}0x{:x}", prefix, arg); 66 | } 67 | 68 | debugln!(self, ")"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /internal/shim-sgx/src/handler/enarx.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use sallyport::syscall::{BaseSyscallHandler, EnarxSyscallHandler}; 4 | use sallyport::untrusted::{UntrustedRef, UntrustedRefMut}; 5 | 6 | impl<'a> EnarxSyscallHandler for super::Handler<'a> { 7 | // NOTE: The 'nonce' field is called 'hash' here, as it is used to pass in 8 | // a hash of a public key from the client that is to be embedded in the Quote. 9 | // For more on this syscall, see: https://github.com/enarx/enarx-keepldr/issues/31 10 | fn get_attestation( 11 | &mut self, 12 | _hash: UntrustedRef, 13 | _hash_len: libc::size_t, 14 | _buf: UntrustedRefMut, 15 | _buf_len: libc::size_t, 16 | ) -> sallyport::Result { 17 | self.trace("get_att", 0); 18 | 19 | /* 20 | // If hash is NULL ptr, it is a Quote size request; return expected Quote size 21 | // without proxying to host. Otherwise get hash value. 22 | let hash = match hash.validate_slice(hash_len, self) { 23 | None => { 24 | let rep: sallyport::Reply = Ok([SGX_QUOTE_SIZE.into(), SGX_TECH.into()]).into(); 25 | return sallyport::Result::from(rep); 26 | } 27 | Some(h) => { 28 | if h.len() != 64 { 29 | return Err(libc::EINVAL); 30 | } 31 | let mut hash = [0u8; 64]; 32 | hash.copy_from_slice(h); 33 | hash 34 | } 35 | }; 36 | 37 | // Used internally for buffer size to host when getting TargetInfo 38 | const REPORT_LEN: usize = 512; 39 | 40 | // Validate output buf memory 41 | let buf = buf.validate_slice(buf_len, self).ok_or(libc::EFAULT)?; 42 | 43 | // Request TargetInfo from host by passing nonce as 0 44 | let c = self.new_cursor(); 45 | let (_, shim_buf_ptr) = c.alloc::(buf_len).or(Err(libc::EMSGSIZE))?; 46 | let req = request!(SYS_ENARX_GETATT => 0, 0, shim_buf_ptr.as_ptr(), REPORT_LEN); 47 | unsafe { self.proxy(req)? }; 48 | 49 | // Retrieve TargetInfo from sallyport block and call EREPORT to 50 | // create Report from TargetInfo. 51 | let mut ti = [0u8; 512]; 52 | let ti_len = ti.len(); 53 | let c = self.new_cursor(); 54 | 55 | unsafe { 56 | c.copy_into_slice(buf_len, &mut ti[..ti_len]) 57 | .or(Err(libc::EFAULT))?; 58 | } 59 | 60 | // Cannot generate a Report from dummy values 61 | if ti.eq(&SGX_DUMMY_TI) { 62 | buf.copy_from_slice(&SGX_DUMMY_QUOTE); 63 | let rep: sallyport::Reply = Ok([SGX_QUOTE_SIZE.into(), SGX_TECH.into()]).into(); 64 | return sallyport::Result::from(rep); 65 | } 66 | 67 | // Generate Report 68 | let mut target_info: TargetInfo = Default::default(); 69 | let mut f = [0u8; 8]; 70 | let mut x = [0u8; 8]; 71 | f.copy_from_slice(&ti[32..40]); 72 | x.copy_from_slice(&ti[40..48]); 73 | let f = u64::from_le_bytes(f); 74 | let x = u64::from_le_bytes(x); 75 | let att = Attributes::new( 76 | Flags::from_bits(f).ok_or(libc::EBADMSG)?, 77 | Xfrm::from_bits(x).ok_or(libc::EBADMSG)?, 78 | ); 79 | target_info.mrenclave.copy_from_slice(&ti[0..32]); 80 | target_info.attributes = att; 81 | let report: Report = unsafe { target_info.get_report(&ReportData(hash)) }; 82 | 83 | // Request Quote from host 84 | let report_slice = &[report]; 85 | let report_bytes = unsafe { 86 | core::slice::from_raw_parts( 87 | report_slice.as_ptr() as *const _ as *const u8, 88 | core::mem::size_of::(), 89 | ) 90 | }; 91 | 92 | let c = self.new_cursor(); 93 | let (c, shim_nonce_ptr) = c.copy_from_slice(report_bytes).or(Err(libc::EMSGSIZE))?; 94 | let (_, shim_buf_ptr) = c.alloc::(buf_len).or(Err(libc::EMSGSIZE))?; 95 | let req = request!(SYS_ENARX_GETATT => shim_nonce_ptr.as_ptr(), report_bytes.len(), shim_buf_ptr.as_ptr(), buf_len); 96 | let result = unsafe { self.proxy(req)? }; 97 | 98 | // Pass Quote back to code layer in buf 99 | let c = self.new_cursor(); 100 | let (c, _) = c.alloc::(report_bytes.len()).or(Err(libc::EMSGSIZE))?; 101 | 102 | let result_len: usize = result[0].into(); 103 | if result_len > buf_len { 104 | self.attacked() 105 | } 106 | 107 | unsafe { 108 | c.copy_into_slice(buf_len, &mut buf[..result_len]) 109 | .or(Err(libc::EFAULT))?; 110 | } 111 | */ 112 | 113 | let rep: sallyport::Reply = Err(libc::ENOSYS).into(); 114 | sallyport::Result::from(rep) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /internal/shim-sgx/src/handler/file.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use sallyport::request; 4 | use sallyport::syscall::{BaseSyscallHandler, FileSyscallHandler}; 5 | use sallyport::untrusted::{UntrustedRef, ValidateSlice}; 6 | 7 | impl<'a> FileSyscallHandler for super::Handler<'a> { 8 | /// Do a readv() syscall 9 | fn readv( 10 | &mut self, 11 | fd: libc::c_int, 12 | iovec: UntrustedRef, 13 | iovcnt: libc::c_int, 14 | ) -> sallyport::Result { 15 | self.trace("readv", 3); 16 | 17 | let mut size = 0usize; 18 | let trusted = iovec.validate_slice(iovcnt, self).ok_or(libc::EFAULT)?; 19 | 20 | let c = self.new_cursor(); 21 | 22 | let (c, untrusted) = c 23 | .copy_from_slice::(trusted) 24 | .or(Err(libc::EMSGSIZE))?; 25 | 26 | let mut c = c; 27 | for (t, u) in trusted.iter().zip(untrusted.iter_mut()) { 28 | let (nc, us) = c.alloc::(t.iov_len).or(Err(libc::EMSGSIZE))?; 29 | c = nc; 30 | u.iov_base = us.as_mut_ptr() as _; 31 | size += u.iov_len; 32 | } 33 | 34 | let req = request!(libc::SYS_readv => fd, untrusted, untrusted.len()); 35 | let ret = unsafe { self.proxy(req)? }; 36 | 37 | let mut read = ret[0].into(); 38 | if size < read { 39 | self.attacked(); 40 | } 41 | 42 | let c = self.new_cursor(); 43 | let (c, _) = c 44 | .alloc::(trusted.len()) 45 | .or(Err(libc::EMSGSIZE))?; 46 | 47 | let mut c = c; 48 | for t in trusted.iter() { 49 | let ts = t.iov_base as *mut u8; 50 | let ts_len: usize = t.iov_len; 51 | 52 | let sz = core::cmp::min(ts_len, read); 53 | 54 | let nc = unsafe { c.copy_into_raw_parts(ts_len, ts, sz) }.or(Err(libc::EMSGSIZE))?; 55 | c = nc; 56 | 57 | read -= sz; 58 | } 59 | 60 | Ok(ret) 61 | } 62 | 63 | /// Do a writev() syscall 64 | fn writev( 65 | &mut self, 66 | fd: libc::c_int, 67 | iovec: UntrustedRef, 68 | iovcnt: libc::c_int, 69 | ) -> sallyport::Result { 70 | self.trace("writev", 3); 71 | 72 | let mut size = 0usize; 73 | let trusted = iovec.validate_slice(iovcnt, self).ok_or(libc::EFAULT)?; 74 | let c = self.new_cursor(); 75 | let (c, untrusted) = c 76 | .copy_from_slice::(trusted) 77 | .or(Err(libc::EMSGSIZE))?; 78 | 79 | let mut c = c; 80 | for (t, mut u) in trusted.iter().zip(untrusted.iter_mut()) { 81 | let (nc, us) = unsafe { c.copy_from_raw_parts(t.iov_base as *const u8, t.iov_len) } 82 | .or(Err(libc::EMSGSIZE))?; 83 | c = nc; 84 | u.iov_base = us as _; 85 | size += u.iov_len; 86 | } 87 | 88 | let req = request!(libc::SYS_writev => fd, untrusted, untrusted.len()); 89 | let ret = unsafe { self.proxy(req)? }; 90 | 91 | if size < ret[0].into() { 92 | self.attacked(); 93 | } 94 | 95 | Ok(ret) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /internal/shim-sgx/src/handler/memory.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use sallyport::syscall::{BaseSyscallHandler, MemorySyscallHandler}; 4 | use sallyport::untrusted::UntrustedRef; 5 | 6 | impl<'a> MemorySyscallHandler for super::Handler<'a> { 7 | /// Do a brk() system call 8 | fn brk(&mut self, addr: *const u8) -> sallyport::Result { 9 | self.trace("brk", 1); 10 | 11 | let ret = self.heap.brk(addr as _); 12 | Ok([ret.into(), Default::default()]) 13 | } 14 | 15 | /// Do a mprotect() system call 16 | // Until EDMM, we can't change any page permissions. 17 | // What you get is what you get. Fake success. 18 | fn mprotect( 19 | &mut self, 20 | _addr: UntrustedRef, 21 | _len: libc::size_t, 22 | _prot: libc::c_int, 23 | ) -> sallyport::Result { 24 | self.trace("mprotect", 3); 25 | 26 | Ok(Default::default()) 27 | } 28 | 29 | /// Do a mmap() system call 30 | fn mmap( 31 | &mut self, 32 | addr: UntrustedRef, 33 | length: libc::size_t, 34 | prot: libc::c_int, 35 | flags: libc::c_int, 36 | fd: libc::c_int, 37 | offset: libc::off_t, 38 | ) -> sallyport::Result { 39 | self.trace("mmap", 6); 40 | 41 | let ret = self.heap.mmap::( 42 | addr.as_ptr() as _, 43 | length, 44 | prot, 45 | flags, 46 | fd, // Allow truncation! 47 | offset, 48 | )?; 49 | 50 | Ok([ret.into(), Default::default()]) 51 | } 52 | 53 | /// Do a munmap() system call 54 | fn munmap(&mut self, addr: UntrustedRef, length: libc::size_t) -> sallyport::Result { 55 | self.trace("munmap", 2); 56 | 57 | self.heap 58 | .munmap::(addr.as_ptr() as _, length)?; 59 | Ok(Default::default()) 60 | } 61 | 62 | // Do madvise syscall 63 | // We don't actually support this. So, fake success. 64 | fn madvise( 65 | &mut self, 66 | _addr: *const libc::c_void, 67 | _length: libc::size_t, 68 | _advice: libc::c_int, 69 | ) -> sallyport::Result { 70 | self.trace("madvise", 3); 71 | Ok(Default::default()) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /internal/shim-sgx/src/handler/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | macro_rules! debug { 4 | ($dst:expr, $($arg:tt)*) => { 5 | #[allow(unused_must_use)] { 6 | if $crate::DEBUG { 7 | use core::fmt::Write; 8 | write!($dst, $($arg)*); 9 | } 10 | } 11 | }; 12 | } 13 | 14 | macro_rules! debugln { 15 | ($dst:expr) => { debugln!($dst,) }; 16 | ($dst:expr, $($arg:tt)*) => { 17 | #[allow(unused_must_use)] { 18 | if $crate::DEBUG { 19 | use core::fmt::Write; 20 | writeln!($dst, $($arg)*); 21 | } 22 | } 23 | }; 24 | } 25 | 26 | mod base; 27 | mod enarx; 28 | mod file; 29 | mod memory; 30 | mod other; 31 | mod process; 32 | 33 | use core::fmt::Write; 34 | use core::ptr::read_unaligned; 35 | 36 | use enarx_heap::Heap; 37 | use lset::Line; 38 | use sallyport::syscall::*; 39 | use sallyport::{request, Block}; 40 | use sgx::ssa::StateSaveArea; 41 | use x86_64::structures::idt::ExceptionVector; 42 | 43 | // Opcode constants, details in Volume 2 of the Intel 64 and IA-32 Architectures Software 44 | // Developer's Manual 45 | const OP_SYSCALL: u16 = 0x050f; 46 | const OP_CPUID: u16 = 0xa20f; 47 | 48 | pub struct Handler<'a> { 49 | block: &'a mut Block, 50 | ssa: &'a mut StateSaveArea, 51 | heap: Heap, 52 | } 53 | 54 | impl<'a> Write for Handler<'a> { 55 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 56 | if s.as_bytes().is_empty() { 57 | return Ok(()); 58 | } 59 | 60 | let c = self.new_cursor(); 61 | let (_, untrusted) = c.copy_from_slice(s.as_bytes()).or(Err(core::fmt::Error))?; 62 | 63 | let req = request!(libc::SYS_write => libc::STDERR_FILENO, untrusted, untrusted.len()); 64 | let res = unsafe { self.proxy(req) }; 65 | 66 | match res { 67 | Ok(res) if usize::from(res[0]) > s.bytes().len() => self.attacked(), 68 | Ok(res) if usize::from(res[0]) == s.bytes().len() => Ok(()), 69 | _ => Err(core::fmt::Error), 70 | } 71 | } 72 | } 73 | 74 | impl<'a> Handler<'a> { 75 | fn new(ssa: &'a mut StateSaveArea, block: &'a mut Block, heap: Line) -> Self { 76 | Self { 77 | ssa, 78 | block, 79 | heap: unsafe { Heap::new(heap.into()) }, 80 | } 81 | } 82 | 83 | /// Finish handling an exception 84 | pub fn finish(ssa: &'a mut StateSaveArea) { 85 | if let Some(ExceptionVector::InvalidOpcode) = ssa.vector() { 86 | if let OP_SYSCALL | OP_CPUID = unsafe { read_unaligned(ssa.gpr.rip as _) } { 87 | // Skip the instruction. 88 | ssa.gpr.rip += 2; 89 | return; 90 | } 91 | } 92 | 93 | unsafe { asm!("ud2", options(noreturn)) }; 94 | } 95 | 96 | /// Handle an exception 97 | pub fn handle(ssa: &'a mut StateSaveArea, block: &'a mut Block, heap: Line) { 98 | let mut h = Self::new(ssa, block, heap); 99 | 100 | match h.ssa.vector() { 101 | Some(ExceptionVector::InvalidOpcode) => { 102 | match unsafe { read_unaligned(h.ssa.gpr.rip as _) } { 103 | OP_SYSCALL => h.handle_syscall(), 104 | OP_CPUID => h.handle_cpuid(), 105 | r => { 106 | debugln!(h, "unsupported opcode: {:?}", r); 107 | h.exit(1) 108 | } 109 | } 110 | } 111 | 112 | _ => h.attacked(), 113 | } 114 | } 115 | 116 | fn handle_syscall(&mut self) { 117 | let ret = self.syscall( 118 | self.ssa.gpr.rdi.into(), 119 | self.ssa.gpr.rsi.into(), 120 | self.ssa.gpr.rdx.into(), 121 | self.ssa.gpr.r10.into(), 122 | self.ssa.gpr.r8.into(), 123 | self.ssa.gpr.r9.into(), 124 | self.ssa.gpr.rax as usize, 125 | ); 126 | 127 | self.ssa.gpr.rip += 2; 128 | 129 | match ret { 130 | Err(e) => self.ssa.gpr.rax = -e as u64, 131 | Ok([rax, rdx]) => { 132 | self.ssa.gpr.rax = rax.into(); 133 | self.ssa.gpr.rdx = rdx.into(); 134 | } 135 | } 136 | } 137 | 138 | fn handle_cpuid(&mut self) { 139 | debug!( 140 | self, 141 | "cpuid({:08x}, {:08x})", 142 | self.ssa.gpr.rax.clone(), 143 | self.ssa.gpr.rcx.clone(), 144 | ); 145 | 146 | self.block.msg.req = request!(SYS_ENARX_CPUID => self.ssa.gpr.rax, self.ssa.gpr.rcx); 147 | 148 | unsafe { 149 | // prevent earlier writes from being moved beyond this point 150 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::Release); 151 | 152 | asm!("cpuid"); 153 | 154 | // prevent later reads from being moved before this point 155 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::Acquire); 156 | 157 | self.ssa.gpr.rax = self.block.msg.req.arg[0].into(); 158 | self.ssa.gpr.rbx = self.block.msg.req.arg[1].into(); 159 | self.ssa.gpr.rcx = self.block.msg.req.arg[2].into(); 160 | self.ssa.gpr.rdx = self.block.msg.req.arg[3].into(); 161 | } 162 | 163 | debugln!( 164 | self, 165 | " = ({:08x}, {:08x}, {:08x}, {:08x})", 166 | self.ssa.gpr.rax.clone(), 167 | self.ssa.gpr.rbx.clone(), 168 | self.ssa.gpr.rcx.clone(), 169 | self.ssa.gpr.rdx.clone() 170 | ); 171 | 172 | self.ssa.gpr.rip += 2; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /internal/shim-sgx/src/handler/other.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::Handler; 4 | 5 | use sallyport::syscall::{NetworkSyscallHandler, SyscallHandler, SystemSyscallHandler}; 6 | use sallyport::untrusted::AddressValidator; 7 | 8 | impl<'a> NetworkSyscallHandler for Handler<'a> {} 9 | impl<'a> SystemSyscallHandler for Handler<'a> {} 10 | impl<'a> SyscallHandler for Handler<'a> {} 11 | 12 | impl<'a> AddressValidator for Handler<'a> { 13 | fn validate_const_mem_fn(&self, _ptr: *const (), _size: usize) -> bool { 14 | // FIXME: https://github.com/enarx/enarx/issues/630 15 | true 16 | } 17 | 18 | fn validate_mut_mem_fn(&self, _ptr: *mut (), _size: usize) -> bool { 19 | // FIXME: https://github.com/enarx/enarx/issues/630 20 | true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /internal/shim-sgx/src/handler/process.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use sallyport::syscall::{BaseSyscallHandler, ProcessSyscallHandler}; 4 | use sallyport::syscall::{ARCH_GET_FS, ARCH_GET_GS, ARCH_SET_FS, ARCH_SET_GS}; 5 | 6 | impl<'a> ProcessSyscallHandler for super::Handler<'a> { 7 | /// Do an arch_prctl() syscall 8 | fn arch_prctl(&mut self, code: libc::c_int, addr: libc::c_ulong) -> sallyport::Result { 9 | self.trace("arch_prctl", 2); 10 | 11 | // TODO: Check that addr in %rdx does not point to an unmapped address 12 | // and is not outside of the process address space. 13 | match code { 14 | ARCH_SET_FS => self.ssa.gpr.fsbase = addr, 15 | ARCH_SET_GS => self.ssa.gpr.gsbase = addr, 16 | ARCH_GET_FS => return Err(libc::ENOSYS), 17 | ARCH_GET_GS => return Err(libc::ENOSYS), 18 | _ => return Err(libc::EINVAL), 19 | } 20 | 21 | Ok(Default::default()) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | targets = ["x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"] 4 | profile = "minimal" 5 | -------------------------------------------------------------------------------- /src/backend/binary.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::{Config, Loader, Mapper}; 4 | 5 | use std::convert::TryInto; 6 | 7 | use anyhow::{anyhow, Result}; 8 | use goblin::elf::{header::*, note::NoteIterator, program_header::*, Elf}; 9 | use mmarinus::{perms, Kind, Map}; 10 | use primordial::Page; 11 | use sallyport::elf; 12 | 13 | use std::ops::Range; 14 | 15 | #[derive(Clone, Debug)] 16 | struct Segment<'a> { 17 | bytes: &'a [u8], 18 | range: Range, 19 | skipb: usize, 20 | flags: u32, 21 | } 22 | 23 | pub struct Binary<'a>(&'a [u8], Elf<'a>); 24 | 25 | impl<'a> Binary<'a> { 26 | fn new(bytes: &'a [u8]) -> Result { 27 | let elf = Elf::parse(bytes)?; 28 | 29 | if elf.header.e_ident[EI_CLASS] != ELFCLASS64 { 30 | return Err(anyhow!("unsupported ELF header: e_ident[EI_CLASS]")); 31 | } 32 | 33 | if elf.header.e_ident[EI_DATA] != ELFDATA2LSB { 34 | return Err(anyhow!("unsupported ELF header: e_ident[EI_DATA]",)); 35 | } 36 | 37 | if elf.header.e_ident[EI_VERSION] != EV_CURRENT { 38 | return Err(anyhow!("unsupported ELF header: e_ident[EI_VERSION]",)); 39 | } 40 | 41 | if elf.header.e_machine != EM_X86_64 { 42 | return Err(anyhow!("unsupported ELF header: e_machine")); 43 | } 44 | 45 | if elf.header.e_version != EV_CURRENT.into() { 46 | return Err(anyhow!("unsupported ELF header: e_version")); 47 | } 48 | 49 | if elf.program_headers.iter().any(|ph| ph.p_type == PT_INTERP) { 50 | return Err(anyhow!("unsupported ELF header: p_type == PT_INTERP",)); 51 | } 52 | 53 | if !elf 54 | .program_headers 55 | .iter() 56 | .filter(|ph| ph.p_type == PT_LOAD) 57 | .filter(|ph| elf.header.e_entry >= ph.p_vaddr) 58 | .any(|ph| elf.header.e_entry < ph.p_vaddr + ph.p_memsz) 59 | { 60 | return Err(anyhow!("unsupported ELF header: e_entry")); 61 | } 62 | 63 | Ok(Self(bytes, elf)) 64 | } 65 | 66 | fn segments(&self, relocate: usize) -> impl Iterator { 67 | assert_eq!(relocate % Page::SIZE, 0); 68 | 69 | self.headers(PT_LOAD).map(move |phdr| { 70 | let range = phdr.vm_range(); 71 | let range = range.start + relocate..range.end + relocate + Page::SIZE - 1; 72 | 73 | Segment { 74 | bytes: &self.0[phdr.file_range()], 75 | skipb: phdr.p_vaddr as usize % Page::SIZE, 76 | flags: phdr.p_flags, 77 | range: Range { 78 | start: range.start / Page::SIZE * Page::SIZE, 79 | end: range.end / Page::SIZE * Page::SIZE, 80 | }, 81 | } 82 | }) 83 | } 84 | 85 | /// Find the total memory region for the binary. 86 | fn range(&self) -> Range { 87 | let lo = self 88 | .headers(PT_LOAD) 89 | .map(|phdr| phdr.vm_range().start) 90 | .min(); 91 | 92 | let hi = self.headers(PT_LOAD).map(|phdr| phdr.vm_range().end).max(); 93 | 94 | lo.unwrap_or_default()..hi.unwrap_or_default() 95 | } 96 | 97 | pub fn headers(&self, kind: u32) -> impl Iterator { 98 | self.1 99 | .program_headers 100 | .iter() 101 | .filter(move |ph| ph.p_type == kind) 102 | } 103 | 104 | pub fn notes(&self, name: &'a str, kind: u32) -> impl Iterator { 105 | let empty = NoteIterator { 106 | iters: vec![], 107 | index: 0, 108 | }; 109 | 110 | self.1 111 | .iter_note_headers(self.0) 112 | .unwrap_or(empty) 113 | .filter_map(Result::ok) 114 | .filter(move |n| n.n_type == kind) 115 | .filter(move |n| n.name == name) 116 | .map(|n| n.desc) 117 | } 118 | 119 | /// Read a note from the note section 120 | /// 121 | /// # Safety 122 | /// 123 | /// This function transmutes the bytes into the specified type. Beware! 124 | #[allow(dead_code)] 125 | pub unsafe fn note(&self, name: &str, kind: u32) -> Option { 126 | use core::mem::size_of; 127 | 128 | for note in self.notes(name, kind) { 129 | if note.len() == size_of::() { 130 | return Some(note.as_ptr().cast::().read_unaligned()); 131 | } 132 | } 133 | 134 | None 135 | } 136 | } 137 | 138 | impl Loader for T { 139 | fn load(shim: impl AsRef<[u8]>, exec: impl AsRef<[u8]>) -> Result { 140 | // Parse the ELF files. 141 | let sbin = Binary::new(shim.as_ref())?; 142 | let ebin = Binary::new(exec.as_ref())?; 143 | 144 | // Find the offset for loading the code. 145 | let slot = sbin 146 | .headers(sallyport::elf::pt::EXEC) 147 | .next() 148 | .ok_or_else(|| anyhow!("Shim is missing the executable slot!"))? 149 | .vm_range(); 150 | 151 | // Check the bounds of the executable. 152 | let range = ebin.range(); 153 | if range.start != 0 || range.end > slot.end - slot.start { 154 | return Err(anyhow!("The executable doesn't fit in the slot!")); 155 | } 156 | 157 | // Check sallyport compatibility 158 | let version = semver::Version::parse(sallyport::VERSION).unwrap(); 159 | let supported = sbin 160 | .notes(elf::note::NAME, elf::note::REQUIRES) 161 | .filter_map(|n| std::str::from_utf8(n).ok()) 162 | .filter_map(|n| semver::VersionReq::parse(n).ok()) 163 | .any(|req| req.matches(&version)); 164 | if !supported { 165 | return Err(anyhow!("Unable to satisfy sallyport version requirement!")); 166 | } 167 | 168 | // Parse the config and create a builder. 169 | let mut loader: Self = Self::Config::new(&sbin, &ebin)?.try_into()?; 170 | 171 | // Get an array of all final segment locations (relocated). 172 | let ssegs: Vec = sbin.segments(0).collect(); 173 | let esegs: Vec = ebin.segments(slot.start).collect(); 174 | 175 | // Ensure no segments overlap in memory. 176 | let mut sorted: Vec<_> = ssegs.iter().chain(esegs.iter()).collect(); 177 | sorted.sort_unstable_by_key(|seg| seg.range.start); 178 | for pair in sorted.windows(2) { 179 | if pair[0].range.end > pair[1].range.start { 180 | return Err(anyhow!("Segments overlap!")); 181 | } 182 | } 183 | 184 | // Load segments. 185 | for seg in ssegs.iter().chain(esegs.iter()) { 186 | // Create the mapping and copy the bytes. 187 | let mut map = Map::map(seg.range.end - seg.range.start) 188 | .anywhere() 189 | .anonymously() 190 | .known::(Kind::Private)?; 191 | map[seg.skipb..][..seg.bytes.len()].copy_from_slice(seg.bytes); 192 | 193 | // Pass the region to the builder. 194 | let flags = Self::Config::flags(seg.flags); 195 | loader.map(map, seg.range.start, flags)?; 196 | } 197 | 198 | loader.try_into() 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/backend/kvm/builder.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::mem::Region; 4 | use anyhow::{Error, Result}; 5 | use kvm_bindings::bindings::kvm_userspace_memory_region; 6 | use kvm_bindings::fam_wrappers::KVM_MAX_CPUID_ENTRIES; 7 | use kvm_ioctls::{Kvm, VmFd}; 8 | use mmarinus::{perms, Map}; 9 | use sallyport::Block; 10 | use std::convert::TryFrom; 11 | use std::mem::size_of; 12 | use std::sync::{Arc, RwLock}; 13 | use x86_64::VirtAddr; 14 | 15 | pub struct Builder { 16 | kvm_fd: Kvm, 17 | vm_fd: VmFd, 18 | regions: Vec, 19 | sallyports: Vec>, 20 | } 21 | 22 | impl TryFrom for Builder { 23 | type Error = Error; 24 | 25 | fn try_from(_config: super::config::Config) -> Result { 26 | let kvm_fd = Kvm::new()?; 27 | let vm_fd = kvm_fd.create_vm()?; 28 | 29 | Ok(Builder { 30 | kvm_fd, 31 | vm_fd, 32 | regions: Vec::new(), 33 | sallyports: Vec::new(), 34 | }) 35 | } 36 | } 37 | 38 | impl super::super::Mapper for Builder { 39 | type Config = super::config::Config; 40 | type Output = Arc; 41 | 42 | fn map( 43 | &mut self, 44 | pages: Map, 45 | to: usize, 46 | sallyport: bool, 47 | ) -> anyhow::Result<()> { 48 | // Ignore regions with no pages. 49 | if pages.is_empty() { 50 | return Ok(()); 51 | } 52 | 53 | if sallyport { 54 | for start in (0..pages.size()).step_by(size_of::()) { 55 | if start + size_of::() <= pages.size() { 56 | let virt = VirtAddr::from_ptr(pages.as_ptr()) + start; 57 | self.sallyports.push(Some(virt)); 58 | } 59 | } 60 | } 61 | 62 | let mem_region = kvm_userspace_memory_region { 63 | slot: self.regions.len() as _, 64 | flags: 0, 65 | guest_phys_addr: to as _, 66 | memory_size: pages.size() as _, 67 | userspace_addr: pages.addr() as _, 68 | }; 69 | 70 | unsafe { self.vm_fd.set_user_memory_region(mem_region)? }; 71 | 72 | self.regions.push(Region::new(mem_region, pages)); 73 | 74 | Ok(()) 75 | } 76 | } 77 | 78 | impl TryFrom for Arc { 79 | type Error = Error; 80 | 81 | fn try_from(builder: Builder) -> Result { 82 | // If no LOAD segment were defined as sallyport blocks 83 | if builder.sallyports.is_empty() { 84 | anyhow::bail!("No sallyport blocks defined!"); 85 | } 86 | 87 | let cpuids = builder.kvm_fd.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES)?; 88 | 89 | let vcpu_fd = builder.vm_fd.create_vcpu(0)?; 90 | vcpu_fd.set_cpuid2(&cpuids)?; 91 | 92 | // FIXME: this will be removed with relative addresses in sallyport 93 | // unwrap, because we have at least one block 94 | let sallyport_block_start = builder.sallyports.first().unwrap().unwrap(); 95 | 96 | Ok(Arc::new(RwLock::new(super::Keep { 97 | kvm_fd: builder.kvm_fd, 98 | vm_fd: builder.vm_fd, 99 | cpu_fds: vec![vcpu_fd], 100 | regions: builder.regions, 101 | sallyports: builder.sallyports, 102 | sallyport_start: sallyport_block_start, 103 | }))) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/backend/kvm/config.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use anyhow::Result; 4 | use goblin::elf64::program_header::PT_LOAD; 5 | use sallyport::elf::pf::kvm::SALLYPORT; 6 | 7 | pub struct Config {} 8 | 9 | impl super::super::Config for Config { 10 | type Flags = bool; 11 | 12 | fn flags(flags: u32) -> Self::Flags { 13 | flags & SALLYPORT != 0 14 | } 15 | 16 | fn new(shim: &super::super::Binary, _exec: &super::super::Binary) -> Result { 17 | let sallyport_headers = shim.headers(PT_LOAD).filter(|p| p.p_flags & SALLYPORT != 0); 18 | 19 | if sallyport_headers.count() != 1 { 20 | anyhow::bail!("KVM shim must contain exactly one sallyport PT_LOAD segment.") 21 | } 22 | 23 | Ok(Self {}) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/backend/kvm/data.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use crate::backend::Datum; 4 | use kvm_ioctls::Kvm; 5 | 6 | pub fn dev_kvm() -> Datum { 7 | let dev_kvm = std::path::Path::new("/dev/kvm"); 8 | 9 | Datum { 10 | name: "Driver".into(), 11 | pass: dev_kvm.exists(), 12 | info: Some("/dev/kvm".into()), 13 | mesg: None, 14 | } 15 | } 16 | 17 | pub fn kvm_version() -> Datum { 18 | let version = Kvm::new().map(|kvm| kvm.get_api_version()); 19 | let (pass, info) = match version { 20 | Ok(v) => (v == 12, Some(v.to_string())), 21 | Err(_) => (false, None), 22 | }; 23 | 24 | Datum { 25 | name: " API Version".into(), 26 | pass, 27 | info, 28 | mesg: None, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/backend/kvm/mem.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::KvmUserspaceMemoryRegion; 4 | 5 | use lset::Span; 6 | use mmarinus::{perms, Map}; 7 | use x86_64::VirtAddr; 8 | 9 | pub struct Region { 10 | kvm_region: KvmUserspaceMemoryRegion, 11 | _backing: Map, 12 | } 13 | 14 | impl Region { 15 | pub fn new(kvm_region: KvmUserspaceMemoryRegion, backing: Map) -> Self { 16 | Self { 17 | kvm_region, 18 | _backing: backing, 19 | } 20 | } 21 | 22 | pub fn as_virt(&self) -> Span { 23 | Span { 24 | start: VirtAddr::new(self.kvm_region.userspace_addr), 25 | count: self.kvm_region.memory_size, 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/backend/kvm/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::Loader; 4 | use crate::backend::kvm::data::{dev_kvm, kvm_version}; 5 | use crate::backend::kvm::mem::Region; 6 | use anyhow::Result; 7 | use kvm_bindings::bindings::kvm_userspace_memory_region; 8 | pub use kvm_bindings::kvm_userspace_memory_region as KvmUserspaceMemoryRegion; 9 | use kvm_ioctls::Kvm; 10 | use kvm_ioctls::{VcpuFd, VmFd}; 11 | use mmarinus::{perms, Map}; 12 | use std::sync::Arc; 13 | use x86_64::VirtAddr; 14 | 15 | mod builder; 16 | mod config; 17 | mod data; 18 | mod mem; 19 | mod thread; 20 | 21 | impl Keep { 22 | pub fn map(&mut self, pages: Map, to: usize) -> std::io::Result<&mut Region> { 23 | let region = kvm_userspace_memory_region { 24 | slot: self.regions.len() as u32, 25 | flags: 0, 26 | guest_phys_addr: to as u64, 27 | memory_size: pages.len() as u64, 28 | userspace_addr: pages.addr() as u64, 29 | }; 30 | 31 | unsafe { self.vm_fd.set_user_memory_region(region)? }; 32 | 33 | self.regions.push(Region::new(region, pages)); 34 | Ok(self.regions.last_mut().unwrap()) 35 | } 36 | } 37 | 38 | struct Keep { 39 | kvm_fd: Kvm, 40 | vm_fd: VmFd, 41 | cpu_fds: Vec, 42 | // FIXME: This will be removed in the near future 43 | sallyport_start: VirtAddr, 44 | sallyports: Vec>, 45 | regions: Vec, 46 | } 47 | 48 | pub struct Backend; 49 | 50 | impl crate::backend::Backend for Backend { 51 | #[inline] 52 | fn name(&self) -> &'static str { 53 | "kvm" 54 | } 55 | 56 | #[inline] 57 | fn shim(&self) -> &'static [u8] { 58 | include_bytes!(concat!(env!("OUT_DIR"), "/bin/shim-sev")) 59 | } 60 | 61 | #[inline] 62 | fn have(&self) -> bool { 63 | data::dev_kvm().pass 64 | } 65 | 66 | fn data(&self) -> Vec { 67 | vec![dev_kvm(), kvm_version()] 68 | } 69 | 70 | #[inline] 71 | fn keep(&self, shim: &[u8], exec: &[u8]) -> Result> { 72 | builder::Builder::load(shim, exec) 73 | } 74 | 75 | #[inline] 76 | fn hash(&self, _shim: &[u8], _exec: &[u8]) -> Result> { 77 | Ok(Vec::new()) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/backend/kvm/thread.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::super::Command; 4 | 5 | use std::sync::{Arc, RwLock}; 6 | 7 | use anyhow::{anyhow, Result}; 8 | use kvm_ioctls::{VcpuExit, VcpuFd}; 9 | use mmarinus::{perms, Kind, Map}; 10 | use primordial::{Address, Register}; 11 | use sallyport::syscall::enarx::MemInfo; 12 | use sallyport::syscall::{SYS_ENARX_BALLOON_MEMORY, SYS_ENARX_MEM_INFO}; 13 | use sallyport::Block; 14 | use sallyport::{Request, KVM_SYSCALL_TRIGGER_PORT}; 15 | 16 | pub struct Thread { 17 | keep: Arc>, 18 | vcpu_fd: Option, 19 | } 20 | 21 | impl Drop for Thread { 22 | fn drop(&mut self) { 23 | let vcpu_fd = self.vcpu_fd.take().unwrap(); 24 | self.keep.write().unwrap().cpu_fds.push(vcpu_fd); 25 | } 26 | } 27 | 28 | impl super::super::Keep for RwLock { 29 | fn spawn(self: Arc) -> Result>> { 30 | let cpu_opt = self.write().unwrap().cpu_fds.pop(); 31 | match cpu_opt { 32 | None => Ok(None), 33 | Some(vcpu_fd) => Ok(Some(Box::new(Thread { 34 | keep: self, 35 | vcpu_fd: Some(vcpu_fd), 36 | }))), 37 | } 38 | } 39 | } 40 | 41 | impl Thread { 42 | pub fn balloon(&mut self, req: &Request) -> Result<[Register; 2], i32> { 43 | let log2: usize = req.arg[0].into(); 44 | let npgs: usize = req.arg[1].into(); // Number of Pages 45 | let addr: usize = req.arg[2].into(); // Guest Physical Address 46 | let size: usize = 1 << log2; // Page Size 47 | 48 | // Get the current page size 49 | let pgsz = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) } as usize; 50 | assert!(pgsz.is_power_of_two()); 51 | 52 | // Check that the page size is supported and addr is aligned 53 | if size != pgsz || addr % size != 0 { 54 | return Err(libc::EINVAL); 55 | } 56 | 57 | // Allocate the new memory 58 | let pages = Map::map(size * npgs) 59 | .anywhere() 60 | .anonymously() 61 | .known::(Kind::Private) 62 | .map_err(|e| e.err.raw_os_error().unwrap_or(libc::ENOTSUP))?; 63 | 64 | let mut keep = self.keep.write().unwrap(); 65 | 66 | // Map the memory into the VM 67 | let vaddr = keep 68 | .map(pages, addr) 69 | .map_err(|e| e.raw_os_error().unwrap_or(libc::ENOTSUP))? 70 | .as_virt() 71 | .start; 72 | 73 | Ok([vaddr.as_u64().into(), 0.into()]) 74 | } 75 | 76 | pub fn meminfo(&self, block: &mut Block) -> Result<[Register; 2], i32> { 77 | let keep = self.keep.read().unwrap(); 78 | 79 | // The maximum number of memory slots possible for a virtual machine 80 | // minus the ones which were already used. 81 | let mem_slots = keep.kvm_fd.get_nr_memslots() - keep.regions.len(); 82 | 83 | // FIXME: 84 | // Obsolete, if [host side syscall verification and address translation](https://github.com/enarx/enarx/issues/957) 85 | // is implemented. 86 | let virt_start = Address::from(keep.sallyport_start.as_mut_ptr()); 87 | 88 | let mem_info: MemInfo = MemInfo { 89 | virt_start, 90 | mem_slots, 91 | }; 92 | 93 | let c = block.cursor(); 94 | c.write(&mem_info).map_err(|_| libc::ENOBUFS)?; 95 | 96 | Ok([0.into(), 0.into()]) 97 | } 98 | } 99 | 100 | impl super::super::Thread for Thread { 101 | fn enter(&mut self) -> Result { 102 | let vcpu_fd = self.vcpu_fd.as_mut().unwrap(); 103 | match vcpu_fd.run()? { 104 | VcpuExit::IoOut(KVM_SYSCALL_TRIGGER_PORT, data) => { 105 | debug_assert_eq!(data.len(), 2); 106 | let block_nr = data[0] as usize + ((data[1] as usize) << 8); 107 | 108 | let block_virt = self.keep.write().unwrap().sallyports[block_nr] 109 | .take() 110 | .unwrap(); 111 | 112 | // If some other thread tried to use the same block, the above unwrap would have panicked. 113 | let block = unsafe { &mut *block_virt.as_mut_ptr::() }; 114 | 115 | // To avoid clashing of rep and req in the union, clone the request 116 | let req = unsafe { block.msg.req }; 117 | 118 | let ret = match i64::from(req.num) { 119 | SYS_ENARX_BALLOON_MEMORY => { 120 | let rep = self.balloon(&req).into(); 121 | block.msg.rep = rep; 122 | Ok(Command::Continue) 123 | } 124 | 125 | SYS_ENARX_MEM_INFO => { 126 | block.msg.rep = self.meminfo(block).into(); 127 | Ok(Command::Continue) 128 | } 129 | 130 | _ => Ok(Command::SysCall(block)), 131 | }; 132 | 133 | self.keep.write().unwrap().sallyports[block_nr].replace(block_virt); 134 | ret 135 | } 136 | 137 | #[cfg(debug_assertions)] 138 | reason => Err(anyhow!( 139 | "{:?} {:#x?} {:#x?}", 140 | reason, 141 | vcpu_fd.get_regs(), 142 | vcpu_fd.get_sregs() 143 | )), 144 | 145 | #[cfg(not(debug_assertions))] 146 | reason => Err(anyhow!("{:?}", reason)), 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/backend/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(feature = "backend-kvm")] 4 | pub mod kvm; 5 | 6 | #[cfg(feature = "backend-sgx")] 7 | pub mod sgx; 8 | 9 | mod binary; 10 | mod probe; 11 | 12 | use binary::Binary; 13 | 14 | use std::convert::TryFrom; 15 | use std::sync::Arc; 16 | 17 | use anyhow::{Error, Result}; 18 | use mmarinus::{perms, Map}; 19 | use sallyport::Block; 20 | 21 | trait Config: Sized { 22 | type Flags; 23 | 24 | fn flags(flags: u32) -> Self::Flags; 25 | fn new(shim: &Binary, exec: &Binary) -> Result; 26 | } 27 | 28 | trait Mapper: Sized + TryFrom { 29 | type Config: Config; 30 | type Output: TryFrom; 31 | 32 | fn map( 33 | &mut self, 34 | pages: Map, 35 | to: usize, 36 | with: ::Flags, 37 | ) -> Result<()>; 38 | } 39 | 40 | trait Loader: Mapper { 41 | fn load(shim: impl AsRef<[u8]>, exec: impl AsRef<[u8]>) -> Result; 42 | } 43 | 44 | pub trait Backend { 45 | /// The name of the backend 46 | fn name(&self) -> &'static str; 47 | 48 | /// The builtin shim 49 | fn shim(&self) -> &'static [u8]; 50 | 51 | /// The tests that show platform support for the backend 52 | fn data(&self) -> Vec; 53 | 54 | /// Create a keep instance 55 | fn keep(&self, shim: &[u8], exec: &[u8]) -> Result>; 56 | 57 | /// Hash the inputs 58 | fn hash(&self, shim: &[u8], exec: &[u8]) -> Result>; 59 | 60 | /// Whether or not the platform has support for this keep type 61 | fn have(&self) -> bool { 62 | !self.data().iter().fold(false, |e, d| e | !d.pass) 63 | } 64 | } 65 | 66 | pub struct Datum { 67 | /// The name of this datum. 68 | pub name: String, 69 | 70 | /// Whether the datum indicates support for the platform or not. 71 | pub pass: bool, 72 | 73 | /// Short additional information to display to the user. 74 | pub info: Option, 75 | 76 | /// Longer explanatory message on how to resolve problems. 77 | pub mesg: Option, 78 | } 79 | 80 | pub trait Keep { 81 | /// Creates a new thread in the keep. 82 | fn spawn(self: Arc) -> Result>>; 83 | } 84 | 85 | pub trait Thread { 86 | /// Enters the keep. 87 | fn enter(&mut self) -> Result; 88 | } 89 | 90 | pub enum Command<'a> { 91 | #[allow(dead_code)] 92 | SysCall(&'a mut Block), 93 | 94 | #[allow(dead_code)] 95 | CpuId(&'a mut Block), 96 | 97 | #[allow(dead_code)] 98 | Continue, 99 | } 100 | -------------------------------------------------------------------------------- /src/backend/probe/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | pub mod x86_64; 4 | -------------------------------------------------------------------------------- /src/backend/probe/x86_64.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use crate::backend::Datum; 4 | 5 | use std::arch::x86_64::{CpuidResult, __cpuid_count}; 6 | use std::convert::From; 7 | use std::io::{Error, ErrorKind, Result}; 8 | use std::mem::transmute; 9 | 10 | #[derive(Clone, Copy, Eq, PartialEq)] 11 | pub enum Vendor { 12 | Amd, 13 | Intel, 14 | } 15 | 16 | impl Vendor { 17 | pub fn get() -> Result { 18 | let res = unsafe { __cpuid_count(0x00000000, 0x00000000) }; 19 | let name: [u8; 12] = unsafe { transmute([res.ebx, res.edx, res.ecx]) }; 20 | let name = std::str::from_utf8(&name[..]).unwrap(); 21 | 22 | Ok(match name { 23 | "AuthenticAMD" => Self::Amd, 24 | "GenuineIntel" => Self::Intel, 25 | _ => { 26 | return Err(Error::new( 27 | ErrorKind::Other, 28 | format!("unsupported vendor: {}", name), 29 | )) 30 | } 31 | }) 32 | } 33 | } 34 | 35 | pub struct CpuId { 36 | pub name: &'static str, 37 | pub leaf: u32, 38 | pub subl: u32, 39 | pub func: fn(CpuidResult) -> (bool, Option), 40 | pub vend: Option, 41 | } 42 | 43 | impl From<&CpuId> for Datum { 44 | fn from(cpuid: &CpuId) -> Datum { 45 | let datum = Datum { 46 | name: cpuid.name.into(), 47 | pass: false, 48 | info: None, 49 | mesg: None, 50 | }; 51 | 52 | let this_vendor = match Vendor::get() { 53 | Ok(v) => v, 54 | Err(_) => return datum, 55 | }; 56 | 57 | // If there is no vendor requirement for this CPUID 58 | // instruction, then the current vendor will suffice. 59 | let req_vendor = cpuid.vend.unwrap_or(this_vendor); 60 | 61 | // Many CPUID leaves aren't meaningful unless we know 62 | // for sure what kind of vendor we're running on. 63 | let (pass, info) = if this_vendor == req_vendor { 64 | (cpuid.func)(unsafe { __cpuid_count(cpuid.leaf, cpuid.subl) }) 65 | } else { 66 | (false, None) 67 | }; 68 | 69 | Datum { 70 | name: datum.name, 71 | pass, 72 | info, 73 | mesg: datum.mesg, 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/backend/sgx/builder.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::config::Config; 4 | use super::ioctls::*; 5 | 6 | use std::convert::TryFrom; 7 | use std::fs::{File, OpenOptions}; 8 | use std::sync::{Arc, RwLock}; 9 | 10 | use anyhow::{Error, Result}; 11 | use mmarinus::{perms, Kind, Map}; 12 | use primordial::Page; 13 | use sgx::crypto::{openssl::*, *}; 14 | use sgx::page::{Class, Flags, SecInfo}; 15 | use sgx::signature::{Author, Hasher, Signature}; 16 | 17 | pub struct Builder { 18 | file: File, 19 | cnfg: Config, 20 | hash: Hasher, 21 | mmap: Map, 22 | perm: Vec<(*const (), usize, SecInfo)>, 23 | tcsp: Vec<*const super::Tcs>, 24 | } 25 | 26 | impl TryFrom for Builder { 27 | type Error = Error; 28 | 29 | fn try_from(config: super::config::Config) -> Result { 30 | assert!(config.size.is_power_of_two()); // This is verified by `Config`... 31 | 32 | // Map the memory for the enclave 33 | // We map twice as much as we need so that we can naturally align it. 34 | let map = Map::map(config.size * 2) 35 | .anywhere() 36 | .anonymously() 37 | .known::(Kind::Private)?; 38 | 39 | // Naturally align the mapping. 40 | let addr = (map.addr() + config.size - 1) / config.size * config.size; 41 | let (_, map) = map.split_at(addr)?; 42 | let (map, _) = map.split(config.size)?; 43 | 44 | // Open the device. 45 | let mut file = OpenOptions::new() 46 | .read(true) 47 | .write(true) 48 | .open("/dev/sgx_enclave")?; 49 | 50 | // Create the enclave. 51 | let secs = config 52 | .parameters 53 | .secs(map.addr() as *const (), map.size(), config.ssap); 54 | let create = Create::new(&secs); 55 | ENCLAVE_CREATE.ioctl(&mut file, &create)?; 56 | 57 | Ok(Builder { 58 | hash: Hasher::new(config.size, config.ssap), 59 | mmap: map.into(), // Discard typed permissions 60 | perm: Vec::new(), 61 | tcsp: Vec::new(), 62 | cnfg: config, 63 | file, 64 | }) 65 | } 66 | } 67 | 68 | impl super::super::Mapper for Builder { 69 | type Config = super::config::Config; 70 | type Output = Arc; 71 | 72 | fn map( 73 | &mut self, 74 | pages: Map, 75 | to: usize, 76 | with: (SecInfo, bool), 77 | ) -> anyhow::Result<()> { 78 | // Ignore regions with no pages. 79 | if pages.is_empty() { 80 | return Ok(()); 81 | } 82 | 83 | // Update the enclave. 84 | let mut ap = AddPages::new(&*pages, to, &with.0, with.1); 85 | ENCLAVE_ADD_PAGES.ioctl(&mut self.file, &mut ap)?; 86 | 87 | // Update the hasher. 88 | self.hash.load(&*pages, to, with.0, with.1).unwrap(); 89 | 90 | // Save permissions fixups for later. 91 | let mut addr = self.mmap.addr() + to; 92 | self.perm.push((addr as *const (), pages.size(), with.0)); 93 | 94 | // Keep track of TCS pages. 95 | if with.0.class() == Class::Tcs { 96 | for chunk in pages.chunks(Page::SIZE) { 97 | self.tcsp.push(addr as *const super::Tcs); 98 | addr += chunk.len(); 99 | } 100 | } 101 | 102 | Ok(()) 103 | } 104 | } 105 | 106 | impl TryFrom for Arc { 107 | type Error = Error; 108 | 109 | fn try_from(mut builder: Builder) -> Result { 110 | // Create the enclave signature 111 | let hash = builder.hash.finish(); 112 | let author = Author::new(0, 0); 113 | let body = builder.cnfg.parameters.body(hash); 114 | let key = RS256PrivateKey::generate(3)?; 115 | let signature = Signature::new(&key, author, body)?; 116 | 117 | // Initialize the enclave. 118 | let init = Init::new(&signature); 119 | ENCLAVE_INIT.ioctl(&mut builder.file, &init)?; 120 | 121 | // Fix up mapped permissions. 122 | builder.perm.sort_by_key(|x| x.0); 123 | for (addr, size, si) in builder.perm { 124 | let rwx = match si.class() { 125 | Class::Tcs => libc::PROT_READ | libc::PROT_WRITE, 126 | Class::Reg => { 127 | let mut prot = libc::PROT_NONE; 128 | if si.flags().contains(Flags::READ) { 129 | prot |= libc::PROT_READ; 130 | } 131 | if si.flags().contains(Flags::WRITE) { 132 | prot |= libc::PROT_WRITE; 133 | } 134 | if si.flags().contains(Flags::EXECUTE) { 135 | prot |= libc::PROT_EXEC; 136 | } 137 | 138 | prot 139 | } 140 | _ => panic!("Unsupported class!"), 141 | }; 142 | 143 | // Change the permissions on an existing region of memory. 144 | std::mem::forget(unsafe { 145 | Map::map(size) 146 | .onto(addr as usize) 147 | .from(&mut builder.file, 0) 148 | .unknown(Kind::Shared, rwx)? 149 | }); 150 | 151 | //let line = lset::Line::from(span); 152 | //eprintln!("{:016x}-{:016x} {:?}", line.start, line.end, si); 153 | } 154 | 155 | Ok(Arc::new(super::Keep { 156 | _mem: builder.mmap, 157 | tcs: RwLock::new(builder.tcsp), 158 | })) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/backend/sgx/config.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use std::num::NonZeroU32; 4 | 5 | use anyhow::{anyhow, Result}; 6 | use goblin::elf::program_header::{PF_R, PF_W, PF_X}; 7 | use sallyport::elf; 8 | use sgx::page::{Flags, SecInfo}; 9 | use sgx::parameters::{Masked, Parameters}; 10 | 11 | pub struct Config { 12 | pub parameters: Parameters, 13 | pub ssap: NonZeroU32, 14 | pub size: usize, 15 | } 16 | 17 | impl super::super::Config for Config { 18 | type Flags = (SecInfo, bool); 19 | 20 | fn flags(flags: u32) -> Self::Flags { 21 | let mut rwx = Flags::empty(); 22 | if flags & PF_R != 0 { 23 | rwx |= Flags::READ; 24 | } 25 | if flags & PF_W != 0 { 26 | rwx |= Flags::WRITE; 27 | } 28 | if flags & PF_X != 0 { 29 | rwx |= Flags::EXECUTE; 30 | } 31 | 32 | let m = flags & elf::pf::sgx::UNMEASURED == 0; 33 | let si = match flags & elf::pf::sgx::TCS { 34 | 0 => SecInfo::reg(rwx), 35 | _ => SecInfo::tcs(), 36 | }; 37 | 38 | (si, m) 39 | } 40 | 41 | fn new(shim: &super::super::Binary, _exec: &super::super::Binary) -> Result { 42 | unsafe { 43 | let params: Parameters = Parameters { 44 | misc: Masked { 45 | data: shim 46 | .note(elf::note::NAME, elf::note::sgx::MISC) 47 | .ok_or_else(|| anyhow!("SGX shim is missing MISC"))?, 48 | mask: shim 49 | .note(elf::note::NAME, elf::note::sgx::MISCMASK) 50 | .ok_or_else(|| anyhow!("SGX shim is missing MISCMASK"))?, 51 | }, 52 | attr: Masked { 53 | data: shim 54 | .note(elf::note::NAME, elf::note::sgx::ATTR) 55 | .ok_or_else(|| anyhow!("SGX shim is missing ATTR"))?, 56 | mask: shim 57 | .note(elf::note::NAME, elf::note::sgx::ATTRMASK) 58 | .ok_or_else(|| anyhow!("SGX shim is missing ATTRMASK"))?, 59 | }, 60 | pid: shim 61 | .note(elf::note::NAME, elf::note::sgx::PID) 62 | .ok_or_else(|| anyhow!("SGX shim is missing PID"))?, 63 | svn: shim 64 | .note(elf::note::NAME, elf::note::sgx::SVN) 65 | .ok_or_else(|| anyhow!("SGX shim is missing SVN"))?, 66 | }; 67 | 68 | let ssap: u8 = shim 69 | .note(elf::note::NAME, elf::note::sgx::SSAP) 70 | .ok_or_else(|| anyhow!("SGX shim is missing SSAP"))?; 71 | let ssap = 72 | NonZeroU32::new(ssap.into()).ok_or_else(|| anyhow!("SGX shim SSAP is invalid"))?; 73 | 74 | let bits: u8 = shim 75 | .note(elf::note::NAME, elf::note::sgx::BITS) 76 | .ok_or_else(|| anyhow!("SGX shim is missing BITS"))?; 77 | 78 | Ok(Self { 79 | parameters: params, 80 | size: 1 << bits, 81 | ssap, 82 | }) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/backend/sgx/data.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use crate::backend::probe::x86_64::{CpuId, Vendor}; 4 | use crate::backend::Datum; 5 | 6 | use sgx::parameters::{Features, MiscSelect, Xfrm}; 7 | 8 | use std::arch::x86_64::__cpuid_count; 9 | use std::fs::File; 10 | use std::mem::transmute; 11 | use std::str::from_utf8; 12 | 13 | fn humanize(mut size: f64) -> (f64, &'static str) { 14 | let mut iter = 0; 15 | 16 | while size > 512.0 { 17 | size /= 1024.0; 18 | iter += 1; 19 | } 20 | 21 | let suffix = match iter { 22 | 0 => "", 23 | 1 => "KiB", 24 | 2 => "MiB", 25 | 3 => "GiB", 26 | 4 => "TiB", 27 | 5 => "PiB", 28 | 6 => "EiB", 29 | 7 => "ZiB", 30 | 8 => "YiB", 31 | _ => panic!("Size unsupported!"), 32 | }; 33 | 34 | (size, suffix) 35 | } 36 | 37 | pub const CPUIDS: &[CpuId] = &[ 38 | CpuId { 39 | name: "CPU Manufacturer", 40 | leaf: 0x00000000, 41 | subl: 0x00000000, 42 | func: |res| { 43 | let name: [u8; 12] = unsafe { transmute([res.ebx, res.edx, res.ecx]) }; 44 | let name = from_utf8(&name[..]).unwrap(); 45 | (name == "GenuineIntel", Some(name.into())) 46 | }, 47 | vend: None, 48 | }, 49 | CpuId { 50 | name: " SGX Support", 51 | leaf: 0x00000007, 52 | subl: 0x00000000, 53 | func: |res| (res.ebx & (1 << 2) != 0, None), 54 | vend: Some(Vendor::Intel), 55 | }, 56 | CpuId { 57 | name: " Version 1", 58 | leaf: 0x00000012, 59 | subl: 0x00000000, 60 | func: |res| (res.eax & (1 << 0) != 0, None), 61 | vend: Some(Vendor::Intel), 62 | }, 63 | CpuId { 64 | name: " Version 2", 65 | leaf: 0x00000012, 66 | subl: 0x00000000, 67 | func: |res| (res.eax & (1 << 1) != 0, None), 68 | vend: Some(Vendor::Intel), 69 | }, 70 | CpuId { 71 | name: " FLC Support", 72 | leaf: 0x00000007, 73 | subl: 0x00000000, 74 | func: |res| (res.ecx & (1 << 30) != 0, None), 75 | vend: Some(Vendor::Intel), 76 | }, 77 | CpuId { 78 | name: " Max Size (32-bit)", 79 | leaf: 0x00000012, 80 | subl: 0x00000000, 81 | func: |res| { 82 | let bits = res.edx as u8; 83 | let (n, s) = humanize((1u64 << bits) as f64); 84 | (true, Some(format!("{:.0} {}", n, s))) 85 | }, 86 | vend: Some(Vendor::Intel), 87 | }, 88 | CpuId { 89 | name: " Max Size (64-bit)", 90 | leaf: 0x00000012, 91 | subl: 0x00000000, 92 | func: |res| { 93 | let bits = res.edx >> 8 & 0xff; 94 | let (n, s) = humanize((1u64 << bits) as f64); 95 | (true, Some(format!("{:.0} {}", n, s))) 96 | }, 97 | vend: Some(Vendor::Intel), 98 | }, 99 | CpuId { 100 | name: " MiscSelect", 101 | leaf: 0x00000012, 102 | subl: 0x00000000, 103 | func: |res| match MiscSelect::from_bits(res.ebx) { 104 | Some(ms) => (true, Some(format!("{:?}", ms))), 105 | None => (false, None), 106 | }, 107 | vend: Some(Vendor::Intel), 108 | }, 109 | CpuId { 110 | name: " Features", 111 | leaf: 0x00000012, 112 | subl: 0x00000001, 113 | func: |res| match Features::from_bits((res.ebx as u64) << 32 | res.eax as u64) { 114 | Some(features) => (true, Some(format!("{:?}", features))), 115 | None => (false, None), 116 | }, 117 | vend: Some(Vendor::Intel), 118 | }, 119 | CpuId { 120 | name: " Xfrm", 121 | leaf: 0x00000012, 122 | subl: 0x00000001, 123 | func: |res| match Xfrm::from_bits((res.edx as u64) << 32 | res.ecx as u64) { 124 | Some(flags) => (true, Some(format!("{:?}", flags))), 125 | None => (false, None), 126 | }, 127 | vend: Some(Vendor::Intel), 128 | }, 129 | ]; 130 | 131 | pub fn epc_size(max: u32) -> Datum { 132 | let mut pass = false; 133 | let mut info = None; 134 | 135 | if max >= 0x00000012 { 136 | let mut size = 0; 137 | 138 | for i in 2.. { 139 | let result = unsafe { __cpuid_count(0x00000012, i) }; 140 | if result.eax & 0xf != 1 { 141 | break; 142 | } 143 | 144 | let low = result.ecx as u64 & 0xfffff000; 145 | let high = result.edx as u64 & 0x000fffff; 146 | size += high << 12 | low; 147 | } 148 | 149 | let (n, s) = humanize(size as f64); 150 | info = Some(format!("{:.0} {}", n, s)); 151 | pass = true; 152 | } 153 | 154 | Datum { 155 | name: " EPC Size".into(), 156 | mesg: None, 157 | pass, 158 | info, 159 | } 160 | } 161 | 162 | pub fn dev_sgx_enclave() -> Datum { 163 | let mut pass = false; 164 | 165 | if File::open("/dev/sgx_enclave").is_ok() { 166 | pass = true; 167 | } 168 | 169 | Datum { 170 | name: "Driver".into(), 171 | pass, 172 | info: Some("/dev/sgx_enclave".into()), 173 | mesg: None, 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/backend/sgx/hasher.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use std::convert::TryFrom; 4 | 5 | use anyhow::{Error, Result}; 6 | use sgx::page::SecInfo; 7 | 8 | pub struct Hasher(sgx::signature::Hasher); 9 | 10 | impl TryFrom for Hasher { 11 | type Error = Error; 12 | 13 | #[inline] 14 | fn try_from(config: super::config::Config) -> Result { 15 | Ok(Self(sgx::signature::Hasher::new(config.size, config.ssap))) 16 | } 17 | } 18 | 19 | impl super::super::Mapper for Hasher { 20 | type Config = super::config::Config; 21 | type Output = Vec; 22 | 23 | #[inline] 24 | fn map( 25 | &mut self, 26 | pages: mmarinus::Map, 27 | to: usize, 28 | with: (SecInfo, bool), 29 | ) -> anyhow::Result<()> { 30 | self.0.load(&*pages, to, with.0, with.1).unwrap(); 31 | Ok(()) 32 | } 33 | } 34 | 35 | impl TryFrom for Vec { 36 | type Error = Error; 37 | 38 | #[inline] 39 | fn try_from(hasher: Hasher) -> Result { 40 | Ok(hasher.0.finish().into()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/backend/sgx/ioctls.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! This module implements Intel SGX-related IOCTLs using the iocuddle crate. 4 | //! All references to Section or Tables are from 5 | //! https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3d-part-4-manual.pdf 6 | 7 | use std::marker::PhantomData; 8 | 9 | use iocuddle::*; 10 | use sgx::page::{SecInfo, Secs}; 11 | use sgx::signature::Signature; 12 | 13 | const SGX: Group = Group::new(0xA4); 14 | 15 | /// IOCTL identifier for ECREATE (see Section 41-21) 16 | pub const ENCLAVE_CREATE: Ioctl = unsafe { SGX.write(0x00) }; 17 | 18 | /// IOCTL identifier for EADD (see Section 41-11) 19 | pub const ENCLAVE_ADD_PAGES: Ioctl = unsafe { SGX.write_read(0x01) }; 20 | 21 | /// IOCTL identifier for EINIT (see Section 41-35) 22 | pub const ENCLAVE_INIT: Ioctl = unsafe { SGX.write(0x02) }; 23 | 24 | //pub const ENCLAVE_SET_ATTRIBUTE: Ioctl = unsafe { SGX.write(0x03) }; 25 | 26 | #[repr(C)] 27 | #[derive(Debug)] 28 | /// Struct for creating a new enclave from SECS 29 | pub struct Create<'a>(u64, PhantomData<&'a ()>); 30 | 31 | impl<'a> Create<'a> { 32 | /// A new Create struct wraps an SECS struct from the sgx-types crate. 33 | pub fn new(secs: &'a Secs) -> Self { 34 | Create(secs as *const _ as _, PhantomData) 35 | } 36 | } 37 | 38 | #[repr(C)] 39 | #[derive(Debug)] 40 | /// Struct for adding pages to an enclave 41 | pub struct AddPages<'a> { 42 | src: u64, 43 | offset: u64, 44 | length: u64, 45 | secinfo: u64, 46 | flags: u64, 47 | count: u64, 48 | phantom: PhantomData<&'a ()>, 49 | } 50 | 51 | impl<'a> AddPages<'a> { 52 | /// Creates a new AddPages struct for a page at a certain offset 53 | pub fn new(bytes: &'a [u8], offset: usize, secinfo: &'a SecInfo, measure: bool) -> Self { 54 | const MEASURE: u64 = 1 << 0; 55 | 56 | let flags = match measure { 57 | true => MEASURE, 58 | false => 0, 59 | }; 60 | 61 | Self { 62 | src: bytes.as_ptr() as _, 63 | offset: offset as _, 64 | length: bytes.len() as _, 65 | secinfo: secinfo as *const _ as _, 66 | flags, 67 | count: 0, 68 | phantom: PhantomData, 69 | } 70 | } 71 | 72 | #[allow(dead_code)] 73 | /// WIP 74 | pub fn count(&self) -> u64 { 75 | self.count 76 | } 77 | } 78 | 79 | #[repr(C)] 80 | #[derive(Debug)] 81 | /// Struct for initializing an enclave 82 | pub struct Init<'a>(u64, PhantomData<&'a ()>); 83 | 84 | impl<'a> Init<'a> { 85 | /// A new Init struct must wrap a Signature from the sgx-types crate. 86 | pub fn new(sig: &'a Signature) -> Self { 87 | Init(sig as *const _ as _, PhantomData) 88 | } 89 | } 90 | 91 | #[repr(C)] 92 | #[derive(Debug)] 93 | #[allow(dead_code)] 94 | /// Struct for setting enclave attributes - WIP - ERESUME? EREMOVE? 95 | pub struct SetAttribute<'a>(u64, PhantomData<&'a ()>); 96 | 97 | impl<'a> SetAttribute<'a> { 98 | #[allow(dead_code)] 99 | /// A new SetAttribute struct must wrap a file descriptor. 100 | pub fn new(fd: &'a impl std::os::unix::io::AsRawFd) -> Self { 101 | SetAttribute(fd.as_raw_fd() as _, PhantomData) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/backend/sgx/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | mod builder; 4 | mod config; 5 | mod data; 6 | mod hasher; 7 | mod ioctls; 8 | mod thread; 9 | 10 | use super::Loader; 11 | 12 | use anyhow::Result; 13 | use mmarinus::{perms, Map}; 14 | 15 | use std::arch::x86_64::__cpuid_count; 16 | use std::sync::{Arc, RwLock}; 17 | 18 | struct Tcs; 19 | 20 | struct Keep { 21 | _mem: Map, 22 | tcs: RwLock>, 23 | } 24 | 25 | pub struct Backend; 26 | 27 | impl crate::backend::Backend for Backend { 28 | #[inline] 29 | fn name(&self) -> &'static str { 30 | "sgx" 31 | } 32 | 33 | #[inline] 34 | fn shim(&self) -> &'static [u8] { 35 | include_bytes!(concat!(env!("OUT_DIR"), "/bin/shim-sgx")) 36 | } 37 | 38 | #[inline] 39 | fn have(&self) -> bool { 40 | data::dev_sgx_enclave().pass 41 | } 42 | 43 | fn data(&self) -> Vec { 44 | let mut data = vec![data::dev_sgx_enclave()]; 45 | 46 | data.extend(data::CPUIDS.iter().map(|c| c.into())); 47 | 48 | let max = unsafe { __cpuid_count(0x00000000, 0x00000000) }.eax; 49 | data.push(data::epc_size(max)); 50 | 51 | data 52 | } 53 | 54 | #[inline] 55 | fn keep(&self, shim: &[u8], exec: &[u8]) -> Result> { 56 | builder::Builder::load(shim, exec) 57 | } 58 | 59 | #[inline] 60 | fn hash(&self, shim: &[u8], exec: &[u8]) -> Result> { 61 | hasher::Hasher::load(shim, exec) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/backend/sgx/thread.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::super::Command; 4 | 5 | use std::mem::MaybeUninit; 6 | use std::sync::Arc; 7 | 8 | use anyhow::Result; 9 | use sallyport::{syscall::SYS_ENARX_CPUID, Block}; 10 | use sgx::enclu::{EENTER, EEXIT, ERESUME}; 11 | use sgx::ssa::Vector; 12 | use vdso::Symbol; 13 | 14 | pub struct Thread { 15 | enclave: Arc, 16 | vdso: &'static Symbol, 17 | tcs: *const super::Tcs, 18 | block: Block, 19 | cssa: usize, 20 | how: usize, 21 | } 22 | 23 | impl Drop for Thread { 24 | fn drop(&mut self) { 25 | self.enclave.tcs.write().unwrap().push(self.tcs) 26 | } 27 | } 28 | 29 | impl super::super::Keep for super::Keep { 30 | fn spawn(self: Arc) -> Result>> { 31 | let vdso = vdso::Vdso::locate() 32 | .expect("vDSO not found") 33 | .lookup("__vdso_sgx_enter_enclave") 34 | .expect("__vdso_sgx_enter_enclave not found"); 35 | 36 | let tcs = match self.tcs.write().unwrap().pop() { 37 | Some(tcs) => tcs, 38 | None => return Ok(None), 39 | }; 40 | 41 | Ok(Some(Box::new(Thread { 42 | enclave: self, 43 | vdso, 44 | tcs, 45 | block: Block::default(), 46 | cssa: usize::default(), 47 | how: EENTER, 48 | }))) 49 | } 50 | } 51 | 52 | impl super::super::Thread for Thread { 53 | fn enter(&mut self) -> Result { 54 | let mut run: Run = unsafe { MaybeUninit::zeroed().assume_init() }; 55 | run.tcs = self.tcs as u64; 56 | let how = self.how; 57 | 58 | // The `enclu` instruction consumes `rax`, `rbx` and `rcx`. However, 59 | // the vDSO function preserves `rbx` AND sets `rax` as the return 60 | // value. All other registers are passed to and from the enclave 61 | // unmodified. 62 | unsafe { 63 | asm!( 64 | "push rbx", // save rbx 65 | "push rbp", // save rbp 66 | "mov rbp, rsp", // save rsp 67 | "and rsp, ~0xf", // align to 16+0 68 | 69 | "push 0", // align to 16+8 70 | "push r10", // push run address 71 | "call r11", // call vDSO function 72 | 73 | "mov rsp, rbp", // restore rsp 74 | "pop rbp", // restore rbp 75 | "pop rbx", // restore rbx 76 | 77 | inout("rdi") &self.block => _, 78 | lateout("rsi") _, 79 | lateout("rdx") _, 80 | inout("rcx") how => _, 81 | lateout("r8") _, 82 | lateout("r9") _, 83 | inout("r10") &mut run => _, 84 | inout("r11") self.vdso => _, 85 | lateout("r12") _, 86 | lateout("r13") _, 87 | lateout("r14") _, 88 | lateout("r15") _, 89 | lateout("rax") _, 90 | ); 91 | } 92 | 93 | self.how = match run.function as usize { 94 | EENTER | ERESUME if run.vector == Vector::InvalidOpcode => EENTER, 95 | EEXIT => ERESUME, 96 | _ => panic!("Unexpected AEX: {:?}", run.vector), 97 | }; 98 | 99 | // Keep track of the CSSA 100 | match self.how { 101 | EENTER => self.cssa += 1, 102 | ERESUME => match self.cssa { 103 | 0 => unreachable!(), 104 | _ => self.cssa -= 1, 105 | }, 106 | _ => unreachable!(), 107 | } 108 | 109 | // If we have handled an InvalidOpcode error, evaluate the sallyport. 110 | if let (EENTER, ERESUME) = (how, self.how) { 111 | match unsafe { self.block.msg.req }.num.into() { 112 | SYS_ENARX_CPUID => return Ok(Command::CpuId(&mut self.block)), 113 | _ => return Ok(Command::SysCall(&mut self.block)), 114 | } 115 | } 116 | 117 | Ok(Command::Continue) 118 | } 119 | } 120 | 121 | // This structure is defined by the Linux kernel. 122 | // 123 | // See: https://github.com/torvalds/linux/blob/84292fffc2468125632a21c09533a89426ea212e/arch/x86/include/uapi/asm/sgx.h#L112 124 | #[repr(C)] 125 | #[derive(Debug)] 126 | struct Run { 127 | tcs: u64, 128 | function: u32, 129 | vector: Vector, 130 | padding: u8, 131 | exception_error_code: u16, 132 | exception_addr: u64, 133 | user_handler: u64, 134 | user_data: u64, 135 | reserved: [u64; 27], 136 | } 137 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! This crate provides the `enarx-keepldr` executable which loads `static-pie` 4 | //! binaries into an Enarx Keep - that is a hardware isolated environment using 5 | //! technologies such as Intel SGX or AMD SEV. 6 | //! 7 | //! # Building 8 | //! 9 | //! Please see **BUILD.md** for instructions. 10 | //! 11 | //! # Run Tests 12 | //! 13 | //! $ cargo test 14 | //! 15 | //! # Build and Run an Application 16 | //! 17 | //! $ cat > test.c < 19 | //! 20 | //! int main() { 21 | //! printf("Hello World!\n"); 22 | //! return 0; 23 | //! } 24 | //! EOF 25 | //! 26 | //! $ musl-gcc -static-pie -fPIC -o test test.c 27 | //! $ target/debug/enarx-keepldr exec ./test 28 | //! Hello World! 29 | //! 30 | //! # Select a Different Backend 31 | //! 32 | //! `enarx-keepldr exec` will probe the machine it is running on 33 | //! in an attempt to deduce an appropriate deployment backend unless 34 | //! that target is already specified in an environment variable 35 | //! called `ENARX_BACKEND`. 36 | //! 37 | //! To see what backends are supported on your system, run: 38 | //! 39 | //! $ target/debug/enarx-keepldr info 40 | //! 41 | //! To manually select a backend, set the `ENARX_BACKEND` environment 42 | //! variable: 43 | //! 44 | //! $ ENARX_BACKEND=sgx target/debug/enarx-keepldr exec ./test 45 | //! 46 | //! Note that some backends are conditionally compiled. They can all 47 | //! be compiled in like so: 48 | //! 49 | //! $ cargo build --all-features 50 | //! 51 | //! Or specific backends can be compiled in: 52 | //! 53 | //! $ cargo build --features=backend-sgx,backend-kvm 54 | 55 | #![deny(clippy::all)] 56 | #![deny(missing_docs)] 57 | #![feature(asm)] 58 | 59 | mod backend; 60 | mod protobuf; 61 | 62 | use backend::{Backend, Command}; 63 | 64 | use std::convert::TryInto; 65 | use std::path::PathBuf; 66 | 67 | use anyhow::Result; 68 | use structopt::StructOpt; 69 | 70 | const VERSION: &str = env!("CARGO_PKG_VERSION"); 71 | const AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); 72 | 73 | /// Prints information about your current platform 74 | #[derive(StructOpt)] 75 | struct Info {} 76 | 77 | /// Executes a keep 78 | #[derive(StructOpt)] 79 | struct Exec { 80 | /// The payload to run inside the keep 81 | code: PathBuf, 82 | } 83 | 84 | #[derive(StructOpt)] 85 | #[structopt(version=VERSION, author=AUTHORS.split(";").nth(0).unwrap())] 86 | enum Options { 87 | Info(Info), 88 | Exec(Exec), 89 | } 90 | 91 | #[allow(clippy::unnecessary_wraps)] 92 | fn main() -> Result<()> { 93 | let backends: &[Box] = &[ 94 | #[cfg(feature = "backend-sgx")] 95 | Box::new(backend::sgx::Backend), 96 | #[cfg(feature = "backend-kvm")] 97 | Box::new(backend::kvm::Backend), 98 | ]; 99 | 100 | match Options::from_args() { 101 | Options::Info(_) => info(backends), 102 | Options::Exec(e) => exec(backends, e), 103 | } 104 | } 105 | 106 | #[allow(clippy::unnecessary_wraps)] 107 | fn info(backends: &[Box]) -> Result<()> { 108 | use colorful::*; 109 | 110 | for backend in backends { 111 | println!("Backend: {}", backend.name()); 112 | 113 | let data = backend.data(); 114 | 115 | for datum in &data { 116 | let icon = match datum.pass { 117 | true => "✔".green(), 118 | false => "✗".red(), 119 | }; 120 | 121 | if let Some(info) = datum.info.as_ref() { 122 | println!(" {} {}: {}", icon, datum.name, info); 123 | } else { 124 | println!(" {} {}", icon, datum.name); 125 | } 126 | } 127 | 128 | for datum in &data { 129 | if let Some(mesg) = datum.mesg.as_ref() { 130 | println!("\n{}\n", mesg); 131 | } 132 | } 133 | } 134 | 135 | Ok(()) 136 | } 137 | 138 | #[inline] 139 | fn backend(backends: &[Box]) -> &dyn Backend { 140 | let keep = std::env::var_os("ENARX_BACKEND").map(|x| x.into_string().unwrap()); 141 | 142 | let backend = backends 143 | .iter() 144 | .filter(|b| keep.is_none() || keep == Some(b.name().into())) 145 | .find(|b| b.have()); 146 | 147 | match (keep, backend) { 148 | (Some(name), None) => panic!("Keep backend '{:?}' is unsupported.", name), 149 | (None, None) => panic!("No supported backend found!"), 150 | (_, Some(backend)) => &**backend, 151 | } 152 | } 153 | 154 | fn exec(backends: &[Box], opts: Exec) -> Result<()> { 155 | let backend = backend(backends); 156 | 157 | let map = mmarinus::Kind::Private.load::(&opts.code)?; 158 | 159 | let keep = backend.keep(backend.shim(), &map)?; 160 | let mut thread = keep.clone().spawn()?.unwrap(); 161 | loop { 162 | match thread.enter()? { 163 | Command::SysCall(block) => unsafe { 164 | block.msg.rep = block.msg.req.syscall(); 165 | }, 166 | 167 | Command::CpuId(block) => unsafe { 168 | let cpuid = core::arch::x86_64::__cpuid_count( 169 | block.msg.req.arg[0].try_into().unwrap(), 170 | block.msg.req.arg[1].try_into().unwrap(), 171 | ); 172 | 173 | block.msg.req.arg[0] = cpuid.eax.into(); 174 | block.msg.req.arg[1] = cpuid.ebx.into(); 175 | block.msg.req.arg[2] = cpuid.ecx.into(); 176 | block.msg.req.arg[3] = cpuid.edx.into(); 177 | }, 178 | 179 | Command::Continue => (), 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/protobuf/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Provides all generated protobuf modules 4 | 5 | include!(concat!(env!("OUT_DIR"), "/protos/mod.rs")); 6 | -------------------------------------------------------------------------------- /tests/bin/bind.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define UNIX_ABSTRACT_PATH "@enarx_bind_test" 11 | 12 | int main(void) { 13 | struct sockaddr_un sa = { 14 | .sun_family = AF_UNIX, 15 | }; 16 | socklen_t sa_len = strlen(UNIX_ABSTRACT_PATH); 17 | 18 | int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 19 | 20 | if (fd < 0) 21 | return 1; 22 | 23 | strcpy(sa.sun_path, UNIX_ABSTRACT_PATH); 24 | sa.sun_path[0] = '\0'; 25 | 26 | if (bind(fd, (struct sockaddr *)&sa, offsetof(struct sockaddr_un, sun_path) + sa_len) < 0) { 27 | return errno; 28 | } 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /tests/bin/clock_gettime.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | struct timespec t; 7 | 8 | ssize_t rax = clock_gettime(CLOCK_MONOTONIC, &t); 9 | if (rax == 0) { 10 | rax = write(STDOUT_FILENO, &t, sizeof(t)); 11 | } 12 | 13 | return rax != sizeof(t); 14 | } 15 | -------------------------------------------------------------------------------- /tests/bin/close.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | ssize_t ret = close(STDIN_FILENO); 7 | return ret != 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/bin/echo.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use std::io::{self, Read, Write}; 4 | 5 | fn main() -> io::Result<()> { 6 | let mut buffer = Vec::new(); 7 | std::io::stdin().read_to_end(&mut buffer)?; 8 | std::io::stdout().write_all(&buffer)?; 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /tests/bin/enarx.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #ifndef ENARX_H_ 4 | #define ENARX_H_ 5 | 6 | enum tee_tech { 7 | TEE_NONE, 8 | TEE_SEV, 9 | TEE_SGX, 10 | }; 11 | 12 | #endif 13 | 14 | -------------------------------------------------------------------------------- /tests/bin/exit_one.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | return 1; 7 | } 8 | -------------------------------------------------------------------------------- /tests/bin/exit_zero.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/bin/get_att.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | #include "enarx.h" 5 | #include 6 | 7 | int main(void) { 8 | unsigned char nonce[64]; /* correct hash length for SGX */ 9 | unsigned char buf[4598]; 10 | size_t technology; 11 | 12 | ssize_t size = get_att(nonce, sizeof(nonce), buf, sizeof(buf), &technology); 13 | 14 | if (size >= 0) { 15 | switch (technology) { 16 | case TEE_NONE: 17 | case TEE_SEV: 18 | case TEE_SGX: 19 | return 0; 20 | default: return 1; 21 | } 22 | } 23 | 24 | else if (size == -1) 25 | return !(errno == ENOSYS); 26 | 27 | else 28 | return 1; 29 | } 30 | -------------------------------------------------------------------------------- /tests/bin/getegid.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | if (!is_enarx()) { 7 | return 0; 8 | } 9 | return getegid() != 1000; 10 | } 11 | -------------------------------------------------------------------------------- /tests/bin/geteuid.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | if (!is_enarx()) { 7 | return 0; 8 | } 9 | return geteuid() != 1000; 10 | } 11 | -------------------------------------------------------------------------------- /tests/bin/getgid.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | if (!is_enarx()) { 7 | return 0; 8 | } 9 | return getgid() != 1000; 10 | } 11 | -------------------------------------------------------------------------------- /tests/bin/getuid.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | if (!is_enarx()) { 7 | return 0; 8 | } 9 | return getuid() != 1000; 10 | } 11 | -------------------------------------------------------------------------------- /tests/bin/libc.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include 4 | #include /* struct iovec */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int *__errno_location(void) { 13 | static int errnum = 0; 14 | return &errnum; 15 | } 16 | 17 | void _exit(int status) { 18 | asm( 19 | "syscall; ud2" 20 | : 21 | : "a" (SYS_exit), "D" (status) 22 | : "%rcx", "%r11" 23 | ); 24 | 25 | while (1) {} 26 | } 27 | 28 | int main(void); 29 | void _start(void) { 30 | _exit(main()); 31 | } 32 | 33 | ssize_t read(int fd, void *buf, size_t count) { 34 | ssize_t rax; 35 | 36 | asm( 37 | "syscall" 38 | : "=a" (rax) 39 | : "a" (SYS_read), "D" (fd), "S" (buf), "d" (count) 40 | : "%rcx", "%r11" 41 | ); 42 | 43 | if (rax < 0) { 44 | errno = -rax; 45 | return -1; 46 | } 47 | 48 | return rax; 49 | } 50 | 51 | ssize_t readv(int fd, const struct iovec *iov, int iovcnt) { 52 | ssize_t rax; 53 | 54 | asm( 55 | "syscall" 56 | : "=a" (rax) 57 | : "a" (SYS_readv), "D" (fd), "S" (iov), "d" (iovcnt) 58 | : "%rcx", "%r11" 59 | ); 60 | 61 | if (rax < 0) { 62 | errno = -rax; 63 | return -1; 64 | } 65 | 66 | return rax; 67 | } 68 | 69 | ssize_t write(int fd, const void *buf, size_t count) { 70 | ssize_t rax; 71 | 72 | asm( 73 | "syscall" 74 | : "=a" (rax) 75 | : "a" (SYS_write), "D" (fd), "S" (buf), "d" (count) 76 | : "%rcx", "%r11" 77 | ); 78 | 79 | if (rax < 0) { 80 | errno = -rax; 81 | return -1; 82 | } 83 | 84 | return rax; 85 | } 86 | 87 | int clock_gettime(clockid_t clk_id, struct timespec *tp) { 88 | int rax; 89 | 90 | asm( 91 | "syscall" 92 | : "=a" (rax) 93 | : "a" (SYS_clock_gettime), "D" (clk_id), "S" (tp) 94 | : "%rcx", "%r11" 95 | ); 96 | 97 | if (rax < 0) { 98 | errno = -rax; 99 | return -1; 100 | } 101 | 102 | return rax; 103 | } 104 | 105 | int is_enarx() { 106 | ssize_t rax; 107 | 108 | asm( 109 | "syscall" 110 | : "=a" (rax) 111 | : "a" (SYS_fork) 112 | : "%rcx", "%r11" 113 | ); 114 | 115 | switch (rax) { 116 | case 0: _exit(0); 117 | case -ENOSYS: return 1; 118 | default: return 0; 119 | } 120 | } 121 | 122 | ssize_t get_att(void *nonce, size_t nonce_len, void *buf, size_t buf_len, size_t *technology) { 123 | ssize_t rax; 124 | ssize_t tech; 125 | register size_t r10 __asm__("r10") = buf_len; 126 | 127 | asm( 128 | "syscall" 129 | : "=a" (rax), "=d" (tech) 130 | : "a" (0xEA01), "D" (nonce), "S" (nonce_len), "d" (buf), "r" (r10) 131 | : "%rcx", "%r11" 132 | ); 133 | 134 | if (rax < 0) { 135 | errno = -rax; 136 | return -1; 137 | } 138 | 139 | *technology = tech; 140 | return rax; 141 | } 142 | 143 | uid_t getuid() { 144 | uid_t rax; 145 | asm( 146 | "syscall" 147 | : "=a" (rax) 148 | : "a" (SYS_getuid) 149 | : "%rcx", "%r11" 150 | ); 151 | return rax; 152 | } 153 | 154 | 155 | uid_t geteuid() { 156 | uid_t rax; 157 | asm( 158 | "syscall" 159 | : "=a" (rax) 160 | : "a" (SYS_geteuid) 161 | : "%rcx", "%r11" 162 | ); 163 | return rax; 164 | } 165 | 166 | gid_t getgid() { 167 | gid_t rax; 168 | asm( 169 | "syscall" 170 | : "=a" (rax) 171 | : "a" (SYS_getgid) 172 | : "%rcx", "%r11" 173 | ); 174 | return rax; 175 | } 176 | 177 | 178 | gid_t getegid() { 179 | gid_t rax; 180 | asm( 181 | "syscall" 182 | : "=a" (rax) 183 | : "a" (SYS_getegid) 184 | : "%rcx", "%r11" 185 | ); 186 | return rax; 187 | } 188 | 189 | int close(int fd) { 190 | ssize_t rax; 191 | 192 | asm( 193 | "syscall" 194 | : "=a" (rax) 195 | : "a" (SYS_close), "D" (fd) 196 | : "%rcx", "%r11" 197 | ); 198 | 199 | if (rax < 0) { 200 | errno = -rax; 201 | return -1; 202 | } 203 | 204 | return rax; 205 | } 206 | 207 | int uname(struct utsname *buf) { 208 | ssize_t rax; 209 | 210 | asm( 211 | "syscall" 212 | : "=a" (rax) 213 | : "a" (SYS_uname), "D" (buf) 214 | : "%rcx", "%r11" 215 | ); 216 | 217 | if (rax < 0) { 218 | errno = -rax; 219 | return -1; 220 | } 221 | 222 | return rax; 223 | } 224 | 225 | int socket(int domain, int type, int protocol) { 226 | int rax; 227 | 228 | asm( 229 | "syscall" 230 | : "=a" (rax) 231 | : "a" (SYS_socket), "D" (domain), "S" (type), "d" (protocol) 232 | : "%rcx", "%r11" 233 | ); 234 | 235 | if (rax < 0) { 236 | errno = -rax; 237 | return -1; 238 | } 239 | 240 | return rax; 241 | } 242 | 243 | int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { 244 | int rax; 245 | 246 | asm( 247 | "syscall" 248 | : "=a" (rax) 249 | : "a" (SYS_bind), "D" (sockfd), "S" (addr), "d" (addrlen) 250 | : "%rcx", "%r11" 251 | ); 252 | 253 | if (rax < 0) { 254 | errno = -rax; 255 | return -1; 256 | } 257 | 258 | return rax; 259 | } 260 | 261 | int listen(int sockfd, int backlog) { 262 | int rax; 263 | 264 | asm( 265 | "syscall" 266 | : "=a" (rax) 267 | : "a" (SYS_listen), "D" (sockfd), "S" (backlog) 268 | : "%rcx", "%r11" 269 | ); 270 | 271 | if (rax < 0) { 272 | errno = -rax; 273 | return -1; 274 | } 275 | 276 | return rax; 277 | } 278 | 279 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { 280 | int rax; 281 | 282 | asm( 283 | "syscall" 284 | : "=a" (rax) 285 | : "a" (SYS_accept), "D" (sockfd), "S" (addr), "d" (addrlen) 286 | : "%rcx", "%r11" 287 | ); 288 | 289 | if (rax < 0) { 290 | errno = -rax; 291 | return -1; 292 | } 293 | 294 | return rax; 295 | } 296 | 297 | int accept4(int sockfd, const struct sockaddr *addr, socklen_t *addrlen, int flags) { 298 | int rax; 299 | register int r10 __asm__("r10") = flags; 300 | 301 | asm( 302 | "syscall" 303 | : "=a" (rax) 304 | : "a" (SYS_accept4), "D" (sockfd), "S" (addr), "d" (addrlen), "r" (r10) 305 | : "%rcx", "%r11" 306 | ); 307 | 308 | if (rax < 0) { 309 | errno = -rax; 310 | return -1; 311 | } 312 | 313 | return rax; 314 | } 315 | 316 | int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { 317 | int rax; 318 | 319 | asm( 320 | "syscall" 321 | : "=a" (rax) 322 | : "a" (SYS_connect), "D" (sockfd), "S" (addr), "d" (addrlen) 323 | : "%rcx", "%r11" 324 | ); 325 | 326 | if (rax < 0) { 327 | errno = -rax; 328 | return -1; 329 | } 330 | 331 | return rax; 332 | } 333 | 334 | ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 335 | struct sockaddr *src_addr, socklen_t *addrlen) { 336 | int rax; 337 | register int r10 __asm__("r10") = flags; 338 | register struct sockaddr *r8 __asm__("r8") = src_addr; 339 | register socklen_t *r9 __asm__("r9") = addrlen; 340 | 341 | asm( 342 | "syscall" 343 | : "=a" (rax) 344 | : "a" (SYS_recvfrom), "D" (sockfd), "S" (buf), "d" (len), "r" (r10), "r" (r8), "r" (r9) 345 | : "%rcx", "%r11" 346 | ); 347 | 348 | if (rax < 0) { 349 | errno = -rax; 350 | return -1; 351 | } 352 | 353 | return rax; 354 | } 355 | -------------------------------------------------------------------------------- /tests/bin/listen.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define UNIX_ABSTRACT_PATH "@enarx_listen_test" 11 | 12 | int main(void) { 13 | struct sockaddr_un sa = { 14 | .sun_family = AF_UNIX, 15 | }; 16 | socklen_t sa_len = strlen(UNIX_ABSTRACT_PATH); 17 | 18 | int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 19 | 20 | if (fd < 0) 21 | return 1; 22 | 23 | strcpy(sa.sun_path, UNIX_ABSTRACT_PATH); 24 | sa.sun_path[0] = '\0'; 25 | 26 | if (bind(fd, (struct sockaddr *)&sa, offsetof(struct sockaddr_un, sun_path) + sa_len) < 0) { 27 | return 2; 28 | } 29 | 30 | if (listen(fd, 0)) 31 | return errno; 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /tests/bin/memory_stress_test.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | const SIZE_32M: usize = 1024 * 1024 * 32; 4 | 5 | fn main() { 6 | let mut ret = 0; 7 | let mut size: usize = 1; 8 | while size < SIZE_32M { 9 | let mut vec = Vec::with_capacity(size); 10 | vec.push(0u8); 11 | ret += vec.pop().unwrap(); 12 | size *= 2; 13 | drop(vec); 14 | } 15 | 16 | for _i in 0..100 { 17 | let mut vec = Vec::with_capacity(size); 18 | vec.push(0u8); 19 | ret += vec.pop().unwrap(); 20 | drop(vec); 21 | } 22 | 23 | while size > 0 { 24 | let mut vec = Vec::with_capacity(size); 25 | vec.push(0u8); 26 | ret += vec.pop().unwrap(); 27 | size /= 2; 28 | drop(vec); 29 | } 30 | 31 | std::process::exit(ret as _); 32 | } 33 | -------------------------------------------------------------------------------- /tests/bin/memspike.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! `memspike`'s primary intention is to trigger memory ballooning in 4 | //! VM-based keeps. This will help test the ballooning itself as well 5 | //! as memory pinning for SEV. 6 | 7 | fn main() { 8 | let _alloc: Vec = Vec::with_capacity(40_000_000); 9 | } 10 | -------------------------------------------------------------------------------- /tests/bin/read.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int min(int a, int b) { 6 | return a < b ? a : b; 7 | } 8 | 9 | int main(void) { 10 | char buf[16] = {}; 11 | 12 | for (size_t in = 1;; in = min (in * 2 , sizeof(buf))) { 13 | ssize_t out = read(STDIN_FILENO, buf, in); 14 | if (out <= 0) 15 | break; 16 | 17 | write(STDOUT_FILENO, buf, out); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/bin/read_udp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Read and write a buffer of the size of a maximum sized UDP packet 4 | // in one go and fail, if it was fragmented. 5 | 6 | #include "libc.h" 7 | 8 | int main(void) { 9 | char buf[65507]; 10 | 11 | ssize_t out = read(STDIN_FILENO, buf, sizeof(buf)); 12 | 13 | if (out != sizeof(buf)) 14 | return -1; 15 | 16 | write(STDOUT_FILENO, buf, out); 17 | } 18 | -------------------------------------------------------------------------------- /tests/bin/readv.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | /* sizeof("hello, world") = 12 (note: no NUL byte) */ 6 | #define BUF (12) 7 | 8 | int main(void) { 9 | 10 | /* input = "hello, worldhello, worldhello, world" 11 | * so we'll gather each greeting into its own array */ 12 | char a[BUF] = {}; 13 | char b[BUF] = {}; 14 | char c[BUF] = {}; 15 | 16 | struct iovec iov[] = { 17 | { 18 | .iov_base = a, 19 | .iov_len = BUF, 20 | }, 21 | { 22 | .iov_base = b, 23 | .iov_len = BUF, 24 | }, 25 | { 26 | .iov_base = c, 27 | .iov_len = BUF, 28 | }, 29 | }; 30 | int niov = (sizeof(iov)/sizeof(iov[0])); 31 | 32 | readv(STDIN_FILENO, iov, niov); 33 | write(STDOUT_FILENO, a, BUF); 34 | write(STDOUT_FILENO, b, BUF); 35 | write(STDOUT_FILENO, c, BUF); 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /tests/bin/sev_get_att_quote.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | #include "enarx.h" 5 | #include 6 | 7 | /* This test will be run only for SEV */ 8 | 9 | int main(void) { 10 | int* nonce = NULL; 11 | // TODO Update this buffer size to match real Quote. 12 | unsigned char buf[16*1024]; 13 | size_t technology; 14 | 15 | ssize_t size = get_att(nonce, sizeof(nonce), buf, sizeof(buf), &technology); 16 | 17 | if (technology != TEE_SEV) 18 | return 0; 19 | 20 | if (size < 0) 21 | return 1; 22 | 23 | write(STDOUT_FILENO, buf, size); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /tests/bin/sgx_get_att_quote.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | #include "enarx.h" 5 | #include 6 | 7 | /* This test will be run only for SGX. It is designed to request a 8 | * Quote from get_attestation() and check that the first bytes of 9 | * the returned Quote in buf match expected values. */ 10 | 11 | int main(void) { 12 | int* nonce[64]; /* empty pseudo-hash value to embed in SGX Quote */ 13 | unsigned char buf[4598]; 14 | size_t technology; 15 | int i; 16 | unsigned char expected[32] = { 17 | 3, 0, 2, 0, 0, 0, 0, 0, 5, 0, 18 | 10, 0, 147, 154, 114, 51, 247, 156, 76, 169, 19 | 148, 10, 13, 179, 149, 127, 6, 7, 14, 153, 20 | 112, 145 21 | }; 22 | unsigned char dummy[32] = { 23 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 24 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 25 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 26 | 44, 44 27 | }; 28 | 29 | 30 | ssize_t size = get_att(nonce, sizeof(nonce), buf, sizeof(buf), &technology); 31 | 32 | /* this test is SGX-specific, so just return success if not running on SGX */ 33 | if (technology != TEE_SGX) 34 | return 0; 35 | 36 | if (size < 0) 37 | return 1; 38 | 39 | /* check beginning of quote matches expected value */ 40 | for (i = 0; i < 32; i++) 41 | { 42 | if (buf[i] != expected[i] && buf[i]!= dummy[i]) 43 | { 44 | return 1; 45 | } 46 | } 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/bin/sgx_get_att_quote_size.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | #include "enarx.h" 5 | #include 6 | 7 | /* This test will be run only for SGX. It is designed to request a 8 | * Quote size from get_attestation() and check that against the expected 9 | * Quote size. */ 10 | 11 | int main(void) { 12 | int* nonce = NULL; 13 | int* buf = NULL; 14 | size_t technology; 15 | ssize_t expected = 4598; 16 | 17 | ssize_t size = get_att(nonce, sizeof(nonce), buf, sizeof(buf), &technology); 18 | 19 | /* this test is SGX-specific, so just return success if not running on SGX */ 20 | if (technology != TEE_SGX) 21 | return 0; 22 | 23 | return (size != expected); 24 | } 25 | -------------------------------------------------------------------------------- /tests/bin/socket.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | #include 5 | 6 | int main(void) { 7 | if (socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0) < 0) 8 | return 1; 9 | 10 | if (socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0) < 0) 11 | return 2; 12 | 13 | if (socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0) < 0) 14 | return 3; 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /tests/bin/uname.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int strcmp(const char* s1, const char* s2) 6 | { 7 | while(*s1 && (*s1==*s2)) 8 | s1++,s2++; 9 | return *(const unsigned char*)s1-*(const unsigned char*)s2; 10 | } 11 | 12 | int main(void) { 13 | 14 | int v; 15 | char *l = "Linux"; 16 | struct utsname buffer; 17 | 18 | errno = 0; 19 | if (uname(&buffer) != 0) { 20 | return 1; 21 | } 22 | 23 | v = strcmp(buffer.sysname, l); 24 | if (v != 0) { 25 | return 1; 26 | } 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /tests/bin/unix_echo.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use std::io::{self, stdin, Read, Write}; 4 | use std::os::unix::net::{UnixListener, UnixStream}; 5 | use std::path::PathBuf; 6 | 7 | fn main() -> io::Result<()> { 8 | let mut dir_name = String::new(); 9 | 10 | stdin().read_line(&mut dir_name)?; 11 | 12 | let dir_name = PathBuf::from(dir_name); 13 | 14 | let listener = UnixListener::bind(dir_name.join("enarx_unix_echo_to_bin"))?; 15 | let (mut socket, _) = listener.accept()?; 16 | 17 | let mut buffer = Vec::new(); 18 | socket.read_to_end(&mut buffer)?; 19 | 20 | let mut socket = UnixStream::connect(dir_name.join("enarx_unix_echo_from_bin")).unwrap(); 21 | socket.write_all(&buffer)?; 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /tests/bin/write_emsgsize.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | char msg[128 * 1024]; 7 | 8 | if (!is_enarx()) { 9 | return 0; 10 | } 11 | 12 | return write(STDOUT_FILENO, msg, sizeof(msg)) != -EMSGSIZE; 13 | } 14 | -------------------------------------------------------------------------------- /tests/bin/write_stderr.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | const char msg[] = "hi\n"; 7 | const int len = sizeof(msg) - 1; 8 | return write(STDERR_FILENO, msg, len) != len; 9 | } 10 | -------------------------------------------------------------------------------- /tests/bin/write_stdout.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "libc.h" 4 | 5 | int main(void) { 6 | const char msg[] = "hi\n"; 7 | const int len = sizeof(msg) - 1; 8 | return write(STDOUT_FILENO, msg, len) != len; 9 | } 10 | --------------------------------------------------------------------------------