-
Notifications
You must be signed in to change notification settings - Fork 88
Provide a way of detecting system library leakage #5259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
97c484e
c0cfacb
4dddd26
adb563b
f6c32d3
2445f20
8efc76f
8decf7c
c8791e9
8029cbe
4e31ae8
276e135
dd71b07
da25241
c5ac263
b78e056
fa5b905
c886aea
c2b849d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # This config file is used by `cargo xtask verify-libraries` | ||
|
|
||
|
|
||
| # These are libraries that we expect to show up in any executable produced | ||
| # by the omicron repo. | ||
| [libraries."libc.so.1"] | ||
| [libraries."libcontract.so.1"] | ||
| [libraries."libcrypto.so.3"] | ||
| [libraries."libdevinfo.so.1"] | ||
| [libraries."libdlpi.so.1"] | ||
| [libraries."libdoor.so.1"] | ||
| [libraries."libefi.so.1"] | ||
| [libraries."libgcc_s.so.1"] | ||
| [libraries."libipcc.so.1"] | ||
| [libraries."libkstat.so.1"] | ||
| [libraries."libm.so.2"] | ||
| [libraries."libnsl.so.1"] | ||
| [libraries."libnvpair.so.1"] | ||
| [libraries."libpq.so.5"] | ||
| [libraries."libpthread.so.1"] | ||
| [libraries."libresolv.so.2"] | ||
| [libraries."librt.so.1"] | ||
| [libraries."libscf.so.1"] | ||
| [libraries."libsocket.so.1"] | ||
| [libraries."libssl.so.3"] | ||
| [libraries."libumem.so.1"] | ||
| [libraries."libxml2.so.2"] | ||
| [libraries."libxmlsec1.so.1"] | ||
|
|
||
| # libnvme is a global zone only library and therefore we must be sure that only | ||
| # programs running in the gz require it. Additionally only sled-agent should be | ||
| # managing a sled's hardware. | ||
| [libraries."libnvme.so.1"] | ||
| binary_allow_list = [ | ||
| "installinator", | ||
| "omicron-dev", | ||
| "omicron-package", | ||
| "services-ledger-check-migrate", | ||
| "sled-agent", | ||
| "sled-agent-sim", | ||
| ] |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| use anyhow::{bail, Context, Result}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MPL files require a license header up top -- could you add one here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in fa5b905 |
||
| use camino::Utf8Path; | ||
| use cargo_metadata::Message; | ||
| use fs_err as fs; | ||
| use serde::Deserialize; | ||
| use std::{ | ||
| collections::{BTreeMap, BTreeSet}, | ||
| io::BufReader, | ||
| process::{Command, Stdio}, | ||
| }; | ||
| use swrite::{swriteln, SWrite}; | ||
|
|
||
| use crate::load_workspace; | ||
|
|
||
| #[derive(Deserialize, Debug)] | ||
| struct LibraryConfig { | ||
| binary_allow_list: Option<BTreeSet<String>>, | ||
| } | ||
|
|
||
| #[derive(Deserialize, Debug)] | ||
| struct XtaskConfig { | ||
| libraries: BTreeMap<String, LibraryConfig>, | ||
| } | ||
|
|
||
| #[derive(Debug)] | ||
| enum LibraryError { | ||
| Unexpected(String), | ||
| NotAllowed(String), | ||
| } | ||
|
|
||
| /// Verify that the binary at the provided path complies with the rules laid out | ||
| /// in the xtask.toml config file. Errors are pushed to a hashmap so that we can | ||
| /// display to a user the entire list of issues in one go. | ||
| fn verify_executable( | ||
| config: &XtaskConfig, | ||
| path: &Utf8Path, | ||
| errors: &mut BTreeMap<String, Vec<LibraryError>>, | ||
| ) -> Result<()> { | ||
| let binary = path.file_name().context("basename of executable")?; | ||
|
|
||
| let command = Command::new("elfedit") | ||
| .args(["-o", "simple", "-r", "-e", "dyn:tag NEEDED"]) | ||
| .arg(path) | ||
| .output() | ||
| .context("exec elfedit")?; | ||
|
|
||
| if !command.status.success() { | ||
| bail!("Failed to execute elfedit successfully {}", command.status); | ||
| } | ||
|
|
||
| let stdout = String::from_utf8(command.stdout)?; | ||
| // `elfedit -o simple -r -e "dyn:tag NEEDED" /file/path` will return | ||
| // a new line seperated list of required libraries so we walk over | ||
| // them looking for a match in our configuration file. If we find | ||
| // the library we make sure the binary is allowed to pull it in via | ||
| // the whitelist. | ||
| for library in stdout.lines() { | ||
| let library_config = match config.libraries.get(library.trim()) { | ||
| Some(config) => config, | ||
| None => { | ||
| errors | ||
| .entry(binary.to_string()) | ||
| .or_default() | ||
| .push(LibraryError::Unexpected(library.to_string())); | ||
|
|
||
| continue; | ||
| } | ||
| }; | ||
|
|
||
| if let Some(allowed) = &library_config.binary_allow_list { | ||
| if !allowed.contains(binary) { | ||
| errors | ||
| .entry(binary.to_string()) | ||
| .or_default() | ||
| .push(LibraryError::NotAllowed(library.to_string())); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
| pub fn cmd_verify_libraries() -> Result<()> { | ||
| let metadata = load_workspace()?; | ||
| let mut config_path = metadata.workspace_root; | ||
| config_path.push(".cargo/xtask.toml"); | ||
| let config = read_xtask_toml(&config_path)?; | ||
|
|
||
| let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); | ||
| let mut command = Command::new(cargo) | ||
| .args(["build", "--bins", "--message-format=json-render-diagnostics"]) | ||
| .stdout(Stdio::piped()) | ||
| .spawn() | ||
| .context("failed to spawn cargo build")?; | ||
|
|
||
| let reader = BufReader::new(command.stdout.take().context("take stdout")?); | ||
|
|
||
| let mut errors = Default::default(); | ||
| for message in cargo_metadata::Message::parse_stream(reader) { | ||
| if let Message::CompilerArtifact(artifact) = message? { | ||
| // We are only interested in artifacts that are binaries | ||
| if let Some(executable) = artifact.executable { | ||
| verify_executable(&config, &executable, &mut errors)?; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let status = command.wait()?; | ||
| if !status.success() { | ||
| bail!("Failed to execute cargo build successfully {}", status); | ||
| } | ||
|
|
||
| if !errors.is_empty() { | ||
| let mut msg = String::new(); | ||
| errors.iter().for_each(|(binary, errors)| { | ||
| swriteln!(msg, "{binary}"); | ||
| errors.iter().for_each(|error| match error { | ||
| LibraryError::Unexpected(lib) => { | ||
| swriteln!(msg, "\tUNEXPECTED dependency on {lib}"); | ||
| } | ||
| LibraryError::NotAllowed(lib) => { | ||
| swriteln!(msg, "\tNEEDS {lib} but is not allowed"); | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| bail!( | ||
| "Found library issues with the following:\n{msg}\n\n\ | ||
| If depending on a new library was intended please add it to xtask.toml" | ||
| ); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn read_xtask_toml(path: &Utf8Path) -> Result<XtaskConfig> { | ||
| let config_str = fs::read_to_string(path)?; | ||
| toml::from_str(&config_str).with_context(|| format!("parse {:?}", path)) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't look like it runs
cargo build --bins? Justcargo build.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in fa5b905