Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

change untar to use unpack instead of unpack_in #19216

Merged
merged 9 commits into from
Aug 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion download-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ log = "0.4.14"
reqwest = { version = "0.11.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
solana-sdk = { path = "../sdk", version = "=1.8.0" }
solana-runtime = { path = "../runtime", version = "=1.8.0" }
tar = "0.4.35"
tar = "0.4.37"

[lib]
crate-type = ["lib"]
Expand Down
2 changes: 1 addition & 1 deletion install/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ solana-logger = { path = "../logger", version = "=1.8.0" }
solana-sdk = { path = "../sdk", version = "=1.8.0" }
solana-version = { path = "../version", version = "=1.8.0" }
semver = "1.0.4"
tar = "0.4.35"
tar = "0.4.37"
tempfile = "3.2.0"
url = "2.2.2"

Expand Down
4 changes: 2 additions & 2 deletions programs/bpf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ solana-secp256k1-program = { path = "../programs/secp256k1", version = "=1.8.0"
solana-stake-program = { path = "../programs/stake", version = "=1.8.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.8.0" }
symlink = "0.1.0"
tar = "0.4.35"
tar = "0.4.37"
tempfile = "3.2.0"
thiserror = "1.0"
zstd = "0.9.0"
Expand Down
87 changes: 83 additions & 4 deletions runtime/src/hardened_unpack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use {
fs::{self, File},
io::{BufReader, Read},
path::{
Component::{CurDir, Normal},
Component::{self, CurDir, Normal},
Path, PathBuf,
},
time::Instant,
Expand Down Expand Up @@ -161,9 +161,14 @@ where
)?;
total_count = checked_total_count_increment(total_count, limit_count)?;

// unpack_in does its own sanitization
// ref: https://docs.rs/tar/*/tar/struct.Entry.html#method.unpack_in
check_unpack_result(entry.unpack_in(unpack_dir)?, path_str)?;
let target = sanitize_path(&entry.path()?, unpack_dir)?; // ? handles file system errors
if target.is_none() {
continue; // skip it
}
let target = target.unwrap();

let unpack = entry.unpack(target);
check_unpack_result(unpack.map(|_unpack| true)?, path_str)?;

// Sanitize permissions.
let mode = match entry.header().entry_type() {
Expand Down Expand Up @@ -199,6 +204,80 @@ where
}
}

// return Err on file system error
// return Some(path) if path is good
// return None if we should skip this file
fn sanitize_path(entry_path: &Path, dst: &Path) -> Result<Option<PathBuf>> {
// We cannot call unpack_in because it errors if we try to use 2 account paths.
// So, this code is borrowed from unpack_in
// ref: https://docs.rs/tar/*/tar/struct.Entry.html#method.unpack_in
let mut file_dst = dst.to_path_buf();
const SKIP: Result<Option<PathBuf>> = Ok(None);
{
let path = entry_path;
for part in path.components() {
match part {
// Leading '/' characters, root paths, and '.'
// components are just ignored and treated as "empty
// components"
Component::Prefix(..) | Component::RootDir | Component::CurDir => continue,

// If any part of the filename is '..', then skip over
// unpacking the file to prevent directory traversal
// security issues. See, e.g.: CVE-2001-1267,
// CVE-2002-0399, CVE-2005-1918, CVE-2007-4131
Component::ParentDir => return SKIP,

Component::Normal(part) => file_dst.push(part),
}
}
}

// Skip cases where only slashes or '.' parts were seen, because
// this is effectively an empty filename.
if *dst == *file_dst {
return SKIP;
}

// Skip entries without a parent (i.e. outside of FS root)
let parent = match file_dst.parent() {
Some(p) => p,
None => return SKIP,
};

fs::create_dir_all(parent)?;

// Here we are different than untar_in. The code for tar::unpack_in internally calling unpack is a little different.
// ignore return value here
validate_inside_dst(dst, parent)?;
let target = parent.join(entry_path.file_name().unwrap());

Ok(Some(target))
}

// copied from:
// https://github.com/alexcrichton/tar-rs/blob/d90a02f582c03dfa0fd11c78d608d0974625ae5d/src/entry.rs#L781
fn validate_inside_dst(dst: &Path, file_dst: &Path) -> Result<PathBuf> {
// Abort if target (canonical) parent is outside of `dst`
let canon_parent = file_dst.canonicalize().map_err(|err| {
UnpackError::Archive(format!(
"{} while canonicalizing {}",
err,
file_dst.display()
))
})?;
let canon_target = dst.canonicalize().map_err(|err| {
UnpackError::Archive(format!("{} while canonicalizing {}", err, dst.display()))
})?;
if !canon_parent.starts_with(&canon_target) {
return Err(UnpackError::Archive(format!(
"trying to unpack outside of destination path: {}",
canon_target.display()
)));
}
Ok(canon_target)
}

/// Map from AppendVec file name to unpacked file system location
pub type UnpackedAppendVecMap = HashMap<String, PathBuf>;

Expand Down
2 changes: 1 addition & 1 deletion sdk/cargo-build-bpf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ regex = "1.5.4"
cargo_metadata = "0.14.0"
solana-sdk = { path = "..", version = "=1.8.0" }
solana-download-utils = { path = "../../download-utils", version = "=1.8.0" }
tar = "0.4.35"
tar = "0.4.37"

[dev-dependencies]
serial_test = "*"
Expand Down