Skip to content

Commit 602aa4d

Browse files
authored
fix: multi-prefix replacement in binary files (#570)
The `pkg-config` package encodes (in the binary) a "PATH" variable that looks something like: ``` /prefix_placeholder/lib:/prefix_placeholder/etc/share\0 ``` So far we only replaced the initial prefix placeholder, and skipped past the rest. With this change we first get the whole "c-string" (until the `\0`), and then replace each occurence of the prefix in the c-string. The padding is then added to make sure that the strings are of the same length.
1 parent 5b60a51 commit 602aa4d

File tree

1 file changed

+37
-18
lines changed

1 file changed

+37
-18
lines changed

crates/rattler/src/install/link.rs

+37-18
Original file line numberDiff line numberDiff line change
@@ -543,34 +543,41 @@ pub fn copy_and_replace_cstring_placeholder(
543543
let old_prefix = prefix_placeholder.as_bytes();
544544
let new_prefix = target_prefix.as_bytes();
545545

546-
// Compute the padding required when replacing the old prefix with the new one. If the old
547-
// prefix is longer than the new one we need to add padding to ensure that the entire part
548-
// will hold the same number of bytes. We do this by adding '\0's (e.g. nul terminators). This
549-
// ensures that the text will remain a valid nul-terminated string.
550-
let padding = vec![b'\0'; old_prefix.len().saturating_sub(new_prefix.len())];
551-
552546
loop {
553547
if let Some(index) = memchr::memmem::find(source_bytes, old_prefix) {
548+
// write all bytes up to the old prefix, followed by the new prefix.
549+
destination.write_all(&source_bytes[..index])?;
550+
554551
// Find the end of the c-style string. The nul terminator basically.
555552
let mut end = index + old_prefix.len();
556553
while end < source_bytes.len() && source_bytes[end] != b'\0' {
557554
end += 1;
558555
}
559556

560-
// Determine the total length of the c-string.
561-
let len = end - index;
557+
let mut out = Vec::new();
558+
let mut old_bytes = &source_bytes[index..end];
559+
let old_len = old_bytes.len();
562560

563-
// Get the suffix part (this is the text after the prefix by up until the nul
564-
// terminator). E.g. in `old-prefix/some/path\0` the suffix would be `/some/path`.
565-
let suffix = &source_bytes[index + old_prefix.len()..end];
561+
// replace all occurrences of the old prefix with the new prefix
562+
while let Some(index) = memchr::memmem::find(old_bytes, old_prefix) {
563+
out.write_all(&old_bytes[..index])?;
564+
out.write_all(new_prefix)?;
565+
old_bytes = &old_bytes[index + old_prefix.len()..];
566+
}
567+
out.write_all(old_bytes)?;
568+
// write everything up to the old length
569+
if out.len() > old_len {
570+
destination.write_all(&out[..old_len])?;
571+
} else {
572+
destination.write_all(&out)?;
573+
}
566574

567-
// Write all bytes up to the old prefix, then the new prefix followed by suffix and
568-
// padding.
569-
destination.write_all(&source_bytes[..index])?;
570-
destination.write_all(&new_prefix[..len.min(new_prefix.len())])?;
571-
destination
572-
.write_all(&suffix[..len.saturating_sub(new_prefix.len()).min(suffix.len())])?;
573-
destination.write_all(&padding)?;
575+
// Compute the padding required when replacing the old prefix(es) with the new one. If the old
576+
// prefix is longer than the new one we need to add padding to ensure that the entire part
577+
// will hold the same number of bytes. We do this by adding '\0's (e.g. nul terminators). This
578+
// ensures that the text will remain a valid nul-terminated string.
579+
let padding = old_len.saturating_sub(out.len());
580+
destination.write_all(&vec![0; padding])?;
574581

575582
// Continue with the rest of the bytes.
576583
source_bytes = &source_bytes[end..];
@@ -662,4 +669,16 @@ mod test {
662669
.unwrap();
663670
assert_eq!(&output.into_inner(), expected_output);
664671
}
672+
673+
#[test]
674+
fn replace_binary_path_var() {
675+
let input =
676+
b"beginrandomdataPATH=/placeholder/etc/share:/placeholder/bin/:\x00somemoretext";
677+
let mut output = Cursor::new(Vec::new());
678+
super::copy_and_replace_cstring_placeholder(input, &mut output, "/placeholder", "/target")
679+
.unwrap();
680+
let out = &output.into_inner();
681+
assert_eq!(out, b"beginrandomdataPATH=/target/etc/share:/target/bin/:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00somemoretext");
682+
assert_eq!(out.len(), input.len());
683+
}
665684
}

0 commit comments

Comments
 (0)