From 6d1c9ff2dc0dca6de859615537dcd743300cf420 Mon Sep 17 00:00:00 2001 From: Kaare Hoff Skovgaard Date: Thu, 10 Jul 2025 20:57:01 +0200 Subject: [PATCH] Attempt to fix compilation error on Macos Former-commit-id: 12ab4ce9189aac73de1ff4a75f77db15cc7baff9 --- rust/Cargo.toml | 2 +- rust/program/openbao-helper/Cargo.toml | 2 +- rust/program/openbao-helper/src/main.rs | 68 +++++++++++++++++++++---- 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 7db53b4..a4d112d 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -21,7 +21,7 @@ clap = { version = "4.5.39", default-features = false, features = [ "derive", ] } log = { version = "0.4.27", default-features = false, features = ["std"] } -nix = { version = "0.30.1", default-features = false, features = ["process"] } +nix = { version = "0.30.1", default-features = false } serde = { version = "1.0.219", default-features = false, features = [ "derive", "std", diff --git a/rust/program/openbao-helper/Cargo.toml b/rust/program/openbao-helper/Cargo.toml index b85f031..b73ed62 100644 --- a/rust/program/openbao-helper/Cargo.toml +++ b/rust/program/openbao-helper/Cargo.toml @@ -9,6 +9,6 @@ anyhow = { workspace = true } clap = { workspace = true } common = { path = "../../lib/common" } log = { workspace = true } -nix = { workspace = true } +nix = { workspace = true, features = ["env", "process"] } serde = { workspace = true } hakari = { version = "0.1", path = "../../lib/hakari" } diff --git a/rust/program/openbao-helper/src/main.rs b/rust/program/openbao-helper/src/main.rs index 8b6ddb0..a6c35ef 100644 --- a/rust/program/openbao-helper/src/main.rs +++ b/rust/program/openbao-helper/src/main.rs @@ -1,4 +1,8 @@ -use std::{collections::BTreeSet, ffi::CString}; +use std::{ + collections::BTreeSet, + convert::Infallible, + ffi::{CStr, CString, OsStr, OsString}, +}; use anyhow::Context as _; use clap::{Parser, Subcommand}; @@ -195,18 +199,13 @@ fn wrap_program(wrap_program: WrapProgram) -> anyhow::Result<()> { return Err(anyhow::format_err!("No command to execute was specified")); } let unique: BTreeSet<_> = BTreeSet::from_iter(endpoint); - let mut env = Vec::::new(); + let mut env = Vec::<(OsString, OsString)>::new(); for (key, value) in std::env::vars() { - env.push( - CString::new(format!("{key}={value}")) - .with_context(|| format!("Environment variable {key} contained a null byte"))?, - ); + env.push((OsString::from(key), OsString::from(value))); } for env_set in unique { for (key, value) in env_set.try_into_env_data()? { - env.push(CString::new(format!("{key}={value}")).with_context(|| { - format!("Environment variable {key} contained a null byte") - })?); + env.push((OsString::from(key), OsString::from(value))); } } let mut args = Vec::new(); @@ -217,7 +216,56 @@ fn wrap_program(wrap_program: WrapProgram) -> anyhow::Result<()> { } (args, env) }; - nix::unistd::execvpe(&args[0], args.as_slice(), env.as_slice())?; + unsafe { + execvpe(&args[0], args.as_slice(), env.as_slice())?; + } // This will never get executed Ok(()) } + +#[cfg(not(target_os = "macos"))] +/// Safety: No other threads may read or write environment variables when this function is called. +/// The easiest way to ensure this is using a single threaded program. +/// Note: On Linux specifically this safety requirement is not needed +unsafe fn execvpe, SEK: AsRef, SEV: AsRef>( + filename: &CStr, + args: &[SA], + environ: &[(SEK, SEV)], +) -> anyhow::Result { + let environ: Vec<_> = environ + .iter() + .map(|(k, v)| { + CString::new(Format!("{k}={v}")) + .with_context(|| format!("Environment variable {k} contains null bytes"))? + }) + .collect(); + Ok(nix::unistd::execvpe(filename, args, &environ)?) +} + +#[cfg(target_os = "macos")] +/// Safety: No other threads may read or write environment variables when this function is called. +/// The easiest way to ensure this is using a single threaded program. +// Simple "bad" version of execvpe that also works on OSX +unsafe fn execvpe, SEK: AsRef, SEV: AsRef>( + filename: &CStr, + args: &[SA], + environ: &[(SEK, SEV)], +) -> anyhow::Result { + let current_env = std::env::vars_os(); + // Safety: Same as this function + unsafe { nix::env::clearenv()? }; + for (key, val) in environ { + // Safety: Same as this function + unsafe { std::env::set_var(key.as_ref(), val.as_ref()) }; + } + match nix::unistd::execvp(filename, args) { + Err(err) => { + unsafe { nix::env::clearenv()? }; + for (key, val) in current_env { + unsafe { std::env::set_var(key.as_os_str(), val.as_os_str()) }; + } + Err(err.into()) + } + _ => unreachable!("execvp doesn't return on success"), + } +}