From 304b9d91466c6d9da34a27b43ebfccfdb1ef158f Mon Sep 17 00:00:00 2001 From: Nathan Whyte Date: Tue, 25 Apr 2023 16:46:26 -0500 Subject: [PATCH 1/8] kata-sys-util: Move CPU info functions Move get_single_cpu_info and get_cpu_flags into kata-sys-util. Add new functions that get a list of flags and check if a flag exists in that list. Fixes #6383 Signed-off-by: Nathan Whyte Signed-off-by: Archana Shinde --- src/libs/kata-sys-util/Cargo.toml | 1 + src/libs/kata-sys-util/src/cpu.rs | 372 ++++++++++++++++++++++++++++++ src/libs/kata-sys-util/src/lib.rs | 1 + 3 files changed, 374 insertions(+) create mode 100644 src/libs/kata-sys-util/src/cpu.rs diff --git a/src/libs/kata-sys-util/Cargo.toml b/src/libs/kata-sys-util/Cargo.toml index 40381b4d0..0d6cff91f 100644 --- a/src/libs/kata-sys-util/Cargo.toml +++ b/src/libs/kata-sys-util/Cargo.toml @@ -11,6 +11,7 @@ license = "Apache-2.0" edition = "2018" [dependencies] +anyhow = "1.0.31" byteorder = "1.4.3" cgroups = { package = "cgroups-rs", version = "0.3.2" } chrono = "0.4.0" diff --git a/src/libs/kata-sys-util/src/cpu.rs b/src/libs/kata-sys-util/src/cpu.rs new file mode 100644 index 000000000..7e1bef38d --- /dev/null +++ b/src/libs/kata-sys-util/src/cpu.rs @@ -0,0 +1,372 @@ +// Copyright (c) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::{anyhow, Result}; + +#[allow(dead_code)] +const ERR_NO_CPUINFO: &str = "cpu_info string is empty"; + +pub const PROC_CPUINFO: &str = "/proc/cpuinfo"; + +#[cfg(target_arch = "x86_64")] +pub const CPUINFO_DELIMITER: &str = "\nprocessor"; +#[cfg(target_arch = "x86_64")] +pub const CPUINFO_FLAGS_TAG: &str = "flags"; + +fn read_file_contents(file_path: &str) -> Result { + let contents = std::fs::read_to_string(file_path)?; + Ok(contents) +} + +// get_single_cpu_info returns the contents of the first cpu from +// the specified cpuinfo file by parsing based on a specified delimiter +pub fn get_single_cpu_info(cpu_info_file: &str, substring: &str) -> Result { + let contents = read_file_contents(cpu_info_file)?; + + if contents.is_empty() { + return Err(anyhow!(ERR_NO_CPUINFO)); + } + + let subcontents: Vec<&str> = contents.split(substring).collect(); + let result = subcontents + .first() + .ok_or("error splitting contents of cpuinfo") + .map_err(|e| anyhow!(e))? + .to_string(); + Ok(result) +} + +// get_cpu_flags returns a string of cpu flags from cpuinfo, passed in +// as a string +#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] +pub fn get_cpu_flags(cpu_info: &str, cpu_flags_tag: &str) -> Result { + if cpu_info.is_empty() { + return Err(anyhow!(ERR_NO_CPUINFO)); + } + + if cpu_flags_tag.is_empty() { + return Err(anyhow!("cpu flags delimiter string is empty"))?; + } + + get_cpu_flags_from_file(cpu_info, cpu_flags_tag) +} + +// get a list of cpu flags in cpu_info_flags +// +// cpu_info is the content of cpuinfo file passed in as a string +// returns empty Vec if no flags are found +#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] +pub fn get_cpu_flags_vec(cpu_info: &str, cpu_flags_tag: &str) -> Result> { + if cpu_info.is_empty() { + return Err(anyhow!(ERR_NO_CPUINFO)); + } + + if cpu_flags_tag.is_empty() { + return Err(anyhow!("cpu flags delimiter string is empty"))?; + } + + let flags = get_cpu_flags_from_file(cpu_info, cpu_flags_tag)?; + + // take each flag, trim whitespace, convert to String, and add to list + // skip the first token in the iterator since it is empty + let flags_vec: Vec = flags + .split(' ') + .skip(1) + .map(|f| f.trim().to_string()) + .collect::>(); + + Ok(flags_vec) +} + +// check if the given flag exists in the given flags_vec +// +// flags_vec can be created by calling get_cpu_flags_vec +#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] +pub fn contains_cpu_flag(flags_vec: &[String], flag: &str) -> Result { + if flag.is_empty() { + return Err(anyhow!("parameter specifying flag to look for is empty"))?; + } + + Ok(flags_vec.iter().any(|f| f == flag)) +} + +// get a String containing the cpu flags in cpu_info +// +// this function returns the list of flags as a single String +// if no flags are found, returns an empty String +#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] +fn get_cpu_flags_from_file(cpu_info: &str, cpu_flags_tag: &str) -> Result { + let subcontents: Vec<&str> = cpu_info.split('\n').collect(); + for line in subcontents { + if line.starts_with(cpu_flags_tag) { + let line_data: Vec<&str> = line.split(':').collect(); + let flags = line_data + .last() + .ok_or("error splitting flags in cpuinfo") + .map_err(|e| anyhow!(e))? + .to_string(); + return Ok(flags); + } + } + + Ok("".to_string()) +} + +#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use std::io::Write; + use tempfile::tempdir; + + #[test] + fn test_get_single_cpu_info() { + // Valid cpuinfo example + let dir = tempdir().unwrap(); + let file_path_full = dir.path().join("cpuinfo_full"); + let path_full = file_path_full.clone(); + let mut file_full = fs::File::create(file_path_full).unwrap(); + let contents = "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4\nprocessor : 1\n".to_string(); + writeln!(file_full, "{}", contents).unwrap(); + + // Empty cpuinfo example + let file_path_empty = dir.path().join("cpuinfo_empty"); + let path_empty = file_path_empty.clone(); + let mut _file_empty = fs::File::create(file_path_empty).unwrap(); + + #[derive(Debug)] + struct TestData<'a> { + cpuinfo_path: &'a str, + processor_delimiter_str: &'a str, + result: Result, + } + let tests = &[ + // Failure scenarios + TestData { + cpuinfo_path: "", + processor_delimiter_str: "", + result: Err(anyhow!("No such file or directory (os error 2)")), + }, + TestData { + cpuinfo_path: &path_empty.as_path().display().to_string(), + processor_delimiter_str: "\nprocessor", + result: Err(anyhow!(ERR_NO_CPUINFO)), + }, + // Success scenarios + TestData { + cpuinfo_path: &path_full.as_path().display().to_string(), + processor_delimiter_str: "\nprocessor", + result: Ok( + "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4" + .to_string(), + ), + }, + ]; + + for (i, d) in tests.iter().enumerate() { + let msg = format!("test[{}]: {:?}", i, d); + let result = get_single_cpu_info(d.cpuinfo_path, d.processor_delimiter_str); + let msg = format!("{}, result: {:?}", msg, result); + + if d.result.is_ok() { + assert_eq!( + result.as_ref().unwrap(), + d.result.as_ref().unwrap(), + "{}", + msg + ); + continue; + } + + let expected_error = format!("{}", d.result.as_ref().unwrap_err()); + let actual_error = format!("{}", result.unwrap_err()); + assert!(actual_error == expected_error, "{}", msg); + } + } + + #[test] + fn test_get_cpu_flags() { + let contents = "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4\nprocessor : 1\n"; + + #[derive(Debug)] + struct TestData<'a> { + cpu_info_str: &'a str, + cpu_flags_tag: &'a str, + result: Result, + } + let tests = &[ + // Failure scenarios + TestData { + cpu_info_str: "", + cpu_flags_tag: "", + result: Err(anyhow!(ERR_NO_CPUINFO)), + }, + TestData { + cpu_info_str: "", + cpu_flags_tag: "flags", + result: Err(anyhow!(ERR_NO_CPUINFO)), + }, + TestData { + cpu_info_str: contents, + cpu_flags_tag: "", + result: Err(anyhow!("cpu flags delimiter string is empty")), + }, + // Success scenarios + TestData { + cpu_info_str: contents, + cpu_flags_tag: "flags", + result: Ok(" flag_1 flag_2 flag_3 flag_4".to_string()), + }, + TestData { + cpu_info_str: contents, + cpu_flags_tag: "flags_err", + result: Ok("".to_string()), + }, + ]; + + for (i, d) in tests.iter().enumerate() { + let msg = format!("test[{}]: {:?}", i, d); + let result = get_cpu_flags(d.cpu_info_str, d.cpu_flags_tag); + let msg = format!("{}, result: {:?}", msg, result); + + if d.result.is_ok() { + assert_eq!( + result.as_ref().unwrap(), + d.result.as_ref().unwrap(), + "{}", + msg + ); + continue; + } + + let expected_error = format!("{}", d.result.as_ref().unwrap_err()); + let actual_error = format!("{}", result.unwrap_err()); + assert!(actual_error == expected_error, "{}", msg); + } + } + + #[test] + fn test_get_cpu_flags_vec() { + let contents = "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4\nprocessor : 1\n"; + + #[derive(Debug)] + struct TestData<'a> { + cpu_info_str: &'a str, + cpu_flags_tag: &'a str, + result: Result>, + } + let tests = &[ + // Failure scenarios + TestData { + cpu_info_str: "", + cpu_flags_tag: "", + result: Err(anyhow!(ERR_NO_CPUINFO)), + }, + TestData { + cpu_info_str: "", + cpu_flags_tag: "flags", + result: Err(anyhow!(ERR_NO_CPUINFO)), + }, + TestData { + cpu_info_str: contents, + cpu_flags_tag: "", + result: Err(anyhow!("cpu flags delimiter string is empty")), + }, + // Success scenarios + TestData { + cpu_info_str: contents, + cpu_flags_tag: "flags", + result: Ok(vec![ + "flag_1".to_string(), + "flag_2".to_string(), + "flag_3".to_string(), + "flag_4".to_string(), + ]), + }, + TestData { + cpu_info_str: contents, + cpu_flags_tag: "flags_err", + result: Ok(Vec::new()), + }, + ]; + + for (i, d) in tests.iter().enumerate() { + let msg = format!("test[{}]: {:?}", i, d); + let result = get_cpu_flags_vec(d.cpu_info_str, d.cpu_flags_tag); + let msg = format!("{}, result: {:?}", msg, result); + + if d.result.is_ok() { + assert_eq!( + result.as_ref().unwrap(), + d.result.as_ref().unwrap(), + "{}", + msg + ); + continue; + } + + let expected_error = format!("{}", d.result.as_ref().unwrap_err()); + let actual_error = format!("{}", result.unwrap_err()); + assert!(actual_error == expected_error, "{}", msg); + } + } + + #[test] + fn test_contains_cpu_flag() { + let flags_vec = vec![ + "flag_1".to_string(), + "flag_2".to_string(), + "flag_3".to_string(), + "flag_4".to_string(), + ]; + + #[derive(Debug)] + struct TestData<'a> { + cpu_flags_vec: &'a Vec, + cpu_flag: &'a str, + result: Result, + } + let tests = &[ + // Failure scenarios + TestData { + cpu_flags_vec: &flags_vec, + cpu_flag: "flag_5", + result: Ok(false), + }, + TestData { + cpu_flags_vec: &flags_vec, + cpu_flag: "", + result: Err(anyhow!("parameter specifying flag to look for is empty")), + }, + // Success scenarios + TestData { + cpu_flags_vec: &flags_vec, + cpu_flag: "flag_1", + result: Ok(true), + }, + ]; + + for (i, d) in tests.iter().enumerate() { + let msg = format!("test[{}]: {:?}", i, d); + let result = contains_cpu_flag(&d.cpu_flags_vec, d.cpu_flag); + let msg = format!("{}, result: {:?}", msg, result); + + if d.result.is_ok() { + assert_eq!( + result.as_ref().unwrap(), + d.result.as_ref().unwrap(), + "{}", + msg + ); + continue; + } + + let expected_error = format!("{}", d.result.as_ref().unwrap_err()); + let actual_error = format!("{}", result.unwrap_err()); + assert!(actual_error == expected_error, "{}", msg); + } + } +} diff --git a/src/libs/kata-sys-util/src/lib.rs b/src/libs/kata-sys-util/src/lib.rs index 2c90adb7c..f7dad3290 100644 --- a/src/libs/kata-sys-util/src/lib.rs +++ b/src/libs/kata-sys-util/src/lib.rs @@ -6,6 +6,7 @@ #[macro_use] extern crate slog; +pub mod cpu; pub mod device; pub mod fs; pub mod hooks; From f5d19571742b33053919e78376653ce7c125b596 Mon Sep 17 00:00:00 2001 From: Archana Shinde Date: Wed, 5 Jul 2023 18:36:15 +0530 Subject: [PATCH 2/8] kata-sys-util: Move additional functionality to cpu.rs Make certain imports architecture specific as these are not used on all architectures. Move additional constants and functionality to cpu.rs. Signed-off-by: Archana Shinde --- src/libs/kata-sys-util/src/cpu.rs | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/libs/kata-sys-util/src/cpu.rs b/src/libs/kata-sys-util/src/cpu.rs index 7e1bef38d..514ad3532 100644 --- a/src/libs/kata-sys-util/src/cpu.rs +++ b/src/libs/kata-sys-util/src/cpu.rs @@ -3,8 +3,16 @@ // SPDX-License-Identifier: Apache-2.0 // +#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] use anyhow::{anyhow, Result}; +#[cfg(target_arch = "s390x")] +use std::collections::HashMap; +#[cfg(target_arch = "s390x")] +use std::io::BufRead; +#[cfg(target_arch = "s390x")] +use std::io::BufReader; + #[allow(dead_code)] const ERR_NO_CPUINFO: &str = "cpu_info string is empty"; @@ -114,6 +122,41 @@ fn get_cpu_flags_from_file(cpu_info: &str, cpu_flags_tag: &str) -> Result Result> { + let f = std::fs::File::open(PROC_CPUINFO)?; + let mut reader = BufReader::new(f); + let mut contents = String::new(); + let facilities_field = "facilities"; + let mut facilities = HashMap::new(); + + while reader.read_line(&mut contents)? > 0 { + let fields: Vec<&str> = contents.split_whitespace().collect(); + if fields.len() < 2 { + contents.clear(); + continue; + } + + if !fields[0].starts_with(facilities_field) { + contents.clear(); + continue; + } + + let mut start = 1; + if fields[1] == ":" { + start = 2; + } + + for field in fields.iter().skip(start) { + let bit = field.parse::()?; + facilities.insert(bit, true); + } + return Ok(facilities); + } + + Ok(facilities) +} + #[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] #[cfg(test)] mod tests { From dacdf7c282ef9d3ea4068085922c199d47149ade Mon Sep 17 00:00:00 2001 From: Archana Shinde Date: Thu, 13 Jul 2023 13:48:34 +0530 Subject: [PATCH 3/8] kata-ctl: Remove cpu related functions from kata-ctl Remove cpu related functions which have been moved to kata-sys-util. Change invocations in kata-ctl to make use of functions now moved to kata-sys-util. Signed-off-by: Nathan Whyte Signed-off-by: Archana Shinde --- src/tools/kata-ctl/src/arch/s390x/mod.rs | 18 ++++---- src/tools/kata-ctl/src/arch/x86_64/mod.rs | 34 +++++---------- src/tools/kata-ctl/src/check.rs | 52 +---------------------- src/tools/kata-ctl/src/utils.rs | 3 +- 4 files changed, 23 insertions(+), 84 deletions(-) diff --git a/src/tools/kata-ctl/src/arch/s390x/mod.rs b/src/tools/kata-ctl/src/arch/s390x/mod.rs index 991d676d1..ddeb2a522 100644 --- a/src/tools/kata-ctl/src/arch/s390x/mod.rs +++ b/src/tools/kata-ctl/src/arch/s390x/mod.rs @@ -37,15 +37,17 @@ mod arch_specific { fn check_cpu() -> Result<()> { info!(sl!(), "check CPU: s390x"); - let cpu_info = check::get_single_cpu_info(check::PROC_CPUINFO, CPUINFO_DELIMITER)?; + let cpu_info = + kata_sys_util::cpu::get_single_cpu_info(check::PROC_CPUINFO, CPUINFO_DELIMITER)?; - let cpu_features = check::get_cpu_flags(&cpu_info, CPUINFO_FEATURES_TAG).map_err(|e| { - anyhow!( - "Error parsing CPU features, file {:?}, {:?}", - check::PROC_CPUINFO, - e - ) - })?; + let cpu_features = kata_sys_util::cpu::get_cpu_flags(&cpu_info, CPUINFO_FEATURES_TAG) + .map_err(|e| { + anyhow!( + "Error parsing CPU features, file {:?}, {:?}", + check::PROC_CPUINFO, + e + ) + })?; let missing_cpu_features = check::check_cpu_flags(&cpu_features, CPU_FEATURES_REQ)?; if !missing_cpu_features.is_empty() { diff --git a/src/tools/kata-ctl/src/arch/x86_64/mod.rs b/src/tools/kata-ctl/src/arch/x86_64/mod.rs index 67fd4ddc8..261baf645 100644 --- a/src/tools/kata-ctl/src/arch/x86_64/mod.rs +++ b/src/tools/kata-ctl/src/arch/x86_64/mod.rs @@ -93,15 +93,17 @@ mod arch_specific { fn check_cpu(_args: &str) -> Result<()> { info!(sl!(), "check CPU: x86_64"); - let cpu_info = check::get_single_cpu_info(check::PROC_CPUINFO, CPUINFO_DELIMITER)?; + let cpu_info = + kata_sys_util::cpu::get_single_cpu_info(check::PROC_CPUINFO, CPUINFO_DELIMITER)?; - let cpu_flags = check::get_cpu_flags(&cpu_info, CPUINFO_FLAGS_TAG).map_err(|e| { - anyhow!( - "Error parsing CPU flags, file {:?}, {:?}", - check::PROC_CPUINFO, - e - ) - })?; + let cpu_flags = + kata_sys_util::cpu::get_cpu_flags(&cpu_info, CPUINFO_FLAGS_TAG).map_err(|e| { + anyhow!( + "Error parsing CPU flags, file {:?}, {:?}", + check::PROC_CPUINFO, + e + ) + })?; // perform checks // TODO: Perform checks based on hypervisor type @@ -118,20 +120,6 @@ mod arch_specific { Ok(()) } - fn retrieve_cpu_flags() -> Result { - let cpu_info = check::get_single_cpu_info(check::PROC_CPUINFO, CPUINFO_DELIMITER)?; - - let cpu_flags = check::get_cpu_flags(&cpu_info, CPUINFO_FLAGS_TAG).map_err(|e| { - anyhow!( - "Error parsing CPU flags, file {:?}, {:?}", - check::PROC_CPUINFO, - e - ) - })?; - - Ok(cpu_flags) - } - pub fn get_cpu_details() -> Result<(String, String)> { utils::get_generic_cpu_details(check::PROC_CPUINFO) } @@ -206,7 +194,7 @@ mod arch_specific { } fn running_on_vmm() -> Result { - match check::get_single_cpu_info(check::PROC_CPUINFO, CPUINFO_DELIMITER) { + match kata_sys_util::cpu::get_single_cpu_info(check::PROC_CPUINFO, CPUINFO_DELIMITER) { Ok(cpu_info) => { // check if the 'hypervisor' flag exist in the cpu features let missing_hypervisor_flag = check::check_cpu_attribs(&cpu_info, VMM_FLAGS)?; diff --git a/src/tools/kata-ctl/src/check.rs b/src/tools/kata-ctl/src/check.rs index 78e30a93a..e410a18ba 100644 --- a/src/tools/kata-ctl/src/check.rs +++ b/src/tools/kata-ctl/src/check.rs @@ -61,57 +61,6 @@ macro_rules! sl { }; } -fn read_file_contents(file_path: &str) -> Result { - let contents = std::fs::read_to_string(file_path)?; - Ok(contents) -} - -// get_single_cpu_info returns the contents of the first cpu from -// the specified cpuinfo file by parsing based on a specified delimiter -pub fn get_single_cpu_info(cpu_info_file: &str, substring: &str) -> Result { - let contents = read_file_contents(cpu_info_file)?; - - if contents.is_empty() { - return Err(anyhow!(ERR_NO_CPUINFO)); - } - - let subcontents: Vec<&str> = contents.split(substring).collect(); - let result = subcontents - .first() - .ok_or("error splitting contents of cpuinfo") - .map_err(|e| anyhow!(e))? - .to_string(); - Ok(result) -} - -// get_cpu_flags returns a string of cpu flags from cpuinfo, passed in -// as a string -#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] -pub fn get_cpu_flags(cpu_info: &str, cpu_flags_tag: &str) -> Result { - if cpu_info.is_empty() { - return Err(anyhow!(ERR_NO_CPUINFO)); - } - - if cpu_flags_tag.is_empty() { - return Err(anyhow!("cpu flags delimiter string is empty"))?; - } - - let subcontents: Vec<&str> = cpu_info.split('\n').collect(); - for line in subcontents { - if line.starts_with(cpu_flags_tag) { - let line_data: Vec<&str> = line.split(':').collect(); - let flags = line_data - .last() - .ok_or("error splitting flags in cpuinfo") - .map_err(|e| anyhow!(e))? - .to_string(); - return Ok(flags); - } - } - - Ok("".to_string()) -} - // get_missing_strings searches for required (strings) in data and returns // a vector containing the missing strings #[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] @@ -397,6 +346,7 @@ mod tests { use super::*; #[cfg(any(target_arch = "x86_64"))] use crate::types::{KernelModule, KernelParam, KernelParamType}; + use kata_sys_util::cpu::{get_cpu_flags, get_single_cpu_info}; use semver::Version; use slog::warn; use std::fs; diff --git a/src/tools/kata-ctl/src/utils.rs b/src/tools/kata-ctl/src/utils.rs index a292d3b78..3b6e4daee 100644 --- a/src/tools/kata-ctl/src/utils.rs +++ b/src/tools/kata-ctl/src/utils.rs @@ -6,7 +6,6 @@ #![allow(dead_code)] use crate::arch::arch_specific; -use crate::check::get_single_cpu_info; use anyhow::{anyhow, Context, Result}; use std::fs; @@ -106,7 +105,7 @@ pub fn get_distro_details(os_release: &str, os_release_clr: &str) -> Result<(Str #[cfg(any(target_arch = "s390x", target_arch = "x86_64", target_arch = "aarch64"))] pub fn get_generic_cpu_details(cpu_info_file: &str) -> Result<(String, String)> { - let cpu_info = get_single_cpu_info(cpu_info_file, "\n\n")?; + let cpu_info = kata_sys_util::cpu::get_single_cpu_info(cpu_info_file, "\n\n")?; let lines = cpu_info.lines(); let mut vendor = String::new(); let mut model = String::new(); From a24dbdc781d6c9c880c1907d99f24981e7ade7d5 Mon Sep 17 00:00:00 2001 From: Archana Shinde Date: Wed, 5 Jul 2023 19:08:18 +0530 Subject: [PATCH 4/8] kata-sys-util: Move utilities to get platform protection Add utilities to get platform protection to kata-sys-util Fixes: #7144 Signed-off-by: Archana Shinde --- src/libs/kata-sys-util/src/lib.rs | 43 ++++ src/libs/kata-sys-util/src/protection.rs | 270 +++++++++++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 src/libs/kata-sys-util/src/protection.rs diff --git a/src/libs/kata-sys-util/src/lib.rs b/src/libs/kata-sys-util/src/lib.rs index f7dad3290..e91704b0b 100644 --- a/src/libs/kata-sys-util/src/lib.rs +++ b/src/libs/kata-sys-util/src/lib.rs @@ -13,10 +13,15 @@ pub mod hooks; pub mod k8s; pub mod mount; pub mod numa; +pub mod protection; pub mod rand; pub mod spec; pub mod validate; +use anyhow::Result; +use std::io::BufRead; +use std::io::BufReader; + // Convenience macro to obtain the scoped logger #[macro_export] macro_rules! sl { @@ -32,3 +37,41 @@ macro_rules! eother { std::io::Error::new(std::io::ErrorKind::Other, format!($fmt, $($arg)*)) }) } + +pub fn check_kernel_cmd_line( + kernel_cmdline_path: &str, + search_param: &str, + search_values: &[&str], +) -> Result { + let f = std::fs::File::open(kernel_cmdline_path)?; + let reader = BufReader::new(f); + + let check_fn = if search_values.is_empty() { + |param: &str, search_param: &str, _search_values: &[&str]| { + return param.eq_ignore_ascii_case(search_param); + } + } else { + |param: &str, search_param: &str, search_values: &[&str]| { + let split: Vec<&str> = param.splitn(2, "=").collect(); + if split.len() < 2 || split[0] != search_param { + return false; + } + + for value in search_values { + if value.eq_ignore_ascii_case(split[1]) { + return true; + } + } + false + } + }; + + for line in reader.lines() { + for field in line?.split_whitespace() { + if check_fn(field, search_param, search_values) { + return Ok(true); + } + } + } + Ok(false) +} diff --git a/src/libs/kata-sys-util/src/protection.rs b/src/libs/kata-sys-util/src/protection.rs new file mode 100644 index 000000000..02b356d33 --- /dev/null +++ b/src/libs/kata-sys-util/src/protection.rs @@ -0,0 +1,270 @@ +// Copyright (c) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +#[cfg(target_arch = "x86_64")] +use anyhow::anyhow; +#[cfg(any(target_arch = "s390x", target_arch = "x86_64", target_arch = "aarch64"))] +use anyhow::Result; +use std::fmt; +#[cfg(target_arch = "x86_64")] +use std::path::Path; +use thiserror::Error; + +#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] +use nix::unistd::Uid; + +#[cfg(target_arch = "x86_64")] +use std::fs; + +#[allow(dead_code)] +#[derive(Debug, PartialEq)] +pub enum GuestProtection { + NoProtection, + Tdx, + Sev, + Snp, + Pef, + Se, +} + +impl fmt::Display for GuestProtection { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + GuestProtection::Tdx => write!(f, "tdx"), + GuestProtection::Sev => write!(f, "sev"), + GuestProtection::Snp => write!(f, "snp"), + GuestProtection::Pef => write!(f, "pef"), + GuestProtection::Se => write!(f, "se"), + GuestProtection::NoProtection => write!(f, "none"), + } + } +} + +#[allow(dead_code)] +#[derive(Error, Debug)] +pub enum ProtectionError { + #[error("No permission to check guest protection")] + NoPerms, + + #[error("Failed to check guest protection: {0}")] + CheckFailed(String), + + #[error("Invalid guest protection value: {0}")] + InvalidValue(String), +} + +#[cfg(target_arch = "x86_64")] +pub const TDX_SYS_FIRMWARE_DIR: &str = "/sys/firmware/tdx_seam/"; +#[cfg(target_arch = "x86_64")] +pub const TDX_CPU_FLAG: &str = "tdx"; +#[cfg(target_arch = "x86_64")] +pub const SEV_KVM_PARAMETER_PATH: &str = "/sys/module/kvm_amd/parameters/sev"; +#[cfg(target_arch = "x86_64")] +pub const SNP_KVM_PARAMETER_PATH: &str = "/sys/module/kvm_amd/parameters/sev_snp"; + +#[cfg(target_arch = "x86_64")] +pub fn available_guest_protection() -> Result { + if !Uid::effective().is_root() { + return Err(ProtectionError::NoPerms); + } + + arch_guest_protection( + TDX_SYS_FIRMWARE_DIR, + TDX_CPU_FLAG, + SEV_KVM_PARAMETER_PATH, + SNP_KVM_PARAMETER_PATH, + ) +} + +#[cfg(target_arch = "x86_64")] +fn retrieve_cpu_flags() -> Result { + let cpu_info = + crate::cpu::get_single_cpu_info(crate::cpu::PROC_CPUINFO, crate::cpu::CPUINFO_DELIMITER)?; + + let cpu_flags = + crate::cpu::get_cpu_flags(&cpu_info, crate::cpu::CPUINFO_FLAGS_TAG).map_err(|e| { + anyhow!( + "Error parsing CPU flags, file {:?}, {:?}", + crate::cpu::PROC_CPUINFO, + e + ) + })?; + + Ok(cpu_flags) +} + +#[cfg(target_arch = "x86_64")] +pub fn arch_guest_protection( + tdx_path: &str, + tdx_flag: &str, + sev_path: &str, + snp_path: &str, +) -> Result { + let flags = + retrieve_cpu_flags().map_err(|err| ProtectionError::CheckFailed(err.to_string()))?; + + let metadata = fs::metadata(tdx_path); + + if metadata.is_ok() && metadata.unwrap().is_dir() && flags.contains(tdx_flag) { + return Ok(GuestProtection::Tdx); + } + + let check_contents = |file_name: &str| -> Result { + let file_path = Path::new(file_name); + if !file_path.exists() { + return Ok(false); + } + + let contents = fs::read_to_string(file_name).map_err(|err| { + ProtectionError::CheckFailed(format!("Error reading file {} : {}", file_name, err)) + })?; + + if contents.trim() == "Y" { + return Ok(true); + } + Ok(false) + }; + + if check_contents(snp_path)? { + return Ok(GuestProtection::Snp); + } + + if check_contents(sev_path)? { + return Ok(GuestProtection::Sev); + } + + Ok(GuestProtection::NoProtection) +} + +#[cfg(target_arch = "s390x")] +#[allow(dead_code)] +// Guest protection is not supported on ARM64. +pub fn available_guest_protection() -> Result { + if !Uid::effective().is_root() { + return Err(ProtectionError::NoPerms)?; + } + + let facilities = crate::cpu::retrieve_cpu_facilities().map_err(|err| { + ProtectionError::CheckFailed(format!( + "Error retrieving cpu facilities file : {}", + err.to_string() + )) + })?; + + // Secure Execution + // https://www.kernel.org/doc/html/latest/virt/kvm/s390-pv.html + let se_cpu_facility_bit: i32 = 158; + if !facilities.contains_key(&se_cpu_facility_bit) { + return Ok(GuestProtection::NoProtection); + } + + let cmd_line_values = vec!["1", "on", "y", "yes"]; + let se_cmdline_param = "prot_virt"; + + let se_cmdline_present = + crate::check_kernel_cmd_line("/proc/cmdline", se_cmdline_param, &cmd_line_values) + .map_err(|err| ProtectionError::CheckFailed(err.to_string()))?; + + if !se_cmdline_present { + return Err(ProtectionError::InvalidValue(String::from( + "Protected Virtualization is not enabled on kernel command line!", + ))); + } + + Ok(GuestProtection::Se) +} + +#[cfg(target_arch = "powerpc64le")] +pub fn available_guest_protection() -> Result { + if !Uid::effective().is_root() { + return Err(check::ProtectionError::NoPerms); + } + + let metadata = fs::metadata(PEF_SYS_FIRMWARE_DIR); + if metadata.is_ok() && metadata.unwrap().is_dir() { + Ok(check::GuestProtection::Pef) + } + + Ok(check::GuestProtection::NoProtection) +} + +#[cfg(target_arch = "aarch64")] +#[allow(dead_code)] +// Guest protection is not supported on ARM64. +pub fn available_guest_protection() -> Result { + Ok(GuestProtection::NoProtection) +} + +#[cfg(target_arch = "x86_64")] +#[cfg(test)] +mod tests { + use super::*; + use nix::unistd::Uid; + use std::fs; + use std::io::Write; + use tempfile::tempdir; + + #[test] + fn test_available_guest_protection_no_privileges() { + if !Uid::effective().is_root() { + let res = available_guest_protection(); + assert!(res.is_err()); + assert_eq!( + "No permission to check guest protection", + res.unwrap_err().to_string() + ); + } + } + + #[test] + fn test_arch_guest_protection_snp() { + // Test snp + let dir = tempdir().unwrap(); + let snp_file_path = dir.path().join("sev_snp"); + let path = snp_file_path.clone(); + let mut snp_file = fs::File::create(snp_file_path).unwrap(); + writeln!(snp_file, "Y").unwrap(); + + let actual = + arch_guest_protection("/xyz/tmp", TDX_CPU_FLAG, "/xyz/tmp", path.to_str().unwrap()); + assert!(actual.is_ok()); + assert_eq!(actual.unwrap(), GuestProtection::Snp); + + writeln!(snp_file, "N").unwrap(); + let actual = + arch_guest_protection("/xyz/tmp", TDX_CPU_FLAG, "/xyz/tmp", path.to_str().unwrap()); + assert!(actual.is_ok()); + assert_eq!(actual.unwrap(), GuestProtection::NoProtection); + } + + #[test] + fn test_arch_guest_protection_sev() { + // Test sev + let dir = tempdir().unwrap(); + let sev_file_path = dir.path().join("sev"); + let sev_path = sev_file_path.clone(); + let mut sev_file = fs::File::create(sev_file_path).unwrap(); + writeln!(sev_file, "Y").unwrap(); + + let actual = arch_guest_protection( + "/xyz/tmp", + TDX_CPU_FLAG, + sev_path.to_str().unwrap(), + "/xyz/tmp", + ); + assert!(actual.is_ok()); + assert_eq!(actual.unwrap(), GuestProtection::Sev); + + writeln!(sev_file, "N").unwrap(); + let actual = arch_guest_protection( + "/xyz/tmp", + TDX_CPU_FLAG, + sev_path.to_str().unwrap(), + "/xyz/tmp", + ); + assert!(actual.is_ok()); + assert_eq!(actual.unwrap(), GuestProtection::NoProtection); + } +} From 61e4032b08c6625fe7ddc3e36444e067974832e9 Mon Sep 17 00:00:00 2001 From: Archana Shinde Date: Wed, 5 Jul 2023 20:10:59 +0530 Subject: [PATCH 5/8] kata-ctl: Remove all utility functions to get platform protection Since these have been added to kata-sys-util, remove these from kata-ctl. Change all invocations to get platform protection to make use of kata-sys-util. Fixes: #7144 Signed-off-by: Archana Shinde --- src/libs/Cargo.lock | 119 ++++++++++- src/tools/kata-ctl/Cargo.lock | 197 +++++++++++++++++- src/tools/kata-ctl/Cargo.toml | 1 + src/tools/kata-ctl/src/arch/aarch64/mod.rs | 6 - .../kata-ctl/src/arch/powerpc64le/mod.rs | 18 -- src/tools/kata-ctl/src/arch/s390x/mod.rs | 74 ------- src/tools/kata-ctl/src/arch/x86_64/mod.rs | 135 ------------ src/tools/kata-ctl/src/check.rs | 39 ---- src/tools/kata-ctl/src/ops/env_ops.rs | 3 +- 9 files changed, 312 insertions(+), 280 deletions(-) diff --git a/src/libs/Cargo.lock b/src/libs/Cargo.lock index 5da16917f..e8cd3e39a 100644 --- a/src/libs/Cargo.lock +++ b/src/libs/Cargo.lock @@ -34,6 +34,17 @@ dependencies = [ "syn", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -130,7 +141,7 @@ dependencies = [ "js-sys", "num-integer", "num-traits", - "time", + "time 0.1.43", "wasm-bindgen", "winapi", ] @@ -172,6 +183,27 @@ dependencies = [ "syn", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "either" version = "1.6.1" @@ -472,6 +504,7 @@ dependencies = [ name = "kata-sys-util" version = "0.1.0" dependencies = [ + "anyhow", "byteorder", "cgroups-rs", "chrono", @@ -559,6 +592,7 @@ dependencies = [ "slog-async", "slog-json", "slog-scope", + "slog-term", "tempfile", ] @@ -681,6 +715,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "oci" version = "0.1.0" @@ -950,13 +993,24 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + [[package]] name = "regex" version = "1.6.0" @@ -983,6 +1037,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + [[package]] name = "ryu" version = "1.0.9" @@ -1113,6 +1173,19 @@ dependencies = [ "slog", ] +[[package]] +name = "slog-term" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d29185c55b7b258b4f120eab00f48557d4d9bc814f41713f449d35b0f8977c" +dependencies = [ + "atty", + "slog", + "term", + "thread_local", + "time 0.3.22", +] + [[package]] name = "smallvec" version = "1.8.0" @@ -1170,6 +1243,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "test-utils" version = "0.1.0" @@ -1216,6 +1300,35 @@ dependencies = [ "winapi", ] +[[package]] +name = "time" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +dependencies = [ + "itoa", + "libc", + "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + [[package]] name = "tokio" version = "1.17.0" diff --git a/src/tools/kata-ctl/Cargo.lock b/src/tools/kata-ctl/Cargo.lock index c7586b6d4..c37d17b5e 100644 --- a/src/tools/kata-ctl/Cargo.lock +++ b/src/tools/kata-ctl/Cargo.lock @@ -33,6 +33,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.2.6" @@ -187,6 +202,34 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cgroups-rs" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b098e7c3a70d03c288fa0a96ccf13e770eb3d78c4cc0e1549b3c13215d5f965" +dependencies = [ + "libc", + "log", + "nix 0.25.1", + "regex", + "thiserror", +] + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + [[package]] name = "clap" version = "4.2.1" @@ -230,6 +273,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +[[package]] +name = "common-path" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" + [[package]] name = "concolor-override" version = "1.0.0" @@ -358,6 +407,17 @@ dependencies = [ "libc", ] +[[package]] +name = "fail" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5e43d0f78a42ad591453aedb1d7ae631ce7ee445c7643691055a9ed8d3b01c" +dependencies = [ + "log", + "once_cell", + "rand", +] + [[package]] name = "fastrand" version = "1.8.0" @@ -500,7 +560,7 @@ checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -676,6 +736,29 @@ dependencies = [ "tokio", ] +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.3.0" @@ -778,6 +861,7 @@ dependencies = [ "epoll", "futures", "hyper", + "kata-sys-util", "kata-types", "libc", "logging", @@ -805,6 +889,30 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "kata-sys-util" +version = "0.1.0" +dependencies = [ + "anyhow", + "byteorder", + "cgroups-rs", + "chrono", + "common-path", + "fail", + "kata-types", + "lazy_static", + "libc", + "nix 0.24.3", + "oci", + "once_cell", + "rand", + "serde_json", + "slog", + "slog-scope", + "subprocess", + "thiserror", +] + [[package]] name = "kata-types" version = "0.1.0" @@ -923,7 +1031,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.42.0", ] @@ -1004,6 +1112,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -1163,6 +1280,12 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "privdrop" version = "0.5.3" @@ -1319,6 +1442,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -1641,7 +1794,7 @@ dependencies = [ "serde", "serde_json", "slog", - "time", + "time 0.3.17", ] [[package]] @@ -1665,7 +1818,7 @@ dependencies = [ "slog", "term", "thread_local", - "time", + "time 0.3.17", ] [[package]] @@ -1721,6 +1874,16 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "subprocess" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "syn" version = "1.0.107" @@ -1821,6 +1984,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "time" version = "0.3.17" @@ -2114,6 +2288,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2248,6 +2428,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-sys" version = "0.42.0" diff --git a/src/tools/kata-ctl/Cargo.toml b/src/tools/kata-ctl/Cargo.toml index a0ba95adb..2c4582432 100644 --- a/src/tools/kata-ctl/Cargo.toml +++ b/src/tools/kata-ctl/Cargo.toml @@ -30,6 +30,7 @@ sys-info = "0.9.1" shim-interface = { path = "../../libs/shim-interface"} kata-types = { path = "../../libs/kata-types" } +kata-sys-util = { path = "../../../src/libs/kata-sys-util/" } safe-path = { path = "../../libs/safe-path" } agent = { path = "../../runtime-rs/crates/agent"} serial_test = "0.5.1" diff --git a/src/tools/kata-ctl/src/arch/aarch64/mod.rs b/src/tools/kata-ctl/src/arch/aarch64/mod.rs index 41d28f8db..01cd4828a 100644 --- a/src/tools/kata-ctl/src/arch/aarch64/mod.rs +++ b/src/tools/kata-ctl/src/arch/aarch64/mod.rs @@ -84,10 +84,4 @@ mod arch_specific { // TODO: Not implemented Ok(true) } - - #[allow(dead_code)] - // Guest protection is not supported on ARM64. - pub fn available_guest_protection() -> Result { - Ok(check::GuestProtection::NoProtection) - } } diff --git a/src/tools/kata-ctl/src/arch/powerpc64le/mod.rs b/src/tools/kata-ctl/src/arch/powerpc64le/mod.rs index 436d5a4d2..bf5822c04 100644 --- a/src/tools/kata-ctl/src/arch/powerpc64le/mod.rs +++ b/src/tools/kata-ctl/src/arch/powerpc64le/mod.rs @@ -32,22 +32,4 @@ mod arch_specific { // to get cpu details specific to powerpc architecture similar // to the goloang implementation of function getCPUDetails() } - - pub fn host_is_vmcontainer_capable() -> Result { - // TODO: Not implemented - Ok(true) - } - - pub fn available_guest_protection() -> Result { - if !Uid::effective().is_root() { - return Err(check::ProtectionError::NoPerms); - } - - let metadata = fs::metadata(PEF_SYS_FIRMWARE_DIR); - if metadata.is_ok() && metadata.unwrap().is_dir() { - Ok(check::GuestProtection::Pef) - } - - Ok(check::GuestProtection::NoProtection) - } } diff --git a/src/tools/kata-ctl/src/arch/s390x/mod.rs b/src/tools/kata-ctl/src/arch/s390x/mod.rs index ddeb2a522..6f3ee1f4e 100644 --- a/src/tools/kata-ctl/src/arch/s390x/mod.rs +++ b/src/tools/kata-ctl/src/arch/s390x/mod.rs @@ -12,9 +12,7 @@ mod arch_specific { use crate::types::*; use crate::utils; use anyhow::{anyhow, Result}; - use nix::unistd::Uid; use slog::{info, o, warn}; - use std::collections::HashMap; use std::io::BufRead; use std::io::BufReader; @@ -95,41 +93,6 @@ mod arch_specific { Err(anyhow!("System is not capable of running a VM")) } - #[allow(dead_code)] - fn retrieve_cpu_facilities() -> Result> { - let f = std::fs::File::open(check::PROC_CPUINFO)?; - let mut reader = BufReader::new(f); - let mut contents = String::new(); - let facilities_field = "facilities"; - let mut facilities = HashMap::new(); - - while reader.read_line(&mut contents)? > 0 { - let fields: Vec<&str> = contents.split_whitespace().collect(); - if fields.len() < 2 { - contents.clear(); - continue; - } - - if !fields[0].starts_with(facilities_field) { - contents.clear(); - continue; - } - - let mut start = 1; - if fields[1] == ":" { - start = 2; - } - - for field in fields.iter().skip(start) { - let bit = field.parse::()?; - facilities.insert(bit, true); - } - return Ok(facilities); - } - - Ok(facilities) - } - #[allow(dead_code)] pub fn check_cmd_line( kernel_cmdline_path: &str, @@ -176,41 +139,4 @@ mod arch_specific { // to get cpu details specific to s390x architecture similar // to the goloang implementation of function getS390xCPUDetails() } - - #[allow(dead_code)] - // Guest protection is not supported on ARM64. - pub fn available_guest_protection() -> Result { - if !Uid::effective().is_root() { - return Err(check::ProtectionError::NoPerms)?; - } - - let facilities = retrieve_cpu_facilities().map_err(|err| { - check::ProtectionError::CheckFailed(format!( - "Error retrieving cpu facilities file : {}", - err.to_string() - )) - })?; - - // Secure Execution - // https://www.kernel.org/doc/html/latest/virt/kvm/s390-pv.html - let se_cpu_facility_bit: i32 = 158; - if !facilities.contains_key(&se_cpu_facility_bit) { - return Ok(check::GuestProtection::NoProtection); - } - - let cmd_line_values = vec!["1", "on", "y", "yes"]; - let se_cmdline_param = "prot_virt"; - - let se_cmdline_present = - check_cmd_line("/proc/cmdline", se_cmdline_param, &cmd_line_values) - .map_err(|err| check::ProtectionError::CheckFailed(err.to_string()))?; - - if !se_cmdline_present { - return Err(check::ProtectionError::InvalidValue(String::from( - "Protected Virtualization is not enabled on kernel command line!", - ))); - } - - Ok(check::GuestProtection::Se) - } } diff --git a/src/tools/kata-ctl/src/arch/x86_64/mod.rs b/src/tools/kata-ctl/src/arch/x86_64/mod.rs index 261baf645..3d6df8ed7 100644 --- a/src/tools/kata-ctl/src/arch/x86_64/mod.rs +++ b/src/tools/kata-ctl/src/arch/x86_64/mod.rs @@ -10,14 +10,10 @@ pub use arch_specific::*; mod arch_specific { use crate::check; - use crate::check::{GuestProtection, ProtectionError}; use crate::types::*; use crate::utils; use anyhow::{anyhow, Context, Result}; - use nix::unistd::Uid; use slog::{info, o, warn}; - use std::fs; - use std::path::Path; const CPUINFO_DELIMITER: &str = "\nprocessor"; const CPUINFO_FLAGS_TAG: &str = "flags"; @@ -133,66 +129,6 @@ mod arch_specific { result.context("KVM check failed") } - pub const TDX_SYS_FIRMWARE_DIR: &str = "/sys/firmware/tdx_seam/"; - pub const TDX_CPU_FLAG: &str = "tdx"; - pub const SEV_KVM_PARAMETER_PATH: &str = "/sys/module/kvm_amd/parameters/sev"; - pub const SNP_KVM_PARAMETER_PATH: &str = "/sys/module/kvm_amd/parameters/sev_snp"; - - pub fn available_guest_protection() -> Result { - if !Uid::effective().is_root() { - return Err(ProtectionError::NoPerms); - } - - arch_guest_protection( - TDX_SYS_FIRMWARE_DIR, - TDX_CPU_FLAG, - SEV_KVM_PARAMETER_PATH, - SNP_KVM_PARAMETER_PATH, - ) - } - - pub fn arch_guest_protection( - tdx_path: &str, - tdx_flag: &str, - sev_path: &str, - snp_path: &str, - ) -> Result { - let flags = - retrieve_cpu_flags().map_err(|err| ProtectionError::CheckFailed(err.to_string()))?; - - let metadata = fs::metadata(tdx_path); - - if metadata.is_ok() && metadata.unwrap().is_dir() && flags.contains(tdx_flag) { - return Ok(GuestProtection::Tdx); - } - - let check_contents = |file_name: &str| -> Result { - let file_path = Path::new(file_name); - if !file_path.exists() { - return Ok(false); - } - - let contents = fs::read_to_string(file_name).map_err(|err| { - ProtectionError::CheckFailed(format!("Error reading file {} : {}", file_name, err)) - })?; - - if contents == "Y" { - return Ok(true); - } - Ok(false) - }; - - if check_contents(snp_path)? { - return Ok(GuestProtection::Snp); - } - - if check_contents(sev_path)? { - return Ok(GuestProtection::Sev); - } - - Ok(GuestProtection::NoProtection) - } - fn running_on_vmm() -> Result { match kata_sys_util::cpu::get_single_cpu_info(check::PROC_CPUINFO, CPUINFO_DELIMITER) { Ok(cpu_info) => { @@ -345,74 +281,3 @@ mod arch_specific { Err(anyhow!("System is not capable of running a VM")) } } - -#[cfg(target_arch = "x86_64")] -#[cfg(test)] -mod tests { - use super::*; - use crate::check; - use nix::unistd::Uid; - use std::fs; - use std::io::Write; - use tempfile::tempdir; - - #[test] - fn test_available_guest_protection_no_privileges() { - if !Uid::effective().is_root() { - let res = available_guest_protection(); - assert!(res.is_err()); - assert_eq!( - "No permission to check guest protection", - res.unwrap_err().to_string() - ); - } - } - - fn test_arch_guest_protection_snp() { - // Test snp - let dir = tempdir().unwrap(); - let snp_file_path = dir.path().join("sev_snp"); - let path = snp_file_path.clone(); - let mut snp_file = fs::File::create(snp_file_path).unwrap(); - writeln!(snp_file, "Y").unwrap(); - - let actual = - arch_guest_protection("/xyz/tmp", TDX_CPU_FLAG, "/xyz/tmp", path.to_str().unwrap()); - assert!(actual.is_ok()); - assert_eq!(actual.unwrap(), check::GuestProtection::Snp); - - writeln!(snp_file, "N").unwrap(); - let actual = - arch_guest_protection("/xyz/tmp", TDX_CPU_FLAG, "/xyz/tmp", path.to_str().unwrap()); - assert!(actual.is_ok()); - assert_eq!(actual.unwrap(), check::GuestProtection::NoProtection); - } - - fn test_arch_guest_protection_sev() { - // Test sev - let dir = tempdir().unwrap(); - let sev_file_path = dir.path().join("sev"); - let sev_path = sev_file_path.clone(); - let mut sev_file = fs::File::create(sev_file_path).unwrap(); - writeln!(sev_file, "Y").unwrap(); - - let actual = arch_guest_protection( - "/xyz/tmp", - TDX_CPU_FLAG, - sev_path.to_str().unwrap(), - "/xyz/tmp", - ); - assert!(actual.is_ok()); - assert_eq!(actual.unwrap(), check::GuestProtection::Sev); - - writeln!(sev_file, "N").unwrap(); - let actual = arch_guest_protection( - "/xyz/tmp", - TDX_CPU_FLAG, - sev_path.to_str().unwrap(), - "/xyz/tmp", - ); - assert!(actual.is_ok()); - assert_eq!(actual.unwrap(), check::GuestProtection::NoProtection); - } -} diff --git a/src/tools/kata-ctl/src/check.rs b/src/tools/kata-ctl/src/check.rs index e410a18ba..3bf899ad2 100644 --- a/src/tools/kata-ctl/src/check.rs +++ b/src/tools/kata-ctl/src/check.rs @@ -16,8 +16,6 @@ use nix::{ioctl_write_int_bad, request_code_none}; use reqwest::header::{CONTENT_TYPE, USER_AGENT}; use serde::{Deserialize, Serialize}; use slog::{info, o}; -use std::fmt; -use thiserror::Error; #[cfg(any(target_arch = "x86_64"))] use std::process::{Command, Stdio}; @@ -98,43 +96,6 @@ pub fn check_cpu_attribs( Ok(missing_attribs) } -#[allow(dead_code)] -#[derive(Debug, PartialEq)] -pub enum GuestProtection { - NoProtection, - Tdx, - Sev, - Snp, - Pef, - Se, -} - -impl fmt::Display for GuestProtection { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - GuestProtection::Tdx => write!(f, "tdx"), - GuestProtection::Sev => write!(f, "sev"), - GuestProtection::Snp => write!(f, "snp"), - GuestProtection::Pef => write!(f, "pef"), - GuestProtection::Se => write!(f, "se"), - GuestProtection::NoProtection => write!(f, "none"), - } - } -} - -#[allow(dead_code)] -#[derive(Error, Debug)] -pub enum ProtectionError { - #[error("No permission to check guest protection")] - NoPerms, - - #[error("Failed to check guest protection: {0}")] - CheckFailed(String), - - #[error("Invalid guest protection value: {0}")] - InvalidValue(String), -} - pub fn run_network_checks() -> Result<()> { Ok(()) } diff --git a/src/tools/kata-ctl/src/ops/env_ops.rs b/src/tools/kata-ctl/src/ops/env_ops.rs index d687f9617..d28d569da 100644 --- a/src/tools/kata-ctl/src/ops/env_ops.rs +++ b/src/tools/kata-ctl/src/ops/env_ops.rs @@ -9,6 +9,7 @@ use crate::arch::arch_specific; use crate::args::EnvArgument; use crate::ops::version; use crate::utils; +use kata_sys_util::protection; use kata_types::config::TomlConfig; use anyhow::{anyhow, Context, Result}; @@ -251,7 +252,7 @@ fn get_host_info() -> Result { let memory_info = get_memory_info()?; let guest_protection = - arch_specific::available_guest_protection().map_err(|e| anyhow!(e.to_string()))?; + protection::available_guest_protection().map_err(|e| anyhow!(e.to_string()))?; let guest_protection = guest_protection.to_string(); From 98242068200156e629c8caaaf332ec3e9f48820b Mon Sep 17 00:00:00 2001 From: Archana Shinde Date: Thu, 13 Jul 2023 12:10:45 +0530 Subject: [PATCH 6/8] agent: Make the static checks pass for agent The static checks for the agent require Cargo.lock to be updated. Signed-off-by: Archana Shinde --- src/agent/Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index 8d2af0519..b1cb32d46 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -860,6 +860,7 @@ dependencies = [ name = "kata-sys-util" version = "0.1.0" dependencies = [ + "anyhow", "byteorder", "cgroups-rs", "chrono", From 02d99caf6d21efd12c1a855f7a31ebf1305af1ab Mon Sep 17 00:00:00 2001 From: Archana Shinde Date: Thu, 13 Jul 2023 18:42:59 +0530 Subject: [PATCH 7/8] static-checks: Make cargo clippy pass. Get rid of cargo clippy warnings. Signed-off-by: Archana Shinde --- src/libs/kata-sys-util/src/cpu.rs | 2 +- src/libs/kata-sys-util/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/kata-sys-util/src/cpu.rs b/src/libs/kata-sys-util/src/cpu.rs index 514ad3532..938f5be42 100644 --- a/src/libs/kata-sys-util/src/cpu.rs +++ b/src/libs/kata-sys-util/src/cpu.rs @@ -394,7 +394,7 @@ mod tests { for (i, d) in tests.iter().enumerate() { let msg = format!("test[{}]: {:?}", i, d); - let result = contains_cpu_flag(&d.cpu_flags_vec, d.cpu_flag); + let result = contains_cpu_flag(d.cpu_flags_vec, d.cpu_flag); let msg = format!("{}, result: {:?}", msg, result); if d.result.is_ok() { diff --git a/src/libs/kata-sys-util/src/lib.rs b/src/libs/kata-sys-util/src/lib.rs index e91704b0b..531883a39 100644 --- a/src/libs/kata-sys-util/src/lib.rs +++ b/src/libs/kata-sys-util/src/lib.rs @@ -48,11 +48,11 @@ pub fn check_kernel_cmd_line( let check_fn = if search_values.is_empty() { |param: &str, search_param: &str, _search_values: &[&str]| { - return param.eq_ignore_ascii_case(search_param); + param.eq_ignore_ascii_case(search_param) } } else { |param: &str, search_param: &str, search_values: &[&str]| { - let split: Vec<&str> = param.splitn(2, "=").collect(); + let split: Vec<&str> = param.splitn(2, '=').collect(); if split.len() < 2 || split[0] != search_param { return false; } From 62080f83cb5dba1a5816aee33ef2e2f7e4bf71f6 Mon Sep 17 00:00:00 2001 From: Archana Shinde Date: Thu, 13 Jul 2023 20:09:43 +0530 Subject: [PATCH 8/8] kata-sys-util: Fix compilation errors Fix compilation errors for aarch64 and s390x Signed-off-by: Archana Shinde --- src/libs/kata-sys-util/src/cpu.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/kata-sys-util/src/cpu.rs b/src/libs/kata-sys-util/src/cpu.rs index 938f5be42..97bc2fd94 100644 --- a/src/libs/kata-sys-util/src/cpu.rs +++ b/src/libs/kata-sys-util/src/cpu.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 // -#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] use anyhow::{anyhow, Result}; #[cfg(target_arch = "s390x")]