syscare/0009-syscared-improve-patch-management.patch

2320 lines
82 KiB
Diff
Raw Normal View History

From 354e0888188d4cabd9fff9912fa0935e4e1b4b52 Mon Sep 17 00:00:00 2001
2024-04-19 16:03:21 +08:00
From: renoseven <dev@renoseven.net>
Date: Tue, 16 Apr 2024 14:20:27 +0800
Subject: [PATCH] syscared: improve patch management
2024-04-19 16:03:21 +08:00
Signed-off-by: renoseven <dev@renoseven.net>
---
syscared/src/patch/driver/kpatch/mod.rs | 161 ++++--
syscared/src/patch/driver/kpatch/sys.rs | 16 +-
syscared/src/patch/driver/kpatch/target.rs | 164 +++----
syscared/src/patch/driver/mod.rs | 18 +-
syscared/src/patch/driver/upatch/entity.rs | 74 +++
syscared/src/patch/driver/upatch/mod.rs | 511 +++++++++++---------
syscared/src/patch/driver/upatch/monitor.rs | 72 +--
syscared/src/patch/driver/upatch/process.rs | 100 ----
syscared/src/patch/driver/upatch/sys.rs | 14 +-
syscared/src/patch/driver/upatch/target.rs | 138 +++---
syscared/src/patch/entity/kpatch.rs | 20 +-
syscared/src/patch/entity/symbol.rs | 10 +-
syscared/src/patch/entity/upatch.rs | 12 +-
syscared/src/patch/resolver/kpatch.rs | 141 +++---
syscared/src/patch/resolver/upatch.rs | 98 ++--
15 files changed, 833 insertions(+), 716 deletions(-)
2024-04-19 16:03:21 +08:00
create mode 100644 syscared/src/patch/driver/upatch/entity.rs
delete mode 100644 syscared/src/patch/driver/upatch/process.rs
diff --git a/syscared/src/patch/driver/kpatch/mod.rs b/syscared/src/patch/driver/kpatch/mod.rs
index e4509d8..45dc719 100644
--- a/syscared/src/patch/driver/kpatch/mod.rs
+++ b/syscared/src/patch/driver/kpatch/mod.rs
@@ -19,12 +19,12 @@ use std::{
use anyhow::{ensure, Result};
use indexmap::{indexset, IndexMap, IndexSet};
-use log::debug;
+use log::{debug, info};
use syscare_abi::PatchStatus;
use syscare_common::{concat_os, os, util::digest};
-use crate::patch::entity::KernelPatch;
+use crate::patch::entity::{KernelPatch, KernelPatchFunction};
mod sys;
mod target;
@@ -32,21 +32,84 @@ mod target;
use target::PatchTarget;
pub struct KernelPatchDriver {
- patch_target_map: IndexMap<OsString, PatchTarget>,
+ target_map: IndexMap<OsString, PatchTarget>,
}
impl KernelPatchDriver {
pub fn new() -> Result<Self> {
Ok(Self {
- patch_target_map: IndexMap::new(),
+ target_map: IndexMap::new(),
})
}
}
+impl KernelPatchDriver {
+ fn group_patch_targets(patch: &KernelPatch) -> IndexSet<&OsStr> {
+ let mut patch_targets = IndexSet::new();
+
+ for function in &patch.functions {
+ patch_targets.insert(function.object.as_os_str());
+ }
+ patch_targets
+ }
+
+ pub fn group_patch_functions(
+ patch: &KernelPatch,
+ ) -> IndexMap<&OsStr, Vec<&KernelPatchFunction>> {
+ let mut patch_function_map: IndexMap<&OsStr, Vec<&KernelPatchFunction>> = IndexMap::new();
+
+ for function in &patch.functions {
+ patch_function_map
+ .entry(function.object.as_os_str())
+ .or_default()
+ .push(function);
+ }
+ patch_function_map
+ }
+}
+
+impl KernelPatchDriver {
+ fn add_patch_target(&mut self, patch: &KernelPatch) {
+ for target_name in Self::group_patch_targets(patch) {
+ if !self.target_map.contains_key(target_name) {
+ self.target_map.insert(
+ target_name.to_os_string(),
+ PatchTarget::new(target_name.to_os_string()),
+ );
+ }
+ }
+ }
+
+ fn remove_patch_target(&mut self, patch: &KernelPatch) {
+ for target_name in Self::group_patch_targets(patch) {
+ if let Some(target) = self.target_map.get_mut(target_name) {
+ if !target.has_function() {
+ self.target_map.remove(target_name);
+ }
+ }
+ }
+ }
+
+ fn add_patch_functions(&mut self, patch: &KernelPatch) {
+ for (target_name, functions) in Self::group_patch_functions(patch) {
+ if let Some(target) = self.target_map.get_mut(target_name) {
+ target.add_functions(patch.uuid, functions);
+ }
+ }
+ }
+
+ fn remove_patch_functions(&mut self, patch: &KernelPatch) {
+ for (target_name, functions) in Self::group_patch_functions(patch) {
+ if let Some(target) = self.target_map.get_mut(target_name) {
+ target.remove_functions(&patch.uuid, functions);
+ }
+ }
+ }
+}
+
impl KernelPatchDriver {
fn check_consistency(patch: &KernelPatch) -> Result<()> {
- let patch_file = patch.patch_file.as_path();
- let real_checksum = digest::file(patch_file)?;
+ let real_checksum = digest::file(&patch.patch_file)?;
debug!("Target checksum: '{}'", patch.checksum);
debug!("Expected checksum: '{}'", real_checksum);
@@ -78,7 +141,7 @@ impl KernelPatchDriver {
let mut non_exist_kmod = IndexSet::new();
let kmod_list = sys::list_kernel_modules()?;
- for kmod_name in Self::parse_target_modules(patch) {
+ for kmod_name in Self::group_patch_targets(patch) {
if kmod_name == VMLINUX_MODULE_NAME {
continue;
}
@@ -102,15 +165,15 @@ impl KernelPatchDriver {
Ok(())
}
- pub fn check_conflict_symbols(&self, patch: &KernelPatch) -> Result<()> {
+ pub fn check_conflict_functions(&self, patch: &KernelPatch) -> Result<()> {
let mut conflict_patches = indexset! {};
- let target_symbols = PatchTarget::classify_symbols(&patch.symbols);
- for (target_name, symbols) in target_symbols {
- if let Some(target) = self.patch_target_map.get(target_name) {
+ let target_functions = Self::group_patch_functions(patch);
+ for (target_name, functions) in target_functions {
+ if let Some(target) = self.target_map.get(target_name) {
conflict_patches.extend(
target
- .get_conflicts(symbols)
+ .get_conflicts(functions)
.into_iter()
.map(|record| record.uuid),
);
@@ -131,15 +194,15 @@ impl KernelPatchDriver {
Ok(())
}
- pub fn check_override_symbols(&self, patch: &KernelPatch) -> Result<()> {
+ pub fn check_override_functions(&self, patch: &KernelPatch) -> Result<()> {
let mut override_patches = indexset! {};
- let target_symbols = PatchTarget::classify_symbols(&patch.symbols);
- for (target_name, symbols) in target_symbols {
- if let Some(target) = self.patch_target_map.get(target_name) {
+ let target_functions = Self::group_patch_functions(patch);
+ for (target_name, functions) in target_functions {
+ if let Some(target) = self.target_map.get(target_name) {
override_patches.extend(
target
- .get_overrides(&patch.uuid, symbols)
+ .get_overrides(&patch.uuid, functions)
.into_iter()
.map(|record| record.uuid),
);
@@ -161,35 +224,6 @@ impl KernelPatchDriver {
}
}
-impl KernelPatchDriver {
- fn parse_target_modules(patch: &KernelPatch) -> impl IntoIterator<Item = &OsStr> {
- patch.symbols.iter().map(|symbol| symbol.target.as_os_str())
- }
-
- fn add_patch_symbols(&mut self, patch: &KernelPatch) {
- let target_symbols = PatchTarget::classify_symbols(&patch.symbols);
-
- for (target_name, symbols) in target_symbols {
- let target = self
- .patch_target_map
- .entry(target_name.to_os_string())
- .or_insert_with(|| PatchTarget::new(target_name));
-
- target.add_symbols(patch.uuid, symbols);
- }
- }
-
- fn remove_patch_symbols(&mut self, patch: &KernelPatch) {
- let target_symbols = PatchTarget::classify_symbols(&patch.symbols);
-
- for (target_name, symbols) in target_symbols {
- if let Some(target) = self.patch_target_map.get_mut(target_name) {
- target.remove_symbols(&patch.uuid, symbols);
- }
- }
- }
-}
-
impl KernelPatchDriver {
pub fn status(&self, patch: &KernelPatch) -> Result<PatchStatus> {
sys::read_patch_status(patch)
@@ -204,24 +238,51 @@ impl KernelPatchDriver {
}
pub fn apply(&mut self, patch: &KernelPatch) -> Result<()> {
+ info!(
+ "Kpatch: Applying patch '{}' ({})",
+ patch.uuid,
+ patch.patch_file.display()
+ );
+
sys::selinux_relable_patch(patch)?;
- sys::apply_patch(patch)
+ sys::apply_patch(patch)?;
+ self.add_patch_target(patch);
+
+ Ok(())
}
pub fn remove(&mut self, patch: &KernelPatch) -> Result<()> {
- sys::remove_patch(patch)
+ info!(
+ "Kpatch: Removing patch '{}' ({})",
+ patch.uuid,
+ patch.patch_file.display()
+ );
+ sys::remove_patch(patch)?;
+ self.remove_patch_target(patch);
+
+ Ok(())
}
pub fn active(&mut self, patch: &KernelPatch) -> Result<()> {
+ info!(
+ "Kpatch: Activating patch '{}' ({})",
+ patch.uuid,
+ patch.patch_file.display()
+ );
sys::active_patch(patch)?;
- self.add_patch_symbols(patch);
+ self.add_patch_functions(patch);
Ok(())
}
pub fn deactive(&mut self, patch: &KernelPatch) -> Result<()> {
+ info!(
+ "Kpatch: Deactivating patch '{}' ({})",
+ patch.uuid,
+ patch.patch_file.display()
+ );
sys::deactive_patch(patch)?;
- self.remove_patch_symbols(patch);
+ self.remove_patch_functions(patch);
Ok(())
}
diff --git a/syscared/src/patch/driver/kpatch/sys.rs b/syscared/src/patch/driver/kpatch/sys.rs
index 5035d06..ea6b68f 100644
--- a/syscared/src/patch/driver/kpatch/sys.rs
+++ b/syscared/src/patch/driver/kpatch/sys.rs
@@ -61,17 +61,11 @@ pub fn read_patch_status(patch: &KernelPatch) -> Result<PatchStatus> {
debug!("Reading {}", sys_file.display());
let status = match fs::read_to_string(sys_file) {
- Ok(str) => {
- let status = str.trim();
- let patch_status = match status {
- KPATCH_STATUS_DISABLED => PatchStatus::Deactived,
- KPATCH_STATUS_ENABLED => PatchStatus::Actived,
- _ => {
- bail!("Kpatch: Invalid patch status");
- }
- };
- Ok(patch_status)
- }
+ Ok(str) => match str.trim() {
+ KPATCH_STATUS_DISABLED => Ok(PatchStatus::Deactived),
+ KPATCH_STATUS_ENABLED => Ok(PatchStatus::Actived),
+ _ => bail!("Kpatch: Invalid patch status"),
+ },
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(PatchStatus::NotApplied),
Err(e) => Err(e),
}
diff --git a/syscared/src/patch/driver/kpatch/target.rs b/syscared/src/patch/driver/kpatch/target.rs
index 0b01f2f..beab803 100644
--- a/syscared/src/patch/driver/kpatch/target.rs
+++ b/syscared/src/patch/driver/kpatch/target.rs
@@ -12,69 +12,106 @@
* See the Mulan PSL v2 for more details.
*/
-use std::ffi::{OsStr, OsString};
+use std::ffi::OsString;
use indexmap::IndexMap;
use uuid::Uuid;
-use crate::patch::entity::KernelPatchSymbol;
+use crate::patch::entity::KernelPatchFunction;
-#[derive(Debug, PartialEq)]
-pub struct PatchTargetRecord {
+#[derive(Debug)]
+pub struct PatchFunction {
pub uuid: Uuid,
pub name: OsString,
pub size: u64,
}
+impl PatchFunction {
+ fn new(uuid: Uuid, function: &KernelPatchFunction) -> Self {
+ Self {
+ uuid,
+ name: function.name.to_os_string(),
+ size: function.new_size,
+ }
+ }
+
+ fn is_same_function(&self, uuid: &Uuid, function: &KernelPatchFunction) -> bool {
+ (self.uuid == *uuid) && (self.name == function.name) && (self.size == function.new_size)
+ }
+}
+
+#[derive(Debug)]
pub struct PatchTarget {
name: OsString,
- symbol_map: IndexMap<u64, Vec<PatchTargetRecord>>, // symbol addr -> symbol collision list
+ function_map: IndexMap<u64, Vec<PatchFunction>>, // function addr -> function collision list
}
impl PatchTarget {
- fn match_record(record: &PatchTargetRecord, uuid: &Uuid, symbol: &KernelPatchSymbol) -> bool {
- (record.uuid == *uuid) && (record.name == symbol.name) && (record.size == symbol.new_size)
+ pub fn new(name: OsString) -> Self {
+ Self {
+ name,
+ function_map: IndexMap::new(),
+ }
}
}
impl PatchTarget {
- pub fn new<S: AsRef<OsStr>>(name: S) -> Self {
- Self {
- name: name.as_ref().to_os_string(),
- symbol_map: IndexMap::new(),
- }
+ pub fn has_function(&self) -> bool {
+ self.function_map.is_empty()
}
- pub fn classify_symbols(
- symbols: &[KernelPatchSymbol],
- ) -> IndexMap<&OsStr, Vec<&KernelPatchSymbol>> {
- let mut symbol_map = IndexMap::new();
-
- for symbol in symbols {
- let target_name = symbol.target.as_os_str();
-
- symbol_map
- .entry(target_name)
- .or_insert_with(Vec::new)
- .push(symbol);
+ pub fn add_functions<'a, I>(&mut self, uuid: Uuid, functions: I)
+ where
+ I: IntoIterator<Item = &'a KernelPatchFunction>,
+ {
+ for function in functions {
+ if self.name != function.object {
+ continue;
+ }
+ self.function_map
+ .entry(function.old_addr)
+ .or_default()
+ .push(PatchFunction::new(uuid, function));
}
+ }
- symbol_map
+ pub fn remove_functions<'a, I>(&mut self, uuid: &Uuid, functions: I)
+ where
+ I: IntoIterator<Item = &'a KernelPatchFunction>,
+ {
+ for function in functions {
+ if self.name != function.object {
+ continue;
+ }
+ if let Some(collision_list) = self.function_map.get_mut(&function.old_addr) {
+ if let Some(index) = collision_list
+ .iter()
+ .position(|patch_function| patch_function.is_same_function(uuid, function))
+ {
+ collision_list.remove(index);
+ if collision_list.is_empty() {
+ self.function_map.remove(&function.old_addr);
+ }
+ }
+ }
+ }
}
+}
+impl PatchTarget {
pub fn get_conflicts<'a, I>(
&'a self,
- symbols: I,
- ) -> impl IntoIterator<Item = &'a PatchTargetRecord>
+ functions: I,
+ ) -> impl IntoIterator<Item = &'a PatchFunction>
where
- I: IntoIterator<Item = &'a KernelPatchSymbol>,
+ I: IntoIterator<Item = &'a KernelPatchFunction>,
{
- symbols.into_iter().filter_map(move |symbol| {
- if self.name != symbol.target {
+ functions.into_iter().filter_map(move |function| {
+ if self.name != function.object {
return None;
}
- self.symbol_map
- .get(&symbol.old_addr)
+ self.function_map
+ .get(&function.old_addr)
.and_then(|list| list.last())
})
}
@@ -82,66 +119,19 @@ impl PatchTarget {
pub fn get_overrides<'a, I>(
&'a self,
uuid: &'a Uuid,
- symbols: I,
- ) -> impl IntoIterator<Item = &'a PatchTargetRecord>
+ functions: I,
+ ) -> impl IntoIterator<Item = &'a PatchFunction>
where
- I: IntoIterator<Item = &'a KernelPatchSymbol>,
+ I: IntoIterator<Item = &'a KernelPatchFunction>,
{
- symbols.into_iter().filter_map(move |symbol| {
- if self.name != symbol.target {
+ functions.into_iter().filter_map(move |function| {
+ if self.name != function.object {
return None;
}
- self.symbol_map
- .get(&symbol.old_addr)
+ self.function_map
+ .get(&function.old_addr)
.and_then(|list| list.last())
- .filter(|record| !Self::match_record(record, uuid, symbol))
+ .filter(|patch_function| !patch_function.is_same_function(uuid, function))
})
}
-
- pub fn add_symbols<'a, I>(&mut self, uuid: Uuid, symbols: I)
- where
- I: IntoIterator<Item = &'a KernelPatchSymbol>,
- {
- for symbol in symbols {
- if self.name != symbol.target {
- continue;
- }
-
- let symbol_addr = symbol.old_addr;
- let symbol_record = PatchTargetRecord {
- uuid,
- name: symbol.name.to_os_string(),
- size: symbol.new_size,
- };
-
- self.symbol_map
- .entry(symbol_addr)
- .or_default()
- .push(symbol_record);
- }
- }
-
- pub fn remove_symbols<'a, I>(&mut self, uuid: &Uuid, symbols: I)
- where
- I: IntoIterator<Item = &'a KernelPatchSymbol>,
- {
- for symbol in symbols {
- if self.name != symbol.target {
- continue;
- }
-
- let symbol_addr = symbol.old_addr;
- if let Some(collision_list) = self.symbol_map.get_mut(&symbol_addr) {
- if let Some(index) = collision_list
- .iter()
- .position(|record| Self::match_record(record, uuid, symbol))
- {
- collision_list.remove(index);
- if collision_list.is_empty() {
- self.symbol_map.remove(&symbol_addr);
- }
- }
- }
- }
- }
}
diff --git a/syscared/src/patch/driver/mod.rs b/syscared/src/patch/driver/mod.rs
index e6dab94..d64c2d4 100644
--- a/syscared/src/patch/driver/mod.rs
+++ b/syscared/src/patch/driver/mod.rs
@@ -37,17 +37,17 @@ pub struct PatchDriver {
}
impl PatchDriver {
- fn check_conflict_symbols(&self, patch: &Patch) -> Result<()> {
+ fn check_conflict_functions(&self, patch: &Patch) -> Result<()> {
match patch {
- Patch::KernelPatch(patch) => self.kpatch.check_conflict_symbols(patch),
- Patch::UserPatch(patch) => self.upatch.check_conflict_symbols(patch),
+ Patch::KernelPatch(patch) => self.kpatch.check_conflict_functions(patch),
+ Patch::UserPatch(patch) => self.upatch.check_conflict_functions(patch),
}
}
- fn check_override_symbols(&self, patch: &Patch) -> Result<()> {
+ fn check_override_functions(&self, patch: &Patch) -> Result<()> {
match patch {
- Patch::KernelPatch(patch) => self.kpatch.check_override_symbols(patch),
- Patch::UserPatch(patch) => self.upatch.check_override_symbols(patch),
+ Patch::KernelPatch(patch) => self.kpatch.check_override_functions(patch),
+ Patch::UserPatch(patch) => self.upatch.check_override_functions(patch),
}
}
}
@@ -96,7 +96,7 @@ impl PatchDriver {
if flag == PatchOpFlag::Force {
return Ok(());
}
- self.check_conflict_symbols(patch)
+ self.check_conflict_functions(patch)
.with_context(|| format!("Patch '{}' is conflicted", patch))
}
@@ -124,7 +124,7 @@ impl PatchDriver {
/// After this action, the patch status would be changed to 'ACTIVED'.
pub fn active_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> {
if flag != PatchOpFlag::Force {
- self.check_conflict_symbols(patch)?;
+ self.check_conflict_functions(patch)?;
}
match patch {
Patch::KernelPatch(patch) => self.kpatch.active(patch),
@@ -137,7 +137,7 @@ impl PatchDriver {
/// After this action, the patch status would be changed to 'DEACTIVED'.
pub fn deactive_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> {
if flag != PatchOpFlag::Force {
- self.check_override_symbols(patch)?;
+ self.check_override_functions(patch)?;
}
match patch {
Patch::KernelPatch(patch) => self.kpatch.deactive(patch),
diff --git a/syscared/src/patch/driver/upatch/entity.rs b/syscared/src/patch/driver/upatch/entity.rs
new file mode 100644
index 0000000..80932c4
--- /dev/null
+++ b/syscared/src/patch/driver/upatch/entity.rs
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: Mulan PSL v2
+/*
+ * Copyright (c) 2024 Huawei Technologies Co., Ltd.
+ * syscared is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ *
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+use std::path::PathBuf;
+
+use indexmap::{indexset, IndexSet};
+
+#[derive(Debug)]
+pub struct PatchEntity {
+ pub patch_file: PathBuf,
+ process_list: IndexSet<i32>,
+ ignored_list: IndexSet<i32>,
+}
+
+impl PatchEntity {
+ pub fn new(patch_file: PathBuf) -> Self {
+ Self {
+ patch_file,
+ process_list: indexset! {},
+ ignored_list: indexset! {},
+ }
+ }
+}
+
+impl PatchEntity {
+ pub fn add_process(&mut self, pid: i32) {
+ self.process_list.insert(pid);
+ }
+
+ pub fn remove_process(&mut self, pid: i32) {
+ self.process_list.remove(&pid);
+ }
+
+ pub fn ignore_process(&mut self, pid: i32) {
+ self.ignored_list.insert(pid);
+ }
+
+ pub fn clean_dead_process(&mut self, process_list: &IndexSet<i32>) {
+ self.process_list.retain(|pid| process_list.contains(pid));
+ self.ignored_list.retain(|pid| process_list.contains(pid));
+ }
+
+ pub fn need_ignored(&self, process_list: &IndexSet<i32>) -> IndexSet<i32> {
+ process_list
+ .intersection(&self.ignored_list)
+ .copied()
+ .collect()
+ }
+
+ pub fn need_actived(&self, process_list: &IndexSet<i32>) -> IndexSet<i32> {
+ process_list
+ .difference(&self.process_list)
+ .copied()
+ .collect()
+ }
+
+ pub fn need_deactived(&self, process_list: &IndexSet<i32>) -> IndexSet<i32> {
+ process_list
+ .intersection(&self.process_list)
+ .copied()
+ .collect()
+ }
+}
diff --git a/syscared/src/patch/driver/upatch/mod.rs b/syscared/src/patch/driver/upatch/mod.rs
index 0b82db9..dd07e9b 100644
--- a/syscared/src/patch/driver/upatch/mod.rs
+++ b/syscared/src/patch/driver/upatch/mod.rs
@@ -13,65 +13,97 @@
*/
use std::{
- ffi::OsString,
+ ffi::OsStr,
fmt::Write,
+ os::linux::fs::MetadataExt,
path::{Path, PathBuf},
sync::Arc,
};
-use anyhow::{ensure, Context, Result};
+use anyhow::{bail, ensure, Context, Result};
use indexmap::{indexset, IndexMap, IndexSet};
-use log::{debug, error};
-use parking_lot::Mutex;
+use log::{debug, info, warn};
+use parking_lot::RwLock;
use uuid::Uuid;
use syscare_abi::PatchStatus;
-use syscare_common::util::digest;
+use syscare_common::{fs, util::digest};
-use crate::patch::entity::UserPatch;
+use crate::patch::{driver::upatch::entity::PatchEntity, entity::UserPatch};
+mod entity;
mod monitor;
-mod process;
mod sys;
mod target;
use monitor::UserPatchMonitor;
use target::PatchTarget;
-type ElfPatchMap = Arc<Mutex<IndexMap<PathBuf, ElfPatchRecord>>>;
-
-#[derive(Default)]
-struct ElfPatchRecord {
- patch_map: IndexMap<Uuid, PathBuf>, // Patch applied to target elf (uuid and patch file)
- processes: IndexSet<i32>, // Target elf process list
-}
-
pub struct UserPatchDriver {
- patch_target_map: IndexMap<OsString, PatchTarget>,
- patch_status_map: IndexMap<Uuid, PatchStatus>,
- elf_patch_map: ElfPatchMap,
- patch_monitor: UserPatchMonitor,
+ status_map: IndexMap<Uuid, PatchStatus>,
+ target_map: Arc<RwLock<IndexMap<PathBuf, PatchTarget>>>,
+ monitor: UserPatchMonitor,
}
impl UserPatchDriver {
pub fn new() -> Result<Self> {
- let elf_patch_map = Arc::new(Mutex::new(IndexMap::new()));
- let patch_monitor = UserPatchMonitor::new(elf_patch_map.clone(), Self::patch_new_process)?;
-
+ let status_map = IndexMap::new();
+ let target_map = Arc::new(RwLock::new(IndexMap::new()));
+ let monitor = UserPatchMonitor::new(target_map.clone(), Self::patch_new_process)?;
let instance = Self {
- patch_target_map: IndexMap::new(),
- patch_status_map: IndexMap::new(),
- elf_patch_map,
- patch_monitor,
+ status_map,
+ target_map,
+ monitor,
};
+
Ok(instance)
}
}
+impl UserPatchDriver {
+ #[inline]
+ fn get_patch_status(&self, uuid: &Uuid) -> PatchStatus {
+ self.status_map
+ .get(uuid)
+ .copied()
+ .unwrap_or(PatchStatus::NotApplied)
+ }
+
+ #[inline]
+ fn set_patch_status(&mut self, uuid: &Uuid, value: PatchStatus) {
+ *self.status_map.entry(*uuid).or_default() = value;
+ }
+
+ fn remove_patch_status(&mut self, uuid: &Uuid) {
+ self.status_map.remove(uuid);
+ }
+}
+
+impl UserPatchDriver {
+ fn add_patch_target(&mut self, patch: &UserPatch) {
+ let target_elf = patch.target_elf.as_path();
+ let mut target_map = self.target_map.write();
+
+ if !target_map.contains_key(target_elf) {
+ target_map.insert(target_elf.to_path_buf(), PatchTarget::default());
+ }
+ }
+
+ fn remove_patch_target(&mut self, patch: &UserPatch) {
+ let target_elf = patch.target_elf.as_path();
+ let mut target_map = self.target_map.write();
+
+ if let Some(target) = target_map.get_mut(target_elf) {
+ if !target.is_patched() {
+ target_map.remove(target_elf);
+ }
+ }
+ }
+}
+
impl UserPatchDriver {
fn check_consistency(patch: &UserPatch) -> Result<()> {
- let patch_file = patch.patch_file.as_path();
- let real_checksum = digest::file(patch_file)?;
+ let real_checksum = digest::file(&patch.patch_file)?;
debug!("Target checksum: '{}'", patch.checksum);
debug!("Expected checksum: '{}'", real_checksum);
@@ -86,12 +118,10 @@ impl UserPatchDriver {
Ok(())
}
- pub fn check_conflict_symbols(&self, patch: &UserPatch) -> Result<()> {
- let patch_symbols = patch.symbols.as_slice();
- let target_name = patch.target_elf.as_os_str();
- let conflict_patches = match self.patch_target_map.get(target_name) {
+ pub fn check_conflict_functions(&self, patch: &UserPatch) -> Result<()> {
+ let conflict_patches = match self.target_map.read().get(&patch.target_elf) {
Some(target) => target
- .get_conflicts(patch_symbols)
+ .get_conflicts(&patch.functions)
.into_iter()
.map(|record| record.uuid)
.collect(),
@@ -112,13 +142,10 @@ impl UserPatchDriver {
Ok(())
}
- pub fn check_override_symbols(&self, patch: &UserPatch) -> Result<()> {
- let patch_uuid = patch.uuid;
- let patch_symbols = patch.symbols.as_slice();
- let target_name = patch.target_elf.as_os_str();
- let override_patches = match self.patch_target_map.get(target_name) {
+ pub fn check_override_functions(&self, patch: &UserPatch) -> Result<()> {
+ let override_patches = match self.target_map.read().get(&patch.target_elf) {
Some(target) => target
- .get_overrides(&patch_uuid, patch_symbols)
+ .get_overrides(&patch.uuid, &patch.functions)
.into_iter()
.map(|record| record.uuid)
.collect(),
@@ -142,110 +169,109 @@ impl UserPatchDriver {
}
impl UserPatchDriver {
- fn add_patch_symbols(&mut self, patch: &UserPatch) {
- let target_name = patch.target_elf.as_os_str();
-
- let patch_uuid = patch.uuid;
- let patch_target = self
- .patch_target_map
- .entry(target_name.to_os_string())
- .or_insert_with(PatchTarget::new);
- let patch_symbols = patch.symbols.as_slice();
-
- patch_target.add_symbols(patch_uuid, patch_symbols);
+ #[inline]
+ fn parse_process_id(proc_path: &Path) -> Option<i32> {
+ proc_path
+ .file_name()
+ .and_then(OsStr::to_str)
+ .map(str::parse)
+ .and_then(Result::ok)
}
- fn remove_patch_symbols(&mut self, patch: &UserPatch) {
- let patch_uuid = patch.uuid;
- let patch_symbols = patch.symbols.as_slice();
- let target_name = patch.target_elf.as_os_str();
-
- if let Some(patch_target) = self.patch_target_map.get_mut(target_name) {
- patch_target.remove_symbols(&patch_uuid, patch_symbols);
+ fn find_target_process<P: AsRef<Path>>(target_elf: P) -> Result<IndexSet<i32>> {
+ let mut target_pids = IndexSet::new();
+ let target_path = target_elf.as_ref();
+ let target_inode = target_path.metadata()?.st_ino();
+
+ for proc_path in fs::list_dirs("/proc", fs::TraverseOptions { recursive: false })? {
+ let pid = match Self::parse_process_id(&proc_path) {
+ Some(pid) => pid,
+ None => continue,
+ };
+ let exec_path = match fs::read_link(format!("/proc/{}/exe", pid)) {
+ Ok(file_path) => file_path,
+ Err(_) => continue,
+ };
+ // Try to match binary path
+ if exec_path == target_path {
+ target_pids.insert(pid);
+ continue;
+ }
+ // Try to match mapped files
+ let map_files = fs::list_symlinks(
+ format!("/proc/{}/map_files", pid),
+ fs::TraverseOptions { recursive: false },
+ )?;
+ for mapped_file in map_files {
+ if let Ok(mapped_inode) = mapped_file
+ .read_link()
+ .and_then(|file_path| Ok(file_path.metadata()?.st_ino()))
+ {
+ if mapped_inode == target_inode {
+ target_pids.insert(pid);
+ break;
+ }
+ };
+ }
}
+
+ Ok(target_pids)
}
-}
-impl UserPatchDriver {
- fn patch_new_process(elf_patch_map: ElfPatchMap, target_elf: &Path) {
- let process_list = match process::find_target_process(target_elf) {
- Ok(processes) => processes,
+ fn patch_new_process(
+ target_map: Arc<RwLock<IndexMap<PathBuf, PatchTarget>>>,
+ target_elf: &Path,
+ ) {
+ let process_list = match Self::find_target_process(target_elf) {
+ Ok(pids) => pids,
Err(_) => return,
};
- let mut patch_map = elf_patch_map.lock();
- let patch_record = match patch_map.get_mut(target_elf) {
- Some(record) => record,
+ let mut target_map = target_map.write();
+ let patch_target = match target_map.get_mut(target_elf) {
+ Some(target) => target,
None => return,
};
- let need_active = process_list
- .difference(&patch_record.processes)
- .copied()
- .collect::<Vec<_>>();
+ for (patch_uuid, patch_entity) in patch_target.all_patches() {
+ patch_entity.clean_dead_process(&process_list);
- // Active patch
- for (uuid, patch_file) in &patch_record.patch_map {
- if !need_active.is_empty() {
+ // Active patch
+ let need_actived = patch_entity.need_actived(&process_list);
+ if !need_actived.is_empty() {
debug!(
- "Upatch: Activating patch '{}' ({}) to process {:?}",
- uuid,
+ "Upatch: Activating patch '{}' ({}) for process {:?}",
+ patch_uuid,
target_elf.display(),
- need_active,
+ need_actived,
);
}
- for pid in &need_active {
- if let Err(e) = sys::active_patch(uuid, *pid, target_elf, patch_file)
- .with_context(|| format!("Failed to patch process, pid={}", pid))
- {
- error!("{}", e);
+
+ let ignore_list = patch_entity.need_ignored(&process_list);
+ for pid in need_actived {
+ if ignore_list.contains(&pid) {
continue;
}
- patch_record.processes.insert(*pid);
+ match sys::active_patch(patch_uuid, pid, target_elf, &patch_entity.patch_file) {
+ Ok(_) => patch_entity.add_process(pid),
+ Err(e) => {
+ warn!(
+ "Upatch: Failed to active patch '{}' for process {}, {}",
+ patch_uuid,
+ pid,
+ e.to_string().to_lowercase(),
+ );
+ patch_entity.ignore_process(pid)
+ }
+ }
}
}
-
- // Remove process no longer exists
- let need_remove = patch_record
- .processes
- .difference(&process_list)
- .copied()
- .collect::<Vec<_>>();
- for pid in need_remove {
- patch_record.processes.remove(&pid);
- }
- }
-}
-
-impl UserPatchDriver {
- #[inline]
- fn get_patch_status(&self, uuid: Uuid) -> Result<PatchStatus> {
- let patch_status = self
- .patch_status_map
- .get(&uuid)
- .copied()
- .context("Upatch: Patch does not exist")?;
-
- Ok(patch_status)
- }
-
- #[inline]
- fn set_patch_status(&mut self, uuid: Uuid, value: PatchStatus) -> Result<()> {
- let patch_status = self
- .patch_status_map
- .get_mut(&uuid)
- .context("Upatch: Patch does not exist")?;
-
- *patch_status = value;
- Ok(())
}
}
impl UserPatchDriver {
pub fn status(&self, patch: &UserPatch) -> Result<PatchStatus> {
- Ok(self
- .get_patch_status(patch.uuid)
- .unwrap_or(PatchStatus::NotApplied))
+ Ok(self.get_patch_status(&patch.uuid))
}
pub fn check(&self, patch: &UserPatch) -> Result<()> {
@@ -256,170 +282,189 @@ impl UserPatchDriver {
}
pub fn apply(&mut self, patch: &UserPatch) -> Result<()> {
- let patch_uuid = patch.uuid;
- ensure!(
- self.get_patch_status(patch_uuid).is_err(),
- "Upatch: Patch already exists"
- );
-
- debug!(
+ info!(
"Upatch: Applying patch '{}' ({})",
- patch_uuid,
+ patch.uuid,
patch.patch_file.display()
);
- self.patch_status_map
- .insert(patch_uuid, PatchStatus::Deactived);
+
+ self.add_patch_target(patch);
+ self.set_patch_status(&patch.uuid, PatchStatus::Deactived);
Ok(())
}
pub fn remove(&mut self, patch: &UserPatch) -> Result<()> {
- let patch_uuid = patch.uuid;
- let patch_status = self.get_patch_status(patch_uuid)?;
- ensure!(
- patch_status == PatchStatus::Deactived,
- "Upatch: Invalid patch status"
- );
-
- debug!(
+ info!(
"Upatch: Removing patch '{}' ({})",
- patch_uuid,
+ patch.uuid,
patch.patch_file.display()
);
- self.patch_status_map.remove(&patch_uuid);
+
+ self.remove_patch_target(patch);
+ self.remove_patch_status(&patch.uuid);
Ok(())
}
pub fn active(&mut self, patch: &UserPatch) -> Result<()> {
- let uuid = patch.uuid;
- let patch_status = self.get_patch_status(uuid)?;
- ensure!(
- patch_status == PatchStatus::Deactived,
- "Upatch: Invalid patch status"
- );
-
- let target_elf = patch.target_elf.as_path();
+ let patch_uuid = &patch.uuid;
let patch_file = patch.patch_file.as_path();
- let process_list = process::find_target_process(target_elf)?;
+ let patch_functions = patch.functions.as_slice();
+ let target_elf = patch.target_elf.as_path();
- let mut patch_map = self.elf_patch_map.lock();
- let patch_record = patch_map.entry(target_elf.to_path_buf()).or_default();
+ let process_list = Self::find_target_process(target_elf)?;
- let need_active = process_list
- .difference(&patch_record.processes)
- .copied()
- .collect::<Vec<_>>();
- let need_remove = patch_record
- .processes
- .difference(&process_list)
- .copied()
- .collect::<Vec<_>>();
- let mut need_start_watch = false;
+ let mut target_map = self.target_map.write();
+ let patch_target = target_map
+ .get_mut(target_elf)
+ .context("Upatch: Cannot find patch target")?;
+ let mut patch_entity = match patch_target.get_patch(patch_uuid) {
+ Some(_) => bail!("Upatch: Patch is already exist"),
+ None => PatchEntity::new(patch_file.to_path_buf()),
+ };
// Active patch
- if !need_active.is_empty() {
- debug!(
- "Upatch: Activating patch '{}' ({}) to process {:?}",
- uuid,
- target_elf.display(),
- need_active,
- );
- }
- for pid in need_active {
- sys::active_patch(&uuid, pid, target_elf, patch_file)
- .with_context(|| format!("Failed to patch process, pid={}", pid))?;
- patch_record.processes.insert(pid);
+ info!(
+ "Upatch: Activating patch '{}' ({}) for {}",
+ patch_uuid,
+ patch_file.display(),
+ target_elf.display(),
+ );
+ let mut results = Vec::new();
+ for pid in patch_entity.need_actived(&process_list) {
+ let result = sys::active_patch(patch_uuid, pid, target_elf, patch_file);
+ match result {
+ Ok(_) => patch_entity.add_process(pid),
+ Err(_) => patch_entity.ignore_process(pid),
+ }
+ results.push((pid, result));
}
- // Remove process no longer exists
- for pid in need_remove {
- patch_record.processes.remove(&pid);
+ // Check results, return error if all process fails
+ match results.iter().any(|(_, result)| result.is_ok()) {
+ true => {
+ for (pid, result) in &results {
+ if let Err(e) = result {
+ warn!(
+ "Upatch: Failed to active patch '{}' for process {}, {}",
+ patch_uuid,
+ pid,
+ e.to_string().to_lowercase(),
+ );
+ }
+ }
+ }
+ false => {
+ let mut err_msg = String::new();
+
+ writeln!(err_msg, "Upatch: Failed to active patch")?;
+ for (pid, result) in &results {
+ if let Err(e) = result {
+ writeln!(err_msg, "* Process {}: {}", pid, e)?;
+ }
+ }
+ bail!(err_msg);
+ }
}
- // If elf is not patched before, start watching it & add a new entry
- if !patch_record.patch_map.contains_key(&uuid) {
- patch_record
- .patch_map
- .insert(uuid, patch_file.to_path_buf());
- need_start_watch = true;
- }
+ // If target is no patched before, start watching it
+ let need_start_watch = !patch_target.is_patched();
- drop(patch_map);
+ // Apply patch to target
+ patch_target.add_patch(*patch_uuid, patch_entity);
+ patch_target.add_functions(*patch_uuid, patch_functions);
+
+ // Drop the lock
+ drop(target_map);
if need_start_watch {
- self.patch_monitor.watch_file(target_elf)?;
+ self.monitor.watch_file(target_elf)?;
}
- self.set_patch_status(uuid, PatchStatus::Actived)?;
- self.add_patch_symbols(patch);
+ self.set_patch_status(patch_uuid, PatchStatus::Actived);
Ok(())
}
pub fn deactive(&mut self, patch: &UserPatch) -> Result<()> {
- let uuid = patch.uuid;
- let patch_status = self.get_patch_status(uuid)?;
- ensure!(
- patch_status == PatchStatus::Actived,
- "Upatch: Invalid patch status"
- );
-
- let target_elf = patch.target_elf.as_path();
+ let patch_uuid = &patch.uuid;
let patch_file = patch.patch_file.as_path();
- let process_list = process::find_target_process(target_elf)?;
+ let patch_functions = patch.functions.as_slice();
+ let target_elf = patch.target_elf.as_path();
+
+ let process_list = Self::find_target_process(target_elf)?;
- let mut patch_map = self.elf_patch_map.lock();
- let patch_record = patch_map
+ let mut target_map = self.target_map.write();
+ let patch_target = target_map
.get_mut(target_elf)
- .context("Failed to find elf patch record")?;
+ .context("Upatch: Cannot find patch target")?;
+ let patch_entity = patch_target
+ .get_patch(patch_uuid)
+ .context("Upatch: Cannot find patch entity")?;
- let need_deactive = process_list
- .intersection(&patch_record.processes)
- .copied()
- .collect::<Vec<_>>();
- let need_removed = patch_record
- .processes
- .difference(&process_list)
- .copied()
- .collect::<Vec<_>>();
- let mut need_stop_watch = false;
+ // Remove dead process
+ patch_entity.clean_dead_process(&process_list);
// Deactive patch
- if !need_deactive.is_empty() {
- debug!(
- "Upatch: Deactivating patch '{}' ({}) of process {:?}",
- uuid,
- target_elf.display(),
- need_deactive,
- );
- }
- for pid in need_deactive {
- sys::deactive_patch(&uuid, pid, target_elf, patch_file)
- .with_context(|| format!("Failed to unpatch process, pid={}", pid))?;
- patch_record.processes.remove(&pid); // remove process from record
+ info!(
+ "Upatch: Deactivating patch '{}' ({}) for {}",
+ patch_uuid,
+ patch_file.display(),
+ target_elf.display(),
+ );
+ let mut results = Vec::new();
+ let ignore_list = patch_entity.need_ignored(&process_list);
+ for pid in patch_entity.need_deactived(&process_list) {
+ if ignore_list.contains(&pid) {
+ continue;
+ }
+ let result = sys::deactive_patch(patch_uuid, pid, target_elf, patch_file);
+ if result.is_ok() {
+ patch_entity.remove_process(pid)
+ }
+ results.push((pid, result));
}
- // Remove process no longer exists
- for pid in need_removed {
- patch_record.processes.remove(&pid);
+ // Check results, return error if any process failes
+ match results.iter().any(|(_, result)| result.is_err()) {
+ true => {
+ let mut err_msg = String::new();
+
+ writeln!(err_msg, "Upatch: Failed to deactive patch")?;
+ for (pid, result) in &results {
+ if let Err(e) = result {
+ writeln!(err_msg, "* Process {}: {}", pid, e)?;
+ }
+ }
+ bail!(err_msg)
+ }
+ false => {
+ for (pid, result) in &results {
+ if let Err(e) = result {
+ warn!(
+ "Upatch: Failed to deactive patch '{}' for process {}, {}",
+ patch_uuid,
+ pid,
+ e.to_string().to_lowercase(),
+ );
+ }
+ }
+ }
}
- // Remove patch from elf patch record
- patch_record.patch_map.remove(&uuid);
+ // Remove patch functions from target
+ patch_target.remove_patch(patch_uuid);
+ patch_target.remove_functions(patch_uuid, patch_functions);
- // If elf has no more patch, stop watching it & remove the entry
- if patch_record.patch_map.is_empty() {
- patch_map.remove(target_elf);
- need_stop_watch = true;
- }
+ // If target is no longer patched, stop watching it
+ let need_stop_watch = !patch_target.is_patched();
- drop(patch_map);
+ drop(target_map);
if need_stop_watch {
- self.patch_monitor.ignore_file(target_elf)?;
+ self.monitor.ignore_file(target_elf)?;
}
- self.set_patch_status(uuid, PatchStatus::Deactived)?;
- self.remove_patch_symbols(patch);
+ self.set_patch_status(patch_uuid, PatchStatus::Deactived);
Ok(())
}
diff --git a/syscared/src/patch/driver/upatch/monitor.rs b/syscared/src/patch/driver/upatch/monitor.rs
index 1dbb513..09df2a2 100644
--- a/syscared/src/patch/driver/upatch/monitor.rs
+++ b/syscared/src/patch/driver/upatch/monitor.rs
@@ -21,12 +21,13 @@ use std::{
};
use anyhow::{bail, Context, Result};
-use indexmap::IndexMap;
-use inotify::{EventMask, Inotify, WatchDescriptor, WatchMask};
+use indexmap::{IndexMap, IndexSet};
+use inotify::{Inotify, WatchDescriptor, WatchMask};
use log::info;
use parking_lot::{Mutex, RwLock};
+use syscare_common::ffi::OsStrExt;
-use super::ElfPatchMap;
+use super::target::PatchTarget;
const MONITOR_THREAD_NAME: &str = "upatch_monitor";
const MONITOR_CHECK_PERIOD: u64 = 100;
@@ -34,33 +35,36 @@ const MONITOR_EVENT_BUFFER_CAPACITY: usize = 16 * 64; // inotify event size: 16
pub(super) struct UserPatchMonitor {
inotify: Arc<Mutex<Option<Inotify>>>,
- watch_map: Arc<Mutex<IndexMap<PathBuf, WatchDescriptor>>>,
- target_map: Arc<RwLock<IndexMap<WatchDescriptor, PathBuf>>>,
+ watch_wd_map: Arc<Mutex<IndexMap<PathBuf, WatchDescriptor>>>,
+ watch_file_map: Arc<RwLock<IndexMap<WatchDescriptor, PathBuf>>>,
monitor_thread: Option<thread::JoinHandle<()>>,
}
impl UserPatchMonitor {
- pub fn new<F>(elf_patch_map: ElfPatchMap, callback: F) -> Result<Self>
+ pub fn new<F>(
+ patch_target_map: Arc<RwLock<IndexMap<PathBuf, PatchTarget>>>,
+ callback: F,
+ ) -> Result<Self>
where
- F: Fn(ElfPatchMap, &Path) + Send + Sync + 'static,
+ F: Fn(Arc<RwLock<IndexMap<PathBuf, PatchTarget>>>, &Path) + Send + Sync + 'static,
{
let inotify = Arc::new(Mutex::new(Some(
Inotify::init().context("Failed to initialize inotify")?,
)));
- let watch_map = Arc::new(Mutex::new(IndexMap::new()));
- let target_map = Arc::new(RwLock::new(IndexMap::new()));
+ let watch_wd_map = Arc::new(Mutex::new(IndexMap::new()));
+ let watch_file_map = Arc::new(RwLock::new(IndexMap::new()));
let monitor_thread = MonitorThread {
inotify: inotify.clone(),
- target_map: target_map.clone(),
- elf_patch_map,
+ watch_file_map: watch_file_map.clone(),
+ patch_target_map,
callback,
}
.run()?;
Ok(Self {
inotify,
- target_map,
- watch_map,
+ watch_wd_map,
+ watch_file_map,
monitor_thread: Some(monitor_thread),
})
}
@@ -69,7 +73,7 @@ impl UserPatchMonitor {
impl UserPatchMonitor {
pub fn watch_file<P: AsRef<Path>>(&self, file_path: P) -> Result<()> {
let watch_file = file_path.as_ref();
- if self.watch_map.lock().contains_key(watch_file) {
+ if self.watch_wd_map.lock().contains_key(watch_file) {
return Ok(());
}
@@ -79,10 +83,10 @@ impl UserPatchMonitor {
.add_watch(watch_file, WatchMask::OPEN)
.with_context(|| format!("Failed to watch file {}", watch_file.display()))?;
- self.target_map
+ self.watch_file_map
.write()
.insert(wd.clone(), watch_file.to_owned());
- self.watch_map.lock().insert(watch_file.to_owned(), wd);
+ self.watch_wd_map.lock().insert(watch_file.to_owned(), wd);
info!("Start watching file {}", watch_file.display());
}
None => bail!("Inotify does not exist"),
@@ -94,10 +98,10 @@ impl UserPatchMonitor {
pub fn ignore_file<P: AsRef<Path>>(&self, file_path: P) -> Result<()> {
let ignore_file = file_path.as_ref();
- if let Some(wd) = self.watch_map.lock().remove(ignore_file) {
+ if let Some(wd) = self.watch_wd_map.lock().remove(ignore_file) {
match self.inotify.lock().as_mut() {
Some(inotify) => {
- self.target_map.write().remove(&wd);
+ self.watch_file_map.write().remove(&wd);
inotify.rm_watch(wd).with_context(|| {
format!("Failed to stop watch file {}", ignore_file.display())
@@ -114,14 +118,14 @@ impl UserPatchMonitor {
struct MonitorThread<F> {
inotify: Arc<Mutex<Option<Inotify>>>,
- target_map: Arc<RwLock<IndexMap<WatchDescriptor, PathBuf>>>,
- elf_patch_map: ElfPatchMap,
+ watch_file_map: Arc<RwLock<IndexMap<WatchDescriptor, PathBuf>>>,
+ patch_target_map: Arc<RwLock<IndexMap<PathBuf, PatchTarget>>>,
callback: F,
}
impl<F> MonitorThread<F>
where
- F: Fn(ElfPatchMap, &Path) + Send + Sync + 'static,
+ F: Fn(Arc<RwLock<IndexMap<PathBuf, PatchTarget>>>, &Path) + Send + Sync + 'static,
{
fn run(self) -> Result<thread::JoinHandle<()>> {
thread::Builder::new()
@@ -130,18 +134,30 @@ where
.with_context(|| format!("Failed to create thread '{}'", MONITOR_THREAD_NAME))
}
+ #[inline]
+ fn filter_blacklist_path(path: &Path) -> bool {
+ const BLACKLIST_KEYWORDS: [&str; 2] = ["syscare", "upatch"];
+
+ for keyword in BLACKLIST_KEYWORDS {
+ if path.contains(keyword) {
+ return false;
+ }
+ }
+ true
+ }
+
fn thread_main(self) {
while let Some(inotify) = self.inotify.lock().as_mut() {
let mut buffer = [0; MONITOR_EVENT_BUFFER_CAPACITY];
if let Ok(events) = inotify.read_events(&mut buffer) {
- for event in events {
- if !event.mask.contains(EventMask::OPEN) {
- continue;
- }
- if let Some(patch_file) = self.target_map.read().get(&event.wd) {
- (self.callback)(self.elf_patch_map.clone(), patch_file);
- }
+ let watch_file_map = self.watch_file_map.read();
+ let target_elfs = events
+ .filter_map(|event| watch_file_map.get(&event.wd))
+ .filter(|path| Self::filter_blacklist_path(path))
+ .collect::<IndexSet<_>>();
+ for target_elf in target_elfs {
+ (self.callback)(self.patch_target_map.clone(), target_elf);
}
}
diff --git a/syscared/src/patch/driver/upatch/process.rs b/syscared/src/patch/driver/upatch/process.rs
deleted file mode 100644
index 9b2f6de..0000000
--- a/syscared/src/patch/driver/upatch/process.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-// SPDX-License-Identifier: Mulan PSL v2
-/*
- * Copyright (c) 2024 Huawei Technologies Co., Ltd.
- * syscared is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-use std::{ffi::OsStr, os::linux::fs::MetadataExt, path::Path};
-
-use anyhow::Result;
-use indexmap::IndexSet;
-use syscare_common::fs;
-
-const PROC_BLACK_LIST: [&str; 18] = [
- "/usr/lib/systemd/systemd-journald",
- "/usr/lib/systemd/systemd-logind",
- "/usr/lib/systemd/systemd-udevd",
- "/usr/lib/systemd/systemd-hostnamed",
- "/usr/bin/udevadm",
- "/usr/sbin/auditd",
- "/usr/bin/syscare",
- "/usr/bin/syscared",
- "/usr/bin/upatchd",
- "/usr/libexec/syscare/as-hijacker",
- "/usr/libexec/syscare/cc-hijacker",
- "/usr/libexec/syscare/c++-hijacker",
- "/usr/libexec/syscare/gcc-hijacker",
- "/usr/libexec/syscare/g++-hijacker",
- "/usr/libexec/syscare/syscare-build",
- "/usr/libexec/syscare/upatch-build",
- "/usr/libexec/syscare/upatch-diff",
- "/usr/libexec/syscare/upatch-manage",
-];
-
-#[inline]
-fn is_blacklisted(file_path: &Path) -> bool {
- PROC_BLACK_LIST
- .iter()
- .map(Path::new)
- .any(|blacklist_path| blacklist_path == file_path)
-}
-
-#[inline]
-fn parse_process_id(proc_path: &Path) -> Option<i32> {
- proc_path
- .file_name()
- .and_then(OsStr::to_str)
- .map(str::parse)
- .and_then(Result::ok)
-}
-
-pub fn find_target_process<P: AsRef<Path>>(target_elf: P) -> Result<IndexSet<i32>> {
- let mut target_pids = IndexSet::new();
- let target_path = target_elf.as_ref();
- let target_inode = target_path.metadata()?.st_ino();
-
- for proc_path in fs::list_dirs("/proc", fs::TraverseOptions { recursive: false })? {
- let pid = match self::parse_process_id(&proc_path) {
- Some(pid) => pid,
- None => continue,
- };
- let exec_path = match fs::read_link(format!("/proc/{}/exe", pid)) {
- Ok(file_path) => file_path,
- Err(_) => continue,
- };
- if is_blacklisted(&exec_path) {
- continue;
- }
- // Try to match binary path
- if exec_path == target_path {
- target_pids.insert(pid);
- continue;
- }
- // Try to match mapped files
- let map_files = fs::list_symlinks(
- format!("/proc/{}/map_files", pid),
- fs::TraverseOptions { recursive: false },
- )?;
- for mapped_file in map_files {
- if let Ok(mapped_inode) = mapped_file
- .read_link()
- .and_then(|file_path| Ok(file_path.metadata()?.st_ino()))
- {
- if mapped_inode == target_inode {
- target_pids.insert(pid);
- break;
- }
- };
- }
- }
-
- Ok(target_pids)
-}
diff --git a/syscared/src/patch/driver/upatch/sys.rs b/syscared/src/patch/driver/upatch/sys.rs
index f0745f0..bfeb1b8 100644
--- a/syscared/src/patch/driver/upatch/sys.rs
+++ b/syscared/src/patch/driver/upatch/sys.rs
@@ -25,12 +25,10 @@ pub fn active_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Path)
.exit_code();
match exit_code {
- 0 => {}
- EEXIST => {}
- _ => bail!("Upatch: {}", std::io::Error::from_raw_os_error(exit_code)),
+ 0 => Ok(()),
+ EEXIST => Ok(()),
+ _ => bail!(std::io::Error::from_raw_os_error(exit_code)),
}
-
- Ok(())
}
pub fn deactive_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Path) -> Result<()> {
@@ -49,9 +47,7 @@ pub fn deactive_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Pat
.exit_code();
match exit_code {
- 0 => {}
- _ => bail!("Upatch: {}", std::io::Error::from_raw_os_error(exit_code)),
+ 0 => Ok(()),
+ _ => bail!(std::io::Error::from_raw_os_error(exit_code)),
}
-
- Ok(())
}
diff --git a/syscared/src/patch/driver/upatch/target.rs b/syscared/src/patch/driver/upatch/target.rs
index df1e075..26c3ed3 100644
--- a/syscared/src/patch/driver/upatch/target.rs
+++ b/syscared/src/patch/driver/upatch/target.rs
@@ -17,98 +17,120 @@ use std::ffi::OsString;
use indexmap::IndexMap;
use uuid::Uuid;
-use crate::patch::entity::UserPatchSymbol;
+use crate::patch::entity::UserPatchFunction;
-#[derive(Debug, PartialEq)]
-pub struct PatchTargetRecord {
+use super::entity::PatchEntity;
+
+#[derive(Debug)]
+pub struct PatchFunction {
pub uuid: Uuid,
pub name: OsString,
pub size: u64,
}
+impl PatchFunction {
+ pub fn new(uuid: Uuid, function: &UserPatchFunction) -> Self {
+ Self {
+ uuid,
+ name: function.name.to_os_string(),
+ size: function.new_size,
+ }
+ }
+
+ pub fn is_same_function(&self, uuid: &Uuid, function: &UserPatchFunction) -> bool {
+ (self.uuid == *uuid) && (self.name == function.name) && (self.size == function.new_size)
+ }
+}
+
+#[derive(Debug, Default)]
pub struct PatchTarget {
- symbol_map: IndexMap<u64, Vec<PatchTargetRecord>>, // symbol addr -> symbol collision list
+ patch_map: IndexMap<Uuid, PatchEntity>, // patched file data
+ function_map: IndexMap<u64, Vec<PatchFunction>>, // function addr -> function collision list
}
impl PatchTarget {
- fn match_record(record: &PatchTargetRecord, uuid: &Uuid, symbol: &UserPatchSymbol) -> bool {
- (record.uuid == *uuid) && (record.name == symbol.name) && (record.size == symbol.new_size)
+ pub fn is_patched(&self) -> bool {
+ !self.patch_map.is_empty()
}
-}
-impl PatchTarget {
- pub fn new() -> Self {
- Self {
- symbol_map: IndexMap::new(),
- }
+ pub fn add_patch(&mut self, uuid: Uuid, entity: PatchEntity) {
+ self.patch_map.insert(uuid, entity);
}
- pub fn get_conflicts<'a, I>(
- &'a self,
- symbols: I,
- ) -> impl IntoIterator<Item = &'a PatchTargetRecord>
- where
- I: IntoIterator<Item = &'a UserPatchSymbol>,
- {
- symbols.into_iter().filter_map(move |symbol| {
- self.symbol_map
- .get(&symbol.old_addr)
- .and_then(|list| list.last())
- })
+ pub fn remove_patch(&mut self, uuid: &Uuid) {
+ self.patch_map.remove(uuid);
}
- pub fn get_overrides<'a, I>(
- &'a self,
- uuid: &'a Uuid,
- symbols: I,
- ) -> impl IntoIterator<Item = &'a PatchTargetRecord>
- where
- I: IntoIterator<Item = &'a UserPatchSymbol>,
- {
- symbols.into_iter().filter_map(move |symbol| {
- self.symbol_map
- .get(&symbol.old_addr)
- .and_then(|list| list.last())
- .filter(|record| !Self::match_record(record, uuid, symbol))
- })
+ pub fn get_patch(&mut self, uuid: &Uuid) -> Option<&mut PatchEntity> {
+ self.patch_map.get_mut(uuid)
+ }
+
+ pub fn all_patches(&mut self) -> impl IntoIterator<Item = (&Uuid, &mut PatchEntity)> {
+ self.patch_map.iter_mut()
}
+}
- pub fn add_symbols<'a, I>(&mut self, uuid: Uuid, symbols: I)
+impl PatchTarget {
+ pub fn add_functions<'a, I>(&mut self, uuid: Uuid, functions: I)
where
- I: IntoIterator<Item = &'a UserPatchSymbol>,
+ I: IntoIterator<Item = &'a UserPatchFunction>,
{
- for symbol in symbols {
- let symbol_addr = symbol.old_addr;
- let symbol_record = PatchTargetRecord {
- uuid,
- name: symbol.name.to_os_string(),
- size: symbol.new_size,
- };
-
- self.symbol_map
- .entry(symbol_addr)
+ for function in functions {
+ self.function_map
+ .entry(function.old_addr)
.or_default()
- .push(symbol_record);
+ .push(PatchFunction::new(uuid, function));
}
}
- pub fn remove_symbols<'a, I>(&mut self, uuid: &Uuid, symbols: I)
+ pub fn remove_functions<'a, I>(&mut self, uuid: &Uuid, functions: I)
where
- I: IntoIterator<Item = &'a UserPatchSymbol>,
+ I: IntoIterator<Item = &'a UserPatchFunction>,
{
- for symbol in symbols {
- let symbol_addr = symbol.old_addr;
- if let Some(collision_list) = self.symbol_map.get_mut(&symbol_addr) {
+ for function in functions {
+ if let Some(collision_list) = self.function_map.get_mut(&function.old_addr) {
if let Some(index) = collision_list
.iter()
- .position(|record| Self::match_record(record, uuid, symbol))
+ .position(|patch_function| patch_function.is_same_function(uuid, function))
{
collision_list.remove(index);
if collision_list.is_empty() {
- self.symbol_map.remove(&symbol_addr);
+ self.function_map.remove(&function.old_addr);
}
}
}
}
}
}
+
+impl PatchTarget {
+ pub fn get_conflicts<'a, I>(
+ &'a self,
+ functions: I,
+ ) -> impl IntoIterator<Item = &'a PatchFunction>
+ where
+ I: IntoIterator<Item = &'a UserPatchFunction>,
+ {
+ functions.into_iter().filter_map(move |function| {
+ self.function_map
+ .get(&function.old_addr)
+ .and_then(|list| list.last())
+ })
+ }
+
+ pub fn get_overrides<'a, I>(
+ &'a self,
+ uuid: &'a Uuid,
+ functions: I,
+ ) -> impl IntoIterator<Item = &'a PatchFunction>
+ where
+ I: IntoIterator<Item = &'a UserPatchFunction>,
+ {
+ functions.into_iter().filter_map(move |function| {
+ self.function_map
+ .get(&function.old_addr)
+ .and_then(|list| list.last())
+ .filter(|patch_function| !patch_function.is_same_function(uuid, function))
+ })
+ }
+}
diff --git a/syscared/src/patch/entity/kpatch.rs b/syscared/src/patch/entity/kpatch.rs
index 9399920..ab2c8b2 100644
--- a/syscared/src/patch/entity/kpatch.rs
+++ b/syscared/src/patch/entity/kpatch.rs
@@ -17,22 +17,22 @@ use std::{ffi::OsString, path::PathBuf, sync::Arc};
use syscare_abi::{PatchInfo, PatchType};
use uuid::Uuid;
-/// Kernel patch symbol definition
+/// Kernel patch function definition
#[derive(Clone)]
-pub struct KernelPatchSymbol {
+pub struct KernelPatchFunction {
pub name: OsString,
- pub target: OsString,
+ pub object: OsString,
pub old_addr: u64,
pub old_size: u64,
pub new_addr: u64,
pub new_size: u64,
}
-impl std::fmt::Debug for KernelPatchSymbol {
+impl std::fmt::Debug for KernelPatchFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("KernelPatchSymbol")
+ f.debug_struct("KernelPatchFunction")
.field("name", &self.name)
- .field("target", &self.target)
+ .field("object", &self.object)
.field("old_addr", &format!("{:#x}", self.old_addr))
.field("old_size", &format!("{:#x}", self.old_size))
.field("new_addr", &format!("{:#x}", self.new_addr))
@@ -41,13 +41,13 @@ impl std::fmt::Debug for KernelPatchSymbol {
}
}
-impl std::fmt::Display for KernelPatchSymbol {
+impl std::fmt::Display for KernelPatchFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
- "name: {}, target: {}, old_addr: {:#x}, old_size: {:#x}, new_addr: {:#x}, new_size: {:#x}",
+ "name: {}, object: {}, old_addr: {:#x}, old_size: {:#x}, new_addr: {:#x}, new_size: {:#x}",
self.name.to_string_lossy(),
- self.target.to_string_lossy(),
+ self.object.to_string_lossy(),
self.old_addr,
self.old_size,
self.new_addr,
@@ -65,7 +65,7 @@ pub struct KernelPatch {
pub info: Arc<PatchInfo>,
pub pkg_name: String,
pub module_name: OsString,
- pub symbols: Vec<KernelPatchSymbol>,
+ pub functions: Vec<KernelPatchFunction>,
pub patch_file: PathBuf,
pub sys_file: PathBuf,
pub checksum: String,
diff --git a/syscared/src/patch/entity/symbol.rs b/syscared/src/patch/entity/symbol.rs
index 300775a..c7845aa 100644
--- a/syscared/src/patch/entity/symbol.rs
+++ b/syscared/src/patch/entity/symbol.rs
@@ -14,9 +14,9 @@
use std::ffi::OsString;
-/// Patch symbol definiation
+/// Patch function definiation
#[derive(Clone)]
-pub struct PatchSymbol {
+pub struct PatchFunction {
pub name: OsString,
pub target: OsString,
pub old_addr: u64,
@@ -25,9 +25,9 @@ pub struct PatchSymbol {
pub new_size: u64,
}
-impl std::fmt::Debug for PatchSymbol {
+impl std::fmt::Debug for PatchFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("PatchSymbol")
+ f.debug_struct("PatchFunction")
.field("name", &self.name)
.field("target", &self.target)
.field("old_addr", &format!("0x{}", self.old_addr))
@@ -38,7 +38,7 @@ impl std::fmt::Debug for PatchSymbol {
}
}
-impl std::fmt::Display for PatchSymbol {
+impl std::fmt::Display for PatchFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
diff --git a/syscared/src/patch/entity/upatch.rs b/syscared/src/patch/entity/upatch.rs
index 4e62431..ef24866 100644
--- a/syscared/src/patch/entity/upatch.rs
+++ b/syscared/src/patch/entity/upatch.rs
@@ -17,9 +17,9 @@ use std::{ffi::OsString, path::PathBuf, sync::Arc};
use syscare_abi::{PatchInfo, PatchType};
use uuid::Uuid;
-/// User patch symbol definition
+/// User patch function definition
#[derive(Clone)]
-pub struct UserPatchSymbol {
+pub struct UserPatchFunction {
pub name: OsString,
pub old_addr: u64,
pub old_size: u64,
@@ -27,9 +27,9 @@ pub struct UserPatchSymbol {
pub new_size: u64,
}
-impl std::fmt::Debug for UserPatchSymbol {
+impl std::fmt::Debug for UserPatchFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("UserPatchSymbol")
+ f.debug_struct("UserPatchFunction")
.field("name", &self.name)
.field("old_addr", &format!("0x{}", self.old_addr))
.field("old_size", &format!("0x{}", self.old_size))
@@ -39,7 +39,7 @@ impl std::fmt::Debug for UserPatchSymbol {
}
}
-impl std::fmt::Display for UserPatchSymbol {
+impl std::fmt::Display for UserPatchFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
@@ -61,7 +61,7 @@ pub struct UserPatch {
pub kind: PatchType,
pub info: Arc<PatchInfo>,
pub pkg_name: String,
- pub symbols: Vec<UserPatchSymbol>,
+ pub functions: Vec<UserPatchFunction>,
pub patch_file: PathBuf,
pub target_elf: PathBuf,
pub checksum: String,
diff --git a/syscared/src/patch/resolver/kpatch.rs b/syscared/src/patch/resolver/kpatch.rs
index 7de81b3..8738225 100644
2024-04-19 16:03:21 +08:00
--- a/syscared/src/patch/resolver/kpatch.rs
+++ b/syscared/src/patch/resolver/kpatch.rs
@@ -13,8 +13,7 @@
*/
use std::{
- ffi::OsString,
- os::unix::ffi::OsStringExt,
+ ffi::{CStr, OsString},
path::{Path, PathBuf},
str::FromStr,
2024-04-19 16:03:21 +08:00
sync::Arc,
@@ -25,10 +24,14 @@ use object::{NativeFile, Object, ObjectSection};
use uuid::Uuid;
2024-04-19 16:03:21 +08:00
use syscare_abi::{PatchEntity, PatchInfo, PatchType};
-use syscare_common::{concat_os, ffi::OsStrExt, fs};
+use syscare_common::{
+ concat_os,
+ ffi::{CStrExt, OsStrExt},
+ fs,
+};
use super::PatchResolverImpl;
-use crate::patch::entity::{KernelPatch, KernelPatchSymbol, Patch};
+use crate::patch::entity::{KernelPatch, KernelPatchFunction, Patch};
const KPATCH_SUFFIX: &str = ".ko";
const KPATCH_SYS_DIR: &str = "/sys/kernel/livepatch";
@@ -37,7 +40,10 @@ const KPATCH_SYS_FILE_NAME: &str = "enabled";
2024-04-19 16:03:21 +08:00
mod ffi {
use std::os::raw::{c_char, c_long, c_ulong};
- use object::Pod;
+ use object::{
+ read::elf::{ElfSectionRelocationIterator, FileHeader},
+ Pod, Relocation,
+ };
#[repr(C)]
#[derive(Debug, Clone, Copy)]
@@ -54,9 +60,9 @@ mod ffi {
2024-04-19 16:03:21 +08:00
pub ref_offset: c_long,
}
- pub const KPATCH_FUNC_SIZE: usize = std::mem::size_of::<KpatchFunction>();
- pub const KPATCH_FUNC_NAME_OFFSET: usize = 40;
- pub const KPATCH_OBJECT_NAME_OFFSET: usize = 48;
+ pub const KPATCH_FUNCTION_SIZE: usize = std::mem::size_of::<KpatchFunction>();
+ pub const KPATCH_FUNCTION_OFFSET: usize = 40;
+ pub const KPATCH_OBJECT_OFFSET: usize = 48;
/*
* SAFETY: This struct is
@@ -66,24 +72,34 @@ mod ffi {
2024-04-19 16:03:21 +08:00
*/
unsafe impl Pod for KpatchFunction {}
- pub enum KpatchRelocation {
- NewAddr = 0,
- Name = 1,
- ObjName = 2,
+ pub struct KpatchRelocation {
+ pub addr: (u64, Relocation),
+ pub name: (u64, Relocation),
+ pub object: (u64, Relocation),
}
- impl From<usize> for KpatchRelocation {
- fn from(value: usize) -> Self {
- match value {
- 0 => KpatchRelocation::NewAddr,
- 1 => KpatchRelocation::Name,
- 2 => KpatchRelocation::ObjName,
- _ => unreachable!(),
- }
+ pub struct KpatchRelocationIterator<'data, 'file, Elf: FileHeader>(
+ ElfSectionRelocationIterator<'data, 'file, Elf, &'data [u8]>,
+ );
+
+ impl<'data, 'file, Elf: FileHeader> KpatchRelocationIterator<'data, 'file, Elf> {
+ pub fn new(relocations: ElfSectionRelocationIterator<'data, 'file, Elf>) -> Self {
+ Self(relocations)
}
}
- pub const KPATCH_FUNC_RELA_TYPE_NUM: usize = 3;
+ impl<'data, 'file, Elf: FileHeader> Iterator for KpatchRelocationIterator<'data, 'file, Elf> {
+ type Item = KpatchRelocation;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let (Some(addr), Some(name), Some(object)) =
+ (self.0.next(), self.0.next(), self.0.next())
+ {
+ return Some(KpatchRelocation { addr, name, object });
+ }
+ None
+ }
+ }
}
use ffi::*;
@@ -115,19 +131,19 @@ impl KpatchResolverImpl {
2024-04-19 16:03:21 +08:00
.with_context(|| format!("Failed to read section '{}'", KPATCH_FUNCS_SECTION))?;
// Resolve patch functions
- let patch_symbols = &mut patch.symbols;
- let patch_functions = object::slice_from_bytes::<KpatchFunction>(
+ let patch_functions = &mut patch.functions;
+ let kpatch_function_slice = object::slice_from_bytes::<KpatchFunction>(
function_data,
- function_data.len() / KPATCH_FUNC_SIZE,
+ function_data.len() / KPATCH_FUNCTION_SIZE,
)
.map(|(f, _)| f)
.map_err(|_| anyhow!("Invalid data format"))
.context("Failed to resolve patch functions")?;
- for function in patch_functions {
- patch_symbols.push(KernelPatchSymbol {
+ for function in kpatch_function_slice {
+ patch_functions.push(KernelPatchFunction {
name: OsString::new(),
- target: OsString::new(),
+ object: OsString::new(),
old_addr: function.old_addr,
old_size: function.old_size,
new_addr: function.new_addr,
@@ -136,44 +152,35 @@ impl KpatchResolverImpl {
2024-04-19 16:03:21 +08:00
}
// Relocate patch functions
- for (index, (offset, relocation)) in function_section.relocations().enumerate() {
- match KpatchRelocation::from(index % KPATCH_FUNC_RELA_TYPE_NUM) {
- KpatchRelocation::Name => {
- let symbol_index =
- (offset as usize - KPATCH_FUNC_NAME_OFFSET) / KPATCH_FUNC_SIZE;
- let patch_symbol = patch_symbols
- .get_mut(symbol_index)
- .context("Failed to find patch symbol")?;
-
- let name_offset = relocation.addend() as usize;
- let mut name_bytes = &string_data[name_offset..];
- let string_end = name_bytes
- .iter()
- .position(|b| b == &b'\0')
- .context("Failed to find termination char")?;
- name_bytes = &name_bytes[..string_end];
-
- patch_symbol.name = OsString::from_vec(name_bytes.to_vec());
- }
- KpatchRelocation::ObjName => {
- let symbol_index =
- (offset as usize - KPATCH_OBJECT_NAME_OFFSET) / KPATCH_FUNC_SIZE;
- let patch_symbol = patch_symbols
- .get_mut(symbol_index)
- .context("Failed to find patch symbol")?;
-
- let name_offset = relocation.addend() as usize;
- let mut name_bytes = &string_data[name_offset..];
- let string_end = name_bytes
- .iter()
- .position(|b| b == &b'\0')
- .context("Failed to find termination char")?;
- name_bytes = &name_bytes[..string_end];
-
- patch_symbol.target = OsString::from_vec(name_bytes.to_vec());
- }
- _ => {}
- };
+ for relocation in KpatchRelocationIterator::new(function_section.relocations()) {
+ let (name_reloc_offset, name_reloc) = relocation.name;
+ let (object_reloc_offset, obj_reloc) = relocation.object;
+
+ // Relocate patch function name
+ let name_index =
+ (name_reloc_offset as usize - KPATCH_FUNCTION_OFFSET) / KPATCH_FUNCTION_SIZE;
+ let name_function = patch_functions
+ .get_mut(name_index)
+ .context("Failed to find patch function")?;
+ let name_offset = name_reloc.addend() as usize;
+ let name_string = CStr::from_bytes_with_next_nul(&string_data[name_offset..])
+ .context("Failed to parse patch object name")?
+ .to_os_string();
+
+ name_function.name = name_string;
+
+ // Relocate patch function object
+ let object_index =
+ (object_reloc_offset as usize - KPATCH_OBJECT_OFFSET) / KPATCH_FUNCTION_SIZE;
+ let object_function = patch_functions
+ .get_mut(object_index)
+ .context("Failed to find patch function")?;
+ let object_offset = obj_reloc.addend() as usize;
+ let object_string = CStr::from_bytes_with_next_nul(&string_data[object_offset..])
+ .context("Failed to parse patch function name")?
+ .to_os_string();
+
+ object_function.object = object_string;
}
Ok(())
@@ -208,10 +215,10 @@ impl PatchResolverImpl for KpatchResolverImpl {
2024-04-19 16:03:21 +08:00
module_name,
patch_file,
sys_file,
- symbols: Vec::new(),
+ functions: Vec::new(),
checksum: patch_entity.checksum.clone(),
};
- Self::resolve_patch_file(&mut patch).context("Failed to resolve patch elf")?;
+ Self::resolve_patch_file(&mut patch).context("Failed to resolve patch")?;
Ok(Patch::KernelPatch(patch))
}
diff --git a/syscared/src/patch/resolver/upatch.rs b/syscared/src/patch/resolver/upatch.rs
index 507bf8e..985b8f1 100644
2024-04-19 16:03:21 +08:00
--- a/syscared/src/patch/resolver/upatch.rs
+++ b/syscared/src/patch/resolver/upatch.rs
@@ -12,22 +12,30 @@
2024-04-19 16:03:21 +08:00
* See the Mulan PSL v2 for more details.
*/
-use std::{ffi::OsString, os::unix::ffi::OsStringExt, path::Path, str::FromStr, sync::Arc};
2024-04-19 16:03:21 +08:00
+use std::{
+ ffi::{CStr, OsString},
+ path::Path,
+ str::FromStr,
2024-04-19 16:03:21 +08:00
+ sync::Arc,
+};
use anyhow::{anyhow, Context, Result};
use object::{NativeFile, Object, ObjectSection};
use uuid::Uuid;
2024-04-19 16:03:21 +08:00
use syscare_abi::{PatchEntity, PatchInfo, PatchType};
-use syscare_common::{concat_os, fs};
+use syscare_common::{concat_os, ffi::CStrExt, fs};
use super::PatchResolverImpl;
-use crate::patch::entity::{Patch, UserPatch, UserPatchSymbol};
+use crate::patch::entity::{Patch, UserPatch, UserPatchFunction};
mod ffi {
use std::os::raw::{c_char, c_ulong};
- use object::Pod;
+ use object::{
+ read::elf::{ElfSectionRelocationIterator, FileHeader},
+ Pod, Relocation,
+ };
#[repr(C)]
#[derive(Debug, Clone, Copy)]
@@ -49,25 +57,34 @@ mod ffi {
2024-04-19 16:03:21 +08:00
*/
unsafe impl Pod for UpatchFunction {}
- pub const UPATCH_FUNC_SIZE: usize = std::mem::size_of::<UpatchFunction>();
- pub const UPATCH_FUNC_NAME_OFFSET: usize = 40;
+ pub const UPATCH_FUNCTION_SIZE: usize = std::mem::size_of::<UpatchFunction>();
+ pub const UPATCH_FUNCTION_OFFSET: usize = 40;
- pub enum UpatchRelocation {
- NewAddr = 0,
- Name = 1,
+ pub struct UpatchRelocation {
+ pub addr: (u64, Relocation),
+ pub name: (u64, Relocation),
}
- impl From<usize> for UpatchRelocation {
- fn from(value: usize) -> Self {
- match value {
- 0 => UpatchRelocation::NewAddr,
- 1 => UpatchRelocation::Name,
- _ => unreachable!(),
- }
+ pub struct UpatchRelocationIterator<'data, 'file, Elf: FileHeader>(
+ ElfSectionRelocationIterator<'data, 'file, Elf, &'data [u8]>,
+ );
+
+ impl<'data, 'file, Elf: FileHeader> UpatchRelocationIterator<'data, 'file, Elf> {
+ pub fn new(relocations: ElfSectionRelocationIterator<'data, 'file, Elf>) -> Self {
+ Self(relocations)
}
}
- pub const UPATCH_FUNC_RELA_TYPE_NUM: usize = 2;
+ impl<'data, 'file, Elf: FileHeader> Iterator for UpatchRelocationIterator<'data, 'file, Elf> {
+ type Item = UpatchRelocation;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let (Some(addr), Some(name)) = (self.0.next(), self.0.next()) {
+ return Some(UpatchRelocation { addr, name });
+ }
+ None
+ }
+ }
}
use ffi::*;
@@ -99,17 +116,17 @@ impl UpatchResolverImpl {
2024-04-19 16:03:21 +08:00
.with_context(|| format!("Failed to read section '{}'", UPATCH_FUNCS_SECTION))?;
// Resolve patch functions
- let patch_symbols = &mut patch.symbols;
- let patch_functions = object::slice_from_bytes::<UpatchFunction>(
+ let patch_functions = &mut patch.functions;
+ let upatch_function_slice = object::slice_from_bytes::<UpatchFunction>(
function_data,
- function_data.len() / UPATCH_FUNC_SIZE,
+ function_data.len() / UPATCH_FUNCTION_SIZE,
)
.map(|(f, _)| f)
.map_err(|_| anyhow!("Invalid data format"))
.context("Failed to resolve patch functions")?;
- for function in patch_functions {
- patch_symbols.push(UserPatchSymbol {
+ for function in upatch_function_slice {
+ patch_functions.push(UserPatchFunction {
name: OsString::new(),
old_addr: function.old_addr,
old_size: function.old_size,
@@ -119,25 +136,20 @@ impl UpatchResolverImpl {
2024-04-19 16:03:21 +08:00
}
// Relocate patch functions
- for (index, (offset, relocation)) in function_section.relocations().enumerate() {
- if let UpatchRelocation::Name =
- UpatchRelocation::from(index % UPATCH_FUNC_RELA_TYPE_NUM)
- {
- let symbol_index = (offset as usize - UPATCH_FUNC_NAME_OFFSET) / UPATCH_FUNC_SIZE;
- let patch_symbol = patch_symbols
- .get_mut(symbol_index)
- .context("Failed to find patch symbol")?;
-
- let name_offset = relocation.addend() as usize;
- let mut name_bytes = &string_data[name_offset..];
- let string_end = name_bytes
- .iter()
- .position(|b| b == &b'\0')
- .context("Failed to find termination char")?;
- name_bytes = &name_bytes[..string_end];
-
- patch_symbol.name = OsString::from_vec(name_bytes.to_vec());
- }
+ for relocation in UpatchRelocationIterator::new(function_section.relocations()) {
+ let (name_reloc_offset, name_reloc) = relocation.name;
+
+ let name_index =
+ (name_reloc_offset as usize - UPATCH_FUNCTION_OFFSET) / UPATCH_FUNCTION_SIZE;
+ let name_function = patch_functions
+ .get_mut(name_index)
+ .context("Failed to find patch function")?;
+ let name_offset = name_reloc.addend() as usize;
+ let name_string = CStr::from_bytes_with_next_nul(&string_data[name_offset..])
+ .context("Failed to parse patch function name")?
+ .to_os_string();
+
+ name_function.name = name_string;
}
Ok(())
@@ -165,10 +177,10 @@ impl PatchResolverImpl for UpatchResolverImpl {
2024-04-19 16:03:21 +08:00
pkg_name: patch_info.target.full_name(),
patch_file: patch_root.join(&patch_entity.patch_name),
target_elf: patch_entity.patch_target.clone(),
- symbols: Vec::new(),
+ functions: Vec::new(),
checksum: patch_entity.checksum.clone(),
};
- Self::resolve_patch_elf(&mut patch).context("Failed to resolve patch elf")?;
+ Self::resolve_patch_elf(&mut patch).context("Failed to resolve patch")?;
Ok(Patch::UserPatch(patch))
}
--
2.34.1
2024-04-19 16:03:21 +08:00