From ba6d8683479b034854c85dcd9b99002d0468c238 Mon Sep 17 00:00:00 2001 From: Longlong Yang Date: Sun, 15 May 2022 22:36:20 +0800 Subject: [PATCH] Reproducible build: introduce a tool to help reproducible build 1. Introduce a reproducible tool, td-reproducible-tool, to help reproducible 2. Add strip = "symbols" to cargo.toml in root workspace to remove symbols Signed-off-by: Longlong Yang --- Cargo.toml | 6 +- sh_script/build_final.sh | 26 +++- td-reproducible-tool/Cargo.toml | 18 +++ td-reproducible-tool/readme.md | 61 ++++++++++ td-reproducible-tool/src/main.rs | 200 +++++++++++++++++++++++++++++++ 5 files changed, 308 insertions(+), 3 deletions(-) create mode 100644 td-reproducible-tool/Cargo.toml create mode 100644 td-reproducible-tool/readme.md create mode 100644 td-reproducible-tool/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 1765c3ff..65becc89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "td-shim", "td-shim-tools", "tdx-tdcall", + "td-reproducible-tool", "tests/test-td-exception", "tests/test-td-paging", "tests/test-td-payload", @@ -25,5 +26,6 @@ panic = "abort" # disable stack unwinding on panic # the profile used for `cargo build --release` [profile.release] -panic = "abort" # disable stack unwinding on panic -lto = true # Link-time optimization +panic = "abort" # disable stack unwinding on panic +lto = true # Link-time optimization +strip = "debuginfo" # remove debug related symbols/sections diff --git a/sh_script/build_final.sh b/sh_script/build_final.sh index 98860bdb..0903a8b7 100644 --- a/sh_script/build_final.sh +++ b/sh_script/build_final.sh @@ -14,6 +14,8 @@ final_boot_kernel() { echo "Build final binary with boot-kernel support" cargo xbuild -p td-shim --target x86_64-unknown-uefi --release --features=main,tdx,boot-kernel + cargo run -p td-reproducible-tool -- -n td-shim --target x86_64-unknown-uefi + cargo run -p td-shim-tools --features=td-shim/default,td-shim/tdx,boot-kernel --bin td-shim-ld -- \ target/x86_64-unknown-uefi/release/ResetVector.bin \ target/x86_64-unknown-uefi/release/td-shim.efi \ @@ -24,6 +26,10 @@ final_pe() { echo final-pe cargo xbuild -p td-shim --target x86_64-unknown-uefi --release --features=main,tdx --no-default-features cargo xbuild -p td-payload --target x86_64-unknown-uefi --release --features=main,tdx --no-default-features + + cargo run -p td-reproducible-tool -- -n td-shim --target x86_64-unknown-uefi + cargo run -p td-reproducible-tool -- -n td-payload --target x86_64-unknown-uefi + cargo run -p td-shim-tools --features="linker" --no-default-features --bin td-shim-ld -- \ target/x86_64-unknown-uefi/release/ResetVector.bin \ target/x86_64-unknown-uefi/release/td-shim.efi \ @@ -38,6 +44,10 @@ final_pe_test() { popd cargo xbuild -p td-shim --target x86_64-unknown-uefi --release --features=main,tdx --no-default-features + + cargo run -p td-reproducible-tool -- -n td-shim --target x86_64-unknown-uefi + cargo run -p td-reproducible-tool -- -n test-td-payload --target x86_64-unknown-uefi + cargo run -p td-shim-tools --features="linker" --no-default-features --bin td-shim-ld -- \ target/x86_64-unknown-uefi/release/ResetVector.bin \ target/x86_64-unknown-uefi/release/td-shim.efi \ @@ -57,6 +67,10 @@ final_elf() { echo final-elf cargo xbuild -p td-shim --target x86_64-unknown-uefi --release --features=main,tdx --no-default-features cargo xbuild -p td-payload --target devtools/rustc-targets/x86_64-unknown-none.json --release --features=main,tdx --no-default-features + + cargo run -p td-reproducible-tool -- -n td-shim --target x86_64-unknown-uefi + cargo run -p td-reproducible-tool -- -n td-payload --target x86_64-unknown-none + cargo run -p td-shim-tools --features="linker" --no-default-features --bin td-shim-ld -- \ target/x86_64-unknown-uefi/release/ResetVector.bin \ target/x86_64-unknown-uefi/release/td-shim.efi \ @@ -71,6 +85,10 @@ final_elf_test() { popd cargo xbuild -p td-shim --target x86_64-unknown-uefi --release --features=main,tdx --no-default-features + + cargo run -p td-reproducible-tool -- -n td-shim --target x86_64-unknown-uefi + cargo run -p td-reproducible-tool -- -n test-td-payload --target x86_64-unknown-none + cargo run -p td-shim-tools --features="linker" --no-default-features --bin td-shim-ld -- \ target/x86_64-unknown-uefi/release/ResetVector.bin \ target/x86_64-unknown-uefi/release/td-shim.efi \ @@ -90,7 +108,10 @@ final_pe_sb_test() { echo "Build final binaries with PE format td payload for secure boot test" cargo xbuild -p td-shim --target x86_64-unknown-uefi --release --features=main,tdx,secure-boot --no-default-features cargo xbuild -p td-payload --target x86_64-unknown-uefi --release --features=main,tdx --no-default-features - + + cargo run -p td-reproducible-tool -- -n td-shim --target x86_64-unknown-uefi + cargo run -p td-reproducible-tool -- -n td-payload --target x86_64-unknown-uefi + cargo run -p td-shim-tools --bin td-shim-sign-payload -- -A ECDSA_NIST_P384_SHA384 data/sample-keys/ecdsa-p384-private.pk8 target/x86_64-unknown-uefi/release/td-payload.efi 1 1 echo "Build final binary with unsigned td payload" @@ -132,6 +153,9 @@ final_elf_sb_test() { cargo xbuild -p td-shim --target x86_64-unknown-uefi --release --features=main,tdx,secure-boot --no-default-features cargo xbuild -p td-payload --target devtools/rustc-targets/x86_64-unknown-none.json --release --features=main,tdx --no-default-features + cargo run -p td-reproducible-tool -- -n td-shim --target x86_64-unknown-uefi + cargo run -p td-reproducible-tool -- -n td-payload --target x86_64-unknown-none + cargo run -p td-shim-tools --bin td-shim-sign-payload -- -A ECDSA_NIST_P384_SHA384 data/sample-keys/ecdsa-p384-private.pk8 target/x86_64-unknown-none/release/td-payload 1 1 echo "Build final binary with unsigned td payload" diff --git a/td-reproducible-tool/Cargo.toml b/td-reproducible-tool/Cargo.toml new file mode 100644 index 00000000..915ed47f --- /dev/null +++ b/td-reproducible-tool/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "td-reproducible-tool" +version = "0.1.0" +description = "A tool modify compiled binary to keep reproducible" +repository = "https://github.com/confidential-containers/td-shim" +homepage = "https://github.com/confidential-containers" +license = "BSD-2-Clause-Patent" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = [] + +[dependencies] +argparse = "0.2.2" +zeroize = "1.5.4" +regex = "1" \ No newline at end of file diff --git a/td-reproducible-tool/readme.md b/td-reproducible-tool/readme.md new file mode 100644 index 00000000..1e15834e --- /dev/null +++ b/td-reproducible-tool/readme.md @@ -0,0 +1,61 @@ +# td-reproducible-tool + +## Background + +Elements break reproducible build (x86_64-unknown-uefi + target x86_64-unknown-none)
+
+``` +1. Debug related sections (PE + ELF) +2. Code base path like \home\USERNAME\rust-td-payload\xxx\xxx.rs (PE + ELF) +3. CARGO_HOME like \home\USERNAME\.cargo\xxx\xxx.rs (PE + ELF) +4. RUSTUP_HOME like \home\USERNAME\.rustup\xxx\xxx.rs (PE + ELF) +5. TimeDateStamp field in COFF file header (PE only) +``` + +Solution:
+ +``` +1. Use strip = "symbols" option to [profile.release] +2. Use strip = "symbols" option to [profile.release] +3. Use a post-build tool to zero CARGO_HOME string +4. Use a post-build tool to zero RUSTUP_HOME string +5. Use a post-build tool to zero TimeDateStamp for PE +``` +NOTE: 3 and 4 may be resolved directly after rust-lang RFC 3127. This tool provide a temp solution to solve 3 and 4 before RFC is implemented. + +## tool usage + +``` +td-reproducible-tool [OPTIONS] + +TD REPRODUCIBLE TOOL + +Optional arguments: + -h,--help Show this help message and exit + -w,--workspace WORKSPACE + Where to find the target folder. + -n,--name NAME Name for the compiled binary. + -t,--target TARGET The built target to find. + -p,--profile PROFILE The built profile to find. + -c,--cargo_home CARGO_HOME + The cargo home. If not specify, system variable CARGO_HOME will be searched. + -r,--rustup_home RUSTUP_HOME + The rustup home. If not specify, system variable RUSTUP_HOME will be searched. + -v,--verbose Verbose output. +``` + +example:
+Command used under windows: +``` +cargo run -p td-reproducible-tool -- -n rust-td-payload --target x86_64-unknown-uefi -p release -v +``` +is equal to +``` +cargo run -p td-reproducible-tool -- -w "." -n rust-td-payload.efi -t x86_64-unknown-uefi -p release -v +``` +
+Command used under linux: + +``` +cargo run -p td-reproducible-tool -- -n rust-td-payload --target x86_64-unknown-none -p release -v +``` \ No newline at end of file diff --git a/td-reproducible-tool/src/main.rs b/td-reproducible-tool/src/main.rs new file mode 100644 index 00000000..fa32d104 --- /dev/null +++ b/td-reproducible-tool/src/main.rs @@ -0,0 +1,200 @@ +// Copyright (c) 2022 Intel Corporation +// +// SPDX-License-Identifier: BSD-2-Clause-Patent + +#![forbid(unsafe_code)] + +use argparse::{ArgumentParser, Store, StoreTrue}; +use regex::bytes::Regex; +use std::convert::TryInto; +use std::fs; +use std::io::Read; +use std::{env, fs::File, path::PathBuf}; +use zeroize::Zeroize; + +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format +const PE_SIG_PTR_OFF: usize = 0x3C; +const TIMEDATASTAMP_OFF_TO_PE_SIG: usize = 0x8; + +fn main() -> std::io::Result<()> { + // handle args + let mut workspace = "".to_string(); + let mut binary_name = "".to_string(); + let mut target = "".to_string(); + let mut profile = "".to_string(); + let mut verbose = false; + + let mut cargo_home = "".to_string(); + let mut rustup_home = "".to_string(); + + { + let mut ap = ArgumentParser::new(); + ap.set_description("TD REPRODUCIBLE TOOL"); + ap.refer(&mut workspace).add_option( + &["-w", "--workspace"], + Store, + "Where to find the target folder.", + ); + ap.refer(&mut binary_name).add_option( + &["-n", "--name"], + Store, + "Name for the compiled binary.", + ); + ap.refer(&mut target) + .add_option(&["-t", "--target"], Store, "The built target to find."); + ap.refer(&mut profile).add_option( + &["-p", "--profile"], + Store, + "The built profile to find.", + ); + ap.refer(&mut cargo_home) + .add_option(&["-c", "--cargo_home"], Store, "The cargo home."); + ap.refer(&mut rustup_home) + .add_option(&["-r", "--rustup_home"], Store, "The rustup home."); + ap.refer(&mut verbose) + .add_option(&["-v", "--verbose"], StoreTrue, "Verbose output."); + ap.parse_args_or_exit(); + } + + if target == "" { + target = ".".to_string(); + } else if target == "x86_64-unknown-uefi" { + if !binary_name.ends_with(".efi") { + binary_name = binary_name + ".efi"; + } + } else if target == "x86_64-unknown-none" { + // nothing to check + } + + // solve the path + let binary_path: PathBuf = [ + if workspace == "" { "." } else { &workspace }, + "target", + &target, + if profile == "" { "release" } else { &profile }, + &binary_name, + ] + .iter() + .collect(); + + assert!(binary_path.exists()); + println!("INFO: Found the compiled file: {:?}", binary_path); + + // check out CARGO_HOME and RUSTUP_HOME + cargo_home = if cargo_home == "" { + if env::var("CARGO_HOME").is_err() { + panic!("Neither --cargo_home nor system environment for \"CARGO_HOME\" is found! ") + } else { + env::var("CARGO_HOME").unwrap() + } + } else { + cargo_home + }; + + rustup_home = if rustup_home == "" { + if env::var("RUSTUP_HOME").is_err() { + panic!("Neither --rustup_home nor system environment for \"RUSTUP_HOME\" is found! ") + } else { + env::var("RUSTUP_HOME").unwrap() + } + } else { + rustup_home + }; + + // load the binary + let mut binary = + File::open(binary_path.clone()).expect("Failed to open the compiled binary!\n"); + let mut buf = Vec::new(); + binary + .read_to_end(&mut buf) + .expect("Failed to read the compiled binary!\n"); + + // strip time data stamp in PE case + if target == "x86_64-unknown-uefi" + || binary_name.ends_with("exe") + || binary_name.ends_with("efi") + { + let pe_sig_off = + u32::from_le_bytes(buf[PE_SIG_PTR_OFF..PE_SIG_PTR_OFF + 4].try_into().unwrap()); + let pe_sig = u32::from_le_bytes( + buf[pe_sig_off as usize..pe_sig_off as usize + 4] + .try_into() + .unwrap(), + ); + + assert_eq!( + pe_sig, + u32::from_le_bytes([b'P', b'E', b'\0', b'\0']), + "Found invalid PE signature!" + ); + + let time_stamp = u32::from_le_bytes( + buf[pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG + ..pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG + 4] + .try_into() + .unwrap(), + ); + + println!("INFO: Detected TimeDateStamp: {:?}", time_stamp); + + buf[pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG + ..pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG + 4] + .zeroize(); + + let time_stamp = u32::from_le_bytes( + buf[pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG + ..pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG + 4] + .try_into() + .unwrap(), + ); + println!("INFO: After removing TimeDateStamp: {:?}", time_stamp); + } + + let mut cargo_like_path_to_strip_regex_pat_str = regex::escape(&cargo_home); + cargo_like_path_to_strip_regex_pat_str.push_str(".*?\\.rs"); + let mut rustup_like_path_to_strip_regex_pat_str = regex::escape(&rustup_home); + rustup_like_path_to_strip_regex_pat_str.push_str(".*?\\.rs"); + + let cargo_like_path_to_strip_regex = + Regex::new(&cargo_like_path_to_strip_regex_pat_str).unwrap(); + let rustup_like_path_to_strip_regex = + Regex::new(&rustup_like_path_to_strip_regex_pat_str).unwrap(); + + let mut stat = 0; + let buf_read_only = buf.clone(); + for mat in cargo_like_path_to_strip_regex.find_iter(&buf_read_only) { + stat += 1; + if verbose { + println!( + "Found an item with start offset:{:?}, end offset:{:?}", + mat.start(), + mat.end() + ); + println!("{:?}", std::str::from_utf8(mat.as_bytes())); + println!("Now removing it!"); + } + buf[mat.start()..mat.end()].zeroize(); + } + + for mat in rustup_like_path_to_strip_regex.find_iter(&buf_read_only) { + stat += 1; + if verbose { + println!( + "Found an item with start offset:{:?}, end offset:{:?}", + mat.start(), + mat.end() + ); + println!("{:?}", std::str::from_utf8(mat.as_bytes())); + println!("Now removing it!"); + } + buf[mat.start()..mat.end()].zeroize(); + } + + println!("INFO: Removed {:?} items!", stat); + + fs::write(binary_path, buf).expect("Failed to open the compiled binary!\n"); + + println!("INFO: Successful patched the compiled binary!"); + + Ok(()) +}