fix(portable): reject malformed RDPKG resources

Distinguish an absent customer package from an invalid resource and
propagate package errors instead of launching the stock payload.

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2026-08-14 11:03:57 +08:00
parent d0da2441e5
commit 2315887619
2 changed files with 34 additions and 16 deletions

View File

@@ -40,16 +40,16 @@ pub(crate) struct BinaryReader {
pub package_paths: Vec<String>, pub package_paths: Vec<String>,
} }
impl Default for BinaryReader { impl BinaryReader {
fn default() -> Self { pub fn new() -> Result<Self, String> {
let package = read_package(); let package = read_package()?;
let package_paths = package.0.iter().map(|f| f.path.clone()).collect(); let package_paths = package.0.iter().map(|f| f.path.clone()).collect();
let (files, exe) = merge(read_embedded(), package); let (files, exe) = merge(read_embedded(), package);
Self { Ok(Self {
files, files,
exe, exe,
package_paths, package_paths,
} })
} }
} }
@@ -118,7 +118,9 @@ fn parse(blob: &'static [u8]) -> Option<(Vec<BinaryData>, String)> {
} }
let path_length = read_u32(blob, base)? as usize; let path_length = read_u32(blob, base)? as usize;
base += LENGTH; base += LENGTH;
let path = String::from_utf8_lossy(blob.get(base..base + path_length)?).to_string(); let path = std::str::from_utf8(blob.get(base..base + path_length)?)
.ok()?
.to_owned();
base += path_length; base += path_length;
let file_length = read_u32(blob, base)? as usize; let file_length = read_u32(blob, base)? as usize;
base += LENGTH; base += LENGTH;
@@ -132,7 +134,7 @@ fn parse(blob: &'static [u8]) -> Option<(Vec<BinaryData>, String)> {
path, path,
}); });
} }
let executable = String::from_utf8_lossy(blob.get(base..)?).to_string(); let executable = std::str::from_utf8(blob.get(base..)?).ok()?.to_owned();
Some((parsed, executable)) Some((parsed, executable))
} }
@@ -146,16 +148,25 @@ fn read_embedded() -> (Vec<BinaryData>, String) {
parse(BIN_DATA).unwrap_or_default() parse(BIN_DATA).unwrap_or_default()
} }
fn parse_package_blob(blob: Option<&'static [u8]>) -> Result<(Vec<BinaryData>, String), String> {
let Some(blob) = blob else {
return Ok(Default::default());
};
let package = parse(blob).ok_or_else(|| "RDPKG resource is invalid".to_owned())?;
if package.1.trim().is_empty() {
return Err("RDPKG resource has no executable".to_owned());
}
Ok(package)
}
#[cfg(windows)] #[cfg(windows)]
fn read_package() -> (Vec<BinaryData>, String) { fn read_package() -> Result<(Vec<BinaryData>, String), String> {
read_resource(PACKAGE_RESOURCE_NAME) parse_package_blob(read_resource(PACKAGE_RESOURCE_NAME))
.and_then(parse)
.unwrap_or_default()
} }
#[cfg(not(windows))] #[cfg(not(windows))]
fn read_package() -> (Vec<BinaryData>, String) { fn read_package() -> Result<(Vec<BinaryData>, String), String> {
Default::default() Ok(Default::default())
} }
// Reads an RCDATA resource out of the running image. Resources live in the mapped // Reads an RCDATA resource out of the running image. Resources live in the mapped
@@ -290,6 +301,13 @@ mod tests {
assert!(parse(b"rustdesk\x00\x00\x00\x40partial".as_slice()).is_none()); assert!(parse(b"rustdesk\x00\x00\x00\x40partial".as_slice()).is_none());
} }
#[test]
fn distinguishes_an_absent_package_from_a_malformed_one() {
assert!(parse_package_blob(None).unwrap().0.is_empty());
assert!(parse_package_blob(Some(b"damaged")).is_err());
assert!(parse_package_blob(Some(blob(&[("./custom.txt", b"cfg")], ""))).is_err());
}
#[test] #[test]
fn without_a_package_the_stock_payload_is_untouched() { fn without_a_package_the_stock_payload_is_untouched() {
let embedded = parse(blob(&[("./rustdesk.exe", b"app")], "./rustdesk.exe")).unwrap(); let embedded = parse(blob(&[("./rustdesk.exe", b"app")], "./rustdesk.exe")).unwrap();

View File

@@ -272,7 +272,7 @@ fn execute(path: PathBuf, args: Vec<String>, _ui: bool) {
} }
} }
fn main() { fn main() -> Result<(), String> {
let mut args = Vec::new(); let mut args = Vec::new();
let mut arg_exe = Default::default(); let mut arg_exe = Default::default();
let mut i = 0; let mut i = 0;
@@ -291,7 +291,7 @@ fn main() {
let quick_support = false; let quick_support = false;
let mut ui = false; let mut ui = false;
let reader = BinaryReader::default(); let reader = BinaryReader::new()?;
if let Some(exe) = setup( if let Some(exe) = setup(
reader, reader,
None, None,
@@ -306,6 +306,7 @@ fn main() {
} }
execute(exe, args, ui); execute(exe, args, ui);
} }
Ok(())
} }
#[cfg(windows)] #[cfg(windows)]
@@ -368,4 +369,3 @@ mod meta_tests {
assert_eq!(resolve_within(base, ""), None); assert_eq!(resolve_within(base, ""), None);
} }
} }