diff --git a/Cargo.lock b/Cargo.lock index 3abbd97..a89f5ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -905,7 +905,6 @@ name = "verify-lib" version = "0.1.0" dependencies = [ "bin-lib", - "fw-lib", "ipk-lib", ] diff --git a/common/bin/src/binary.rs b/common/bin/src/binary.rs index 3ba29ff..6cfe1a1 100644 --- a/common/bin/src/binary.rs +++ b/common/bin/src/binary.rs @@ -1,36 +1,15 @@ -use std::fs::File; -use std::io::{Error, ErrorKind}; -use std::path::Path; - use elf::dynamic::Dyn; use elf::endian::AnyEndian; use elf::symbol::Symbol; use elf::{abi, ElfStream}; -use crate::{BinaryInfo, LibraryInfo}; +use crate::BinaryInfo; impl BinaryInfo { - pub fn find_library(&self, name: &str) -> Option { - for rpath in &self.rpath { - let path = - Path::new(&rpath.replace("$ORIGIN", &self.dir.as_ref().unwrap().to_string_lossy())) - .join(name); - if let Ok(f) = File::open(&path) { - return LibraryInfo::parse(f, false, path.file_name().unwrap().to_string_lossy()) - .map_err(|e| { - Error::new(ErrorKind::InvalidData, format!("Bad library info: {e:?}")) - }) - .ok(); - } - } - return None; - } - - pub fn parse(source: S, name: N, dir: Option) -> Result + pub fn parse(source: S, name: N) -> Result where S: std::io::Read + std::io::Seek, N: AsRef, - D: AsRef, { let mut rpath = Vec::::new(); let mut needed = Vec::::new(); @@ -50,13 +29,12 @@ impl BinaryInfo { dynstr_table.get(entry.d_val() as usize).unwrap(), )); } - abi::DT_RPATH | abi::DT_RUNPATH => rpath.push( + abi::DT_RPATH | abi::DT_RUNPATH => rpath.extend( dynstr_table .get(entry.d_val() as usize) .unwrap() .split(":") - .map(|s| String::from(s)) - .collect(), + .map(|s| String::from(s)), ), _ => {} } @@ -81,10 +59,12 @@ impl BinaryInfo { if !sym.is_undefined() || sym.st_name == 0 || sym.st_bind() == abi::STB_WEAK { return vec![]; } - if let Some(ver_table) = &ver_table { - if let Some(ver) = ver_table.get_requirement(index).ok().flatten() { - return vec![format!("{name}@{}", ver.name)]; - } + if let Some(ver) = ver_table + .as_ref() + .map(|t| t.get_requirement(index).ok().flatten()) + .flatten() + { + return vec![format!("{name}@{}", ver.name)]; } return vec![name.clone()]; }) @@ -92,7 +72,6 @@ impl BinaryInfo { return Ok(Self { name: String::from(name.as_ref()), - dir: dir.map(|d| d.as_ref().to_path_buf()), rpath, needed, undefined, @@ -110,7 +89,7 @@ mod tests { fn test_parse() { let mut content = Cursor::new(include_bytes!("fixtures/sample.bin")); let info = - BinaryInfo::parse(&mut content, "sample.bin", None).expect("should not have any error"); + BinaryInfo::parse(&mut content, "sample.bin").expect("should not have any error"); assert_eq!(info.needed[0], "libc.so.6"); } } diff --git a/common/bin/src/lib.rs b/common/bin/src/lib.rs index f0e3847..9b3afc3 100644 --- a/common/bin/src/lib.rs +++ b/common/bin/src/lib.rs @@ -1,4 +1,3 @@ -use std::path::PathBuf; use serde::{Deserialize, Serialize}; pub mod binary; @@ -7,7 +6,6 @@ pub mod library; #[derive(Debug, Serialize, Deserialize)] pub struct BinaryInfo { pub name: String, - pub dir: Option, pub rpath: Vec, pub needed: Vec, pub undefined: Vec, @@ -22,4 +20,19 @@ pub struct LibraryInfo { pub names: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] pub undefined: Vec, + #[serde(skip_serializing, default = "LibraryPriority::default")] + pub priority: LibraryPriority, +} + +#[derive(Debug, Clone, Deserialize, PartialEq)] +pub enum LibraryPriority { + Rpath, + System, + Package, +} + +impl Default for LibraryPriority { + fn default() -> Self { + return LibraryPriority::System; + } } diff --git a/common/bin/src/library.rs b/common/bin/src/library.rs index de39909..dd2df10 100644 --- a/common/bin/src/library.rs +++ b/common/bin/src/library.rs @@ -142,6 +142,7 @@ impl LibraryInfo { symbols, undefined, names: Default::default(), + priority: Default::default(), }); } } diff --git a/common/fw/src/firmware.rs b/common/fw/src/firmware.rs index e4ada5b..c0de8db 100644 --- a/common/fw/src/firmware.rs +++ b/common/fw/src/firmware.rs @@ -111,7 +111,7 @@ impl Firmware { return if cfg!(feature = "linux-install") { PathBuf::from("/usr/share/webosbrew/compat-checker/data") } else { - PathBuf::from("common/data") + PathBuf::from("common").join("data") }; } } diff --git a/common/ipk/src/component.rs b/common/ipk/src/component.rs index 9d7d301..ef69de8 100644 --- a/common/ipk/src/component.rs +++ b/common/ipk/src/component.rs @@ -3,11 +3,11 @@ use std::collections::HashMap; use std::fs; use std::fs::File; use std::io::{Error, ErrorKind}; -use std::path::Path; +use std::path::{Path, PathBuf}; use path_slash::CowExt; -use bin_lib::{BinaryInfo, LibraryInfo}; +use bin_lib::{BinaryInfo, LibraryInfo, LibraryPriority}; use crate::{AppInfo, Component, ServiceInfo, Symlinks}; @@ -19,10 +19,10 @@ impl AppInfo { impl ServiceInfo { fn is_native(&self) -> bool { - if let (Some(engine), Some(_)) = (&self.engine, &self.executable) { - return engine == "native"; - } - return false; + let (Some(engine), Some(_)) = (&self.engine, &self.executable) else { + return false; + }; + return engine == "native"; } } @@ -47,29 +47,31 @@ impl Component { libs: Default::default(), }); } - let libs = Self::list_libs(dir, links)?; let exe_path = dir.join(Cow::from_slash(&info.main)); + let bin_info = BinaryInfo::parse( + File::open(&exe_path).map_err(|e| { + Error::new( + e.kind(), + format!("Failed to open main executable {}: {e}", info.main), + ) + })?, + exe_path.file_name().unwrap().to_string_lossy(), + ) + .map_err(|e| { + Error::new( + ErrorKind::InvalidData, + format!("Bad app executable {}: {e}", info.main), + ) + })?; + let libs = Self::list_libs( + dir, + &Component::::rpath(&bin_info.rpath, &exe_path), + links, + )?; return Ok(Self { id: info.id.clone(), info: info.clone(), - exe: Some( - BinaryInfo::parse( - File::open(&exe_path).map_err(|e| { - Error::new( - e.kind(), - format!("Failed to open main executable {}: {e}", info.main), - ) - })?, - exe_path.file_name().unwrap().to_string_lossy(), - exe_path.parent() - ) - .map_err(|e| { - Error::new( - ErrorKind::InvalidData, - format!("Bad app executable {}: {e}", info.main), - ) - })?, - ), + exe: Some(bin_info), libs, }); } @@ -88,24 +90,26 @@ impl Component { }); } let executable = info.executable.as_ref().unwrap(); - let libs = Self::list_libs(dir, links)?; let exe_path = dir.join(Cow::from_slash(executable)); + let bin_info = BinaryInfo::parse( + File::open(dir.join(&exe_path))?, + exe_path.file_name().unwrap().to_string_lossy(), + ) + .map_err(|e| { + Error::new( + ErrorKind::InvalidData, + format!("Bad app executable {}: {e:?}", executable), + ) + })?; + let libs = Self::list_libs( + dir, + &Component::::rpath(&bin_info.rpath, &exe_path), + links, + )?; return Ok(Self { id: info.id.clone(), info: info.clone(), - exe: Some( - BinaryInfo::parse( - File::open(dir.join(&exe_path))?, - exe_path.file_name().unwrap().to_string_lossy(), - exe_path.parent() - ) - .map_err(|e| { - Error::new( - ErrorKind::InvalidData, - format!("Bad app executable {}: {e:?}", executable), - ) - })?, - ), + exe: Some(bin_info), libs, }); } @@ -117,35 +121,67 @@ impl Component { } pub fn is_required(&self, lib: &LibraryInfo) -> bool { - if let Some(exe) = &self.exe { - if exe - .needed - .iter() - .find(|needed| lib.has_name(needed)) - .is_some() - { - return true; - } - } - return false; + let Some(exe) = &self.exe else { + return false; + }; + return exe + .needed + .iter() + .find(|needed| lib.has_name(needed)) + .is_some(); } - fn list_libs(dir: &Path, links: &Symlinks) -> Result, Error> { + fn rpath

(rpath: &Vec, bin_path: P) -> Vec + where + P: AsRef, + { + return rpath + .iter() + .filter_map(|p| { + PathBuf::from(p.replace( + "$ORIGIN", + bin_path.as_ref().parent().unwrap().to_str().unwrap(), + )) + .canonicalize() + .ok() + }) + .collect(); + } + + fn list_libs( + dir: &Path, + rpath: &Vec, + links: &Symlinks, + ) -> Result, Error> { let mut libs = HashMap::new(); - if let Ok(entries) = fs::read_dir(dir.join("lib")) { + let lib_dir = dir.join("lib").canonicalize(); + let mut lib_dirs: Vec<(&Path, bool)> = rpath.iter().map(|p| (p.as_path(), true)).collect(); + if let Ok(lib_dir) = lib_dir.as_ref() { + if !rpath.contains(&lib_dir) { + lib_dirs.push((lib_dir.as_path(), false)); + } + } + for (lib_dir, is_rpath) in lib_dirs { + let Ok(entries) = fs::read_dir(lib_dir) else{continue;}; for entry in entries { let entry = entry?; if !entry.file_type()?.is_file() { continue; } let path = entry.path(); - if let Ok(lib) = LibraryInfo::parse( + let Ok(mut lib) = LibraryInfo::parse( File::open(&path)?, true, path.file_name().unwrap().to_string_lossy(), - ) { - libs.insert(path, lib); - } + ) else { + continue; + }; + lib.priority = if is_rpath { + LibraryPriority::Rpath + } else { + LibraryPriority::Package + }; + libs.insert(path, lib); } } for (path, lib) in &mut libs { diff --git a/common/verify/Cargo.toml b/common/verify/Cargo.toml index 422d6fc..d835ed2 100644 --- a/common/verify/Cargo.toml +++ b/common/verify/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fw-lib = { path = "../../common/fw" } bin-lib = { path = "../../common/bin", optional = true } ipk-lib = { path = "../../common/ipk", optional = true } diff --git a/common/verify/src/bin/binary.rs b/common/verify/src/bin/binary.rs index b8cd0f1..77204dc 100644 --- a/common/verify/src/bin/binary.rs +++ b/common/verify/src/bin/binary.rs @@ -1,34 +1,30 @@ use std::collections::HashSet; use bin_lib::{BinaryInfo, LibraryInfo}; -use fw_lib::Firmware; use crate::bin::BinVerifyResult; -use crate::{VerifyResult, VerifyWithFirmware}; +use crate::{Verify, VerifyResult}; -impl VerifyWithFirmware for BinaryInfo { - fn verify(&self, firmware: &Firmware) -> BinVerifyResult { +impl Verify for BinaryInfo { + fn verify(&self, find_library: &F) -> BinVerifyResult + where + F: Fn(&str) -> Option, + { let mut result = BinVerifyResult::new(self.name.clone()); result.undefined_sym.extend(self.undefined.clone()); let mut visited_libs: HashSet = Default::default(); - let find_library = |name: &str| -> Option { - return firmware - .find_library(name) - .or_else(|| self.find_library(name)); - }; - for needed in &self.needed { - if let Some(lib) = find_library(needed) { - recursive_resolve_symbols( - &lib, - &mut result.undefined_sym, - &mut visited_libs, - &find_library, - ); - } else { + let Some(lib) = find_library(needed) else { result.missing_lib.push(needed.clone()); - } + continue; + }; + recursive_resolve_symbols( + &lib, + &mut result.undefined_sym, + &mut visited_libs, + &find_library, + ); } return result; } @@ -48,9 +44,10 @@ pub(crate) fn recursive_resolve_symbols( continue; } visited.insert(needed.clone()); - if let Some(needed) = lib_resolver(needed) { - recursive_resolve_symbols(&needed, undefined, visited, lib_resolver); - } + let Some(needed) = lib_resolver(needed) else { + continue; + }; + recursive_resolve_symbols(&needed, undefined, visited, lib_resolver); } } diff --git a/common/verify/src/ipk/component.rs b/common/verify/src/ipk/component.rs index bddfa9d..94fc426 100644 --- a/common/verify/src/ipk/component.rs +++ b/common/verify/src/ipk/component.rs @@ -1,15 +1,13 @@ -use std::collections::HashSet; - -use bin_lib::{BinaryInfo, LibraryInfo}; -use fw_lib::Firmware; +use bin_lib::{BinaryInfo, LibraryInfo, LibraryPriority}; use ipk_lib::Component; -use crate::bin::binary::recursive_resolve_symbols; use crate::ipk::{ComponentBinVerifyResult, ComponentVerifyResult}; -use crate::{bin::BinVerifyResult, VerifyResult, VerifyWithFirmware}; +use crate::{bin::BinVerifyResult, Verify, VerifyResult}; trait ComponentImpl { - fn verify_bin(&self, bin: &BinaryInfo, firmware: &Firmware) -> BinVerifyResult; + fn verify_bin(&self, bin: &BinaryInfo, find_library: &F) -> BinVerifyResult + where + F: Fn(&str) -> Option; } impl VerifyResult for ComponentVerifyResult { @@ -30,36 +28,34 @@ impl VerifyResult for ComponentVerifyResult { } impl ComponentImpl for Component { - fn verify_bin(&self, bin: &BinaryInfo, firmware: &Firmware) -> BinVerifyResult { - let mut result = bin.verify(firmware); - let mut visited_libs: HashSet = Default::default(); - result.missing_lib.retain_mut(|lib| { - if let Some(lib) = self.find_lib(lib) { - let find_library = |name: &str| -> Option { - return firmware - .find_library(name) - .or_else(|| self.find_lib(name).cloned()); - }; + fn verify_bin(&self, bin: &BinaryInfo, find_library: &F) -> BinVerifyResult + where + F: Fn(&str) -> Option, + { + return bin.verify(&|name| { + let lib = self.find_lib(name); + return if let Some(lib) = lib { + if lib.priority == LibraryPriority::Rpath { + return Some(lib.clone()); + } - recursive_resolve_symbols( - &lib, - &mut result.undefined_sym, - &mut visited_libs, - &find_library, - ); - return false; - } - return true; + if let Some(sys) = find_library(name) { + return Some(sys.clone()); + } + Some(lib.clone()) + } else { + find_library(name) + }; }); - return result; } } -impl VerifyWithFirmware for Component { - fn verify(&self, firmware: &Firmware) -> ComponentVerifyResult { - let exe = if let Some(bin) = &self.exe { - self.verify_bin(bin, firmware) - } else { +impl Verify for Component { + fn verify(&self, find_library: &F) -> ComponentVerifyResult + where + F: Fn(&str) -> Option, + { + let Some(exe) = &self.exe else { return ComponentVerifyResult { id: self.id.clone(), exe: ComponentBinVerifyResult::Skipped { @@ -68,29 +64,31 @@ impl VerifyWithFirmware for Component { libs: Default::default(), }; }; + let bin = self.verify_bin(exe, find_library); let mut libs: Vec<(bool, ComponentBinVerifyResult)> = self .libs .iter() .map(|lib| { let required = self.is_required(lib); // System library has higher precedence - if let Some(_) = firmware.find_library(&lib.name) { - return ( - required, - ComponentBinVerifyResult::Skipped { - name: lib.name.clone(), - }, - ); + if lib.priority != LibraryPriority::Rpath { + if find_library(&lib.name).is_some() { + return ( + required, + ComponentBinVerifyResult::Skipped { + name: lib.name.clone(), + }, + ); + } } let verify_result = self.verify_bin( &BinaryInfo { name: lib.name.clone(), - dir: Default::default(), rpath: Default::default(), needed: lib.needed.clone(), undefined: lib.undefined.clone(), }, - firmware, + find_library, ); ( required, @@ -113,10 +111,10 @@ impl VerifyWithFirmware for Component { }); return ComponentVerifyResult { id: self.id.clone(), - exe: if exe.is_good() { - ComponentBinVerifyResult::Ok { name: exe.name } + exe: if bin.is_good() { + ComponentBinVerifyResult::Ok { name: bin.name } } else { - ComponentBinVerifyResult::Failed(exe) + ComponentBinVerifyResult::Failed(bin) }, libs, }; diff --git a/common/verify/src/ipk/mod.rs b/common/verify/src/ipk/mod.rs index 81bf719..64b3e9f 100644 --- a/common/verify/src/ipk/mod.rs +++ b/common/verify/src/ipk/mod.rs @@ -1,7 +1,7 @@ -use fw_lib::Firmware; +use bin_lib::LibraryInfo; use ipk_lib::Package; -use crate::{bin::BinVerifyResult, VerifyResult, VerifyWithFirmware}; +use crate::{bin::BinVerifyResult, VerifyResult, Verify}; pub mod component; @@ -25,14 +25,17 @@ pub enum ComponentBinVerifyResult { Failed(BinVerifyResult), } -impl VerifyWithFirmware for Package { - fn verify(&self, firmware: &Firmware) -> PackageVerifyResult { +impl Verify for Package { + fn verify(&self, find_library: &F) -> PackageVerifyResult + where + F: Fn(&str) -> Option, + { return PackageVerifyResult { - app: self.app.verify(firmware), + app: self.app.verify(find_library), services: self .services .iter() - .map(|svc| svc.verify(firmware)) + .map(|svc| svc.verify(find_library)) .collect(), }; } diff --git a/common/verify/src/lib.rs b/common/verify/src/lib.rs index 006b87d..1cd8aac 100644 --- a/common/verify/src/lib.rs +++ b/common/verify/src/lib.rs @@ -1,12 +1,14 @@ -use fw_lib::Firmware; +use bin_lib::LibraryInfo; #[cfg(feature = "bin")] pub mod bin; #[cfg(feature = "ipk")] pub mod ipk; -pub trait VerifyWithFirmware { - fn verify(&self, firmware: &Firmware) -> R; +pub trait Verify { + fn verify(&self, find_library: &F) -> R + where + F: Fn(&str) -> Option; } pub trait VerifyResult { diff --git a/packages/elf-verify/src/main.rs b/packages/elf-verify/src/main.rs index e659289..7f06adc 100644 --- a/packages/elf-verify/src/main.rs +++ b/packages/elf-verify/src/main.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use bin_lib::BinaryInfo; use clap::Parser; use fw_lib::Firmware; -use verify_lib::VerifyWithFirmware; +use verify_lib::Verify; #[derive(Parser, Debug)] struct Args { @@ -19,7 +19,10 @@ struct Args { fn main() { let args = Args::parse(); for executable in args.executables { - let file = File::open(&executable).unwrap(); + let Ok(file) = File::open(&executable) else { + eprintln!("Failed to open file {}", executable.to_string_lossy()); + continue; + }; let mut info = BinaryInfo::parse(file, executable.file_name().unwrap().to_string_lossy()) .expect("parse error"); info.rpath.extend(args.lib_paths.clone()); @@ -27,7 +30,7 @@ fn main() { println!( "Verify result for firmware {} {:?}", firmware.info, - info.verify(&firmware) + info.verify(&|name| firmware.find_library(name)) ); } } diff --git a/packages/ipk-verify/src/main.rs b/packages/ipk-verify/src/main.rs index 87e418a..4ee8be9 100644 --- a/packages/ipk-verify/src/main.rs +++ b/packages/ipk-verify/src/main.rs @@ -13,7 +13,7 @@ use fw_lib::Firmware; use ipk_lib::Package; use verify_lib::bin::BinVerifyResult; use verify_lib::ipk::{ComponentBinVerifyResult, ComponentVerifyResult, PackageVerifyResult}; -use verify_lib::{VerifyResult, VerifyWithFirmware}; +use verify_lib::{VerifyResult, Verify}; use crate::output::ReportOutput; @@ -96,7 +96,7 @@ fn main() { let results: Vec<(&Firmware, PackageVerifyResult)> = firmwares .iter() .map(|fw| { - let verify = package.verify(&fw); + let verify = package.verify(&|name| fw.find_library(name)); return (fw, verify); }) .collect();