Skip to content

Commit

Permalink
Reproducible buid: introduce a tool to help reproducible build
Browse files Browse the repository at this point in the history
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 <longlong.yang@intel.com>
  • Loading branch information
longlongyang committed May 15, 2022
1 parent 0e08c3e commit d88ad7c
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -27,3 +28,4 @@ panic = "abort" # disable stack unwinding on panic
[profile.release]
panic = "abort" # disable stack unwinding on panic
lto = true # Link-time optimization
strip = "symbols" # remove debug related symbols/sections
26 changes: 25 additions & 1 deletion sh_script/build_final.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 \
Expand All @@ -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 \
Expand All @@ -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 \
Expand All @@ -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 \
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
18 changes: 18 additions & 0 deletions td-reproducible-tool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
61 changes: 61 additions & 0 deletions td-reproducible-tool/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# td-reproducible-tool

## Background

Elements break reproducible build (x86_64-unknown-uefi + target x86_64-unknown-none) <br>
<br>
```
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: <br>

```
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:<br>
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
```
<br>
Command used under linux:

```
cargo run -p td-reproducible-tool -- -n rust-td-payload --target x86_64-unknown-none -p release -v
```
200 changes: 200 additions & 0 deletions td-reproducible-tool/src/main.rs
Original file line number Diff line number Diff line change
@@ -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(())
}

0 comments on commit d88ad7c

Please sign in to comment.