Skip to content
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
83 changes: 36 additions & 47 deletions nvx-rs/src/guest/managed_agent/exec_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,29 @@ fn encode(execution: &GuestExecPayload, cwd_entered: bool) -> Result<Vec<u8>, Er
if execution.inherit_default_env {
flags |= EXEC_INHERIT_DEFAULT_ENV;
}
let environment = execution.environment.as_deref().unwrap_or_default();
encode_fields(
flags,
cwd.unwrap_or_default(),
execution.environment.as_deref().unwrap_or_default(),
)
}

/// Lays out the private helper configuration format with `flags`, the working directory `cwd`,
/// empty without one, and the `environment` entries: the little-endian flags, entry count, and
/// working-directory length, then the working directory, then each entry after its
/// little-endian length. Tests also lay out configurations that [`encode`] never produces.
pub(super) fn encode_fields(
flags: u16,
cwd: &[u8],
environment: &[Vec<u8>],
) -> Result<Vec<u8>, Errno> {
let count = u16::try_from(environment.len()).map_err(|_| Errno::EOVERFLOW)?;
let cwd_len = u32::try_from(cwd.map_or(0, <[u8]>::len)).map_err(|_| Errno::EOVERFLOW)?;
let cwd_len = u32::try_from(cwd.len()).map_err(|_| Errno::EOVERFLOW)?;
let mut bytes = Vec::new();
bytes.extend_from_slice(&flags.to_le_bytes());
bytes.extend_from_slice(&count.to_le_bytes());
bytes.extend_from_slice(&cwd_len.to_le_bytes());
if let Some(cwd) = cwd {
bytes.extend_from_slice(cwd);
}
bytes.extend_from_slice(cwd);
for entry in environment {
let length = u32::try_from(entry.len()).map_err(|_| Errno::EOVERFLOW)?;
bytes.extend_from_slice(&length.to_le_bytes());
Expand Down Expand Up @@ -119,10 +132,20 @@ pub(crate) fn read(descriptor: &FileDescriptor) -> Result<HelperConfig, Errno> {
})
}

/// Returns a descriptor of a file that holds `bytes`, from which [`read`] reads a configuration
/// as it reads a sealed one.
#[cfg(test)]
mod tests {
pub(super) fn file_with(bytes: &[u8]) -> FileDescriptor {
use std::io::{Seek, Write};

let mut file = tempfile::tempfile().unwrap();
file.write_all(bytes).unwrap();
file.rewind().unwrap();
FileDescriptor::from_owned(file.into())
}

#[cfg(test)]
mod tests {
use super::*;

fn execution() -> GuestExecPayload {
Expand All @@ -135,13 +158,6 @@ mod tests {
}
}

fn file_with(bytes: &[u8]) -> FileDescriptor {
let mut file = tempfile::tempfile().unwrap();
file.write_all(bytes).unwrap();
file.rewind().unwrap();
FileDescriptor::from_owned(file.into())
}

#[test]
fn helper_configuration_round_trips_empty_exact_and_large_environments() {
for environment in [
Expand Down Expand Up @@ -178,40 +194,13 @@ mod tests {
#[test]
fn helper_defensively_rejects_unknown_flags_and_malformed_environment() {
for bytes in [
{
let mut bytes = Vec::new();
bytes.extend_from_slice(&0x4000u16.to_le_bytes());
bytes.extend_from_slice(&0u16.to_le_bytes());
bytes.extend_from_slice(&0u32.to_le_bytes());
bytes
},
{
let mut bytes = Vec::new();
bytes.extend_from_slice(&EXEC_ENVIRONMENT_PRESENT.to_le_bytes());
bytes.extend_from_slice(&1u16.to_le_bytes());
bytes.extend_from_slice(&0u32.to_le_bytes());
bytes.extend_from_slice(&9u32.to_le_bytes());
bytes.extend_from_slice(b"NO_EQUALS");
bytes
},
{
let mut bytes = Vec::new();
bytes.extend_from_slice(&EXEC_ENVIRONMENT_PRESENT.to_le_bytes());
bytes.extend_from_slice(&1u16.to_le_bytes());
bytes.extend_from_slice(&0u32.to_le_bytes());
bytes.extend_from_slice(&6u32.to_le_bytes());
bytes.extend_from_slice(b"=value");
bytes
},
{
let mut bytes = Vec::new();
bytes.extend_from_slice(&EXEC_CWD_ENTERED.to_le_bytes());
bytes.extend_from_slice(&0u16.to_le_bytes());
bytes.extend_from_slice(&1u32.to_le_bytes());
bytes.push(b'/');
bytes
},
] {
encode_fields(0x4000, b"", &[]),
encode_fields(EXEC_ENVIRONMENT_PRESENT, b"", &[b"NO_EQUALS".to_vec()]),
encode_fields(EXEC_ENVIRONMENT_PRESENT, b"", &[b"=value".to_vec()]),
encode_fields(EXEC_CWD_ENTERED, b"/", &[]),
]
.map(Result::unwrap)
{
let descriptor = file_with(&bytes);
assert!(read(&descriptor).is_err(), "{bytes:?}");
}
Expand Down
32 changes: 9 additions & 23 deletions nvx-rs/src/guest/managed_agent/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,6 @@ pub(crate) fn safe_path(path: &[u8]) -> bool {

#[cfg(test)]
mod tests {
use std::io::{Seek, Write};

use crate::guest::sys::FileDescriptor;
use crate::protocol::{self, GuestExecPayload};

Expand Down Expand Up @@ -166,13 +164,6 @@ mod tests {
arguments
}

fn file_with(bytes: &[u8]) -> FileDescriptor {
let mut file = tempfile::tempfile().unwrap();
file.write_all(bytes).unwrap();
file.rewind().unwrap();
FileDescriptor::from_owned(file.into())
}

/// Returns the inherited environment without the helper's descriptor variable.
fn inherited() -> Vec<Vec<u8>> {
let mut environment = environment::inherited().unwrap();
Expand Down Expand Up @@ -224,26 +215,21 @@ mod tests {
);
assert!(system.execs.is_empty());

let mut relative = Vec::new();
relative.extend_from_slice(&protocol::EXEC_CWD_PRESENT.to_le_bytes());
relative.extend_from_slice(&0u16.to_le_bytes());
relative.extend_from_slice(&1u32.to_le_bytes());
relative.push(b'.');
let fd = file_with(&relative);
let relative = exec_config::encode_fields(protocol::EXEC_CWD_PRESENT, b".", &[]).unwrap();
let fd = exec_config::file_with(&relative);
let helper_arguments = arguments(&mut system, fd, &[b"/bin/pwd"]);
assert_eq!(
run(&mut system, &helper_arguments),
Err(CommandError::Agent)
);

let mut conflicting = Vec::new();
conflicting.extend_from_slice(
&(protocol::EXEC_CWD_PRESENT | exec_config::EXEC_CWD_ENTERED).to_le_bytes(),
);
conflicting.extend_from_slice(&0u16.to_le_bytes());
conflicting.extend_from_slice(&1u32.to_le_bytes());
conflicting.push(b'/');
let fd = file_with(&conflicting);
let conflicting = exec_config::encode_fields(
protocol::EXEC_CWD_PRESENT | exec_config::EXEC_CWD_ENTERED,
b"/",
&[],
)
.unwrap();
let fd = exec_config::file_with(&conflicting);
let helper_arguments = arguments(&mut system, fd, &[b"/bin/pwd"]);
assert_eq!(
run(&mut system, &helper_arguments),
Expand Down
Loading