Dadoum commited on 2026-08-01 21:49:13
Showing 1 changed files, with 56 additions and 77 deletions.
| ... | ... |
@@ -373,8 +373,7 @@ impl ThinMachO {
|
| 373 | 373 |
})?; |
| 374 | 374 |
|
| 375 | 375 |
let extra_file_size = signature_len - datasize; |
| 376 |
- let extra_vm_size = page_ceil(signature_len).saturating_sub(page_ceil(datasize)); |
|
| 377 |
- self.grow_linkedit_segment(linkedit_offset, extra_file_size, extra_vm_size)?; |
|
| 376 |
+ self.grow_linkedit_segment(linkedit_offset, extra_file_size)?; |
|
| 378 | 377 |
|
| 379 | 378 |
self.data.truncate(dataoff); |
| 380 | 379 |
self.data.resize(dataoff + signature_len, 0); |
| ... | ... |
@@ -515,7 +514,6 @@ impl ThinMachO {
|
| 515 | 514 |
&mut self, |
| 516 | 515 |
command_offset: usize, |
| 517 | 516 |
extra_file_size: usize, |
| 518 |
- extra_vm_size: usize, |
|
| 519 | 517 |
) -> Result<()> {
|
| 520 | 518 |
let cmd = LittleEndian::read_u32(&self.data[command_offset..command_offset + 4]); |
| 521 | 519 |
match cmd {
|
| ... | ... |
@@ -527,19 +525,19 @@ impl ThinMachO {
|
| 527 | 525 |
as usize; |
| 528 | 526 |
let vmsize = |
| 529 | 527 |
LittleEndian::read_u32(&self.data[vmsize_offset..vmsize_offset + 4]) as usize; |
| 528 |
+ let new_filesize = |
|
| 529 |
+ checked_add(&self.path, filesize, extra_file_size, "__LINKEDIT filesize")?; |
|
| 530 |
+ let new_vmsize = vmsize.max(page_ceil(new_filesize)); |
|
| 531 |
+ |
|
| 530 | 532 |
LittleEndian::write_u32( |
| 531 | 533 |
&mut self.data[filesize_offset..filesize_offset + 4], |
| 532 |
- checked_add(&self.path, filesize, extra_file_size, "__LINKEDIT filesize")? |
|
| 533 |
- .try_into() |
|
| 534 |
- .map_err(|_| {
|
|
| 534 |
+ new_filesize.try_into().map_err(|_| {
|
|
| 535 | 535 |
CodeSignError::macho(&self.path, "__LINKEDIT filesize exceeds u32") |
| 536 | 536 |
})?, |
| 537 | 537 |
); |
| 538 | 538 |
LittleEndian::write_u32( |
| 539 | 539 |
&mut self.data[vmsize_offset..vmsize_offset + 4], |
| 540 |
- checked_add(&self.path, vmsize, extra_vm_size, "__LINKEDIT vmsize")? |
|
| 541 |
- .try_into() |
|
| 542 |
- .map_err(|_| {
|
|
| 540 |
+ new_vmsize.try_into().map_err(|_| {
|
|
| 543 | 541 |
CodeSignError::macho(&self.path, "__LINKEDIT vmsize exceeds u32") |
| 544 | 542 |
})?, |
| 545 | 543 |
); |
| ... | ... |
@@ -550,19 +548,21 @@ impl ThinMachO {
|
| 550 | 548 |
let filesize = |
| 551 | 549 |
LittleEndian::read_u64(&self.data[filesize_offset..filesize_offset + 8]); |
| 552 | 550 |
let vmsize = LittleEndian::read_u64(&self.data[vmsize_offset..vmsize_offset + 8]); |
| 551 |
+ let extra_file_size = u64::try_from(extra_file_size).map_err(|_| {
|
|
| 552 |
+ CodeSignError::macho(&self.path, "__LINKEDIT growth exceeds u64") |
|
| 553 |
+ })?; |
|
| 554 |
+ let new_filesize = filesize.checked_add(extra_file_size).ok_or_else(|| {
|
|
| 555 |
+ CodeSignError::macho(&self.path, "__LINKEDIT filesize overflow") |
|
| 556 |
+ })?; |
|
| 557 |
+ let new_vmsize = vmsize.max(page_ceil_u64(new_filesize)); |
|
| 558 |
+ |
|
| 553 | 559 |
LittleEndian::write_u64( |
| 554 | 560 |
&mut self.data[filesize_offset..filesize_offset + 8], |
| 555 |
- filesize |
|
| 556 |
- .checked_add(extra_file_size as u64) |
|
| 557 |
- .ok_or_else(|| {
|
|
| 558 |
- CodeSignError::macho(&self.path, "__LINKEDIT filesize overflow") |
|
| 559 |
- })?, |
|
| 561 |
+ new_filesize, |
|
| 560 | 562 |
); |
| 561 | 563 |
LittleEndian::write_u64( |
| 562 | 564 |
&mut self.data[vmsize_offset..vmsize_offset + 8], |
| 563 |
- vmsize.checked_add(extra_vm_size as u64).ok_or_else(|| {
|
|
| 564 |
- CodeSignError::macho(&self.path, "__LINKEDIT vmsize overflow") |
|
| 565 |
- })?, |
|
| 565 |
+ new_vmsize, |
|
| 566 | 566 |
); |
| 567 | 567 |
} |
| 568 | 568 |
_ => {
|
| ... | ... |
@@ -618,66 +618,7 @@ impl ThinMachO {
|
| 618 | 618 |
signature_len, |
| 619 | 619 |
"__LINKEDIT code signature growth", |
| 620 | 620 |
)?; |
| 621 |
- let cmd = LittleEndian::read_u32(&self.data[command_offset..command_offset + 4]); |
|
| 622 |
- |
|
| 623 |
- match cmd {
|
|
| 624 |
- LC_SEGMENT => {
|
|
| 625 |
- let filesize_offset = command_offset + 36; |
|
| 626 |
- let vmsize_offset = command_offset + 28; |
|
| 627 |
- let filesize = |
|
| 628 |
- LittleEndian::read_u32(&self.data[filesize_offset..filesize_offset + 4]) |
|
| 629 |
- as usize; |
|
| 630 |
- let vmsize = |
|
| 631 |
- LittleEndian::read_u32(&self.data[vmsize_offset..vmsize_offset + 4]) as usize; |
|
| 632 |
- let new_filesize = |
|
| 633 |
- checked_add(&self.path, filesize, extra_file_size, "__LINKEDIT filesize")?; |
|
| 634 |
- let new_vmsize = vmsize.max(page_ceil(new_filesize)); |
|
| 635 |
- |
|
| 636 |
- LittleEndian::write_u32( |
|
| 637 |
- &mut self.data[filesize_offset..filesize_offset + 4], |
|
| 638 |
- new_filesize.try_into().map_err(|_| {
|
|
| 639 |
- CodeSignError::macho(&self.path, "__LINKEDIT filesize exceeds u32") |
|
| 640 |
- })?, |
|
| 641 |
- ); |
|
| 642 |
- LittleEndian::write_u32( |
|
| 643 |
- &mut self.data[vmsize_offset..vmsize_offset + 4], |
|
| 644 |
- new_vmsize.try_into().map_err(|_| {
|
|
| 645 |
- CodeSignError::macho(&self.path, "__LINKEDIT vmsize exceeds u32") |
|
| 646 |
- })?, |
|
| 647 |
- ); |
|
| 648 |
- } |
|
| 649 |
- LC_SEGMENT_64 => {
|
|
| 650 |
- let filesize_offset = command_offset + 48; |
|
| 651 |
- let vmsize_offset = command_offset + 32; |
|
| 652 |
- let filesize = |
|
| 653 |
- LittleEndian::read_u64(&self.data[filesize_offset..filesize_offset + 8]); |
|
| 654 |
- let vmsize = LittleEndian::read_u64(&self.data[vmsize_offset..vmsize_offset + 8]); |
|
| 655 |
- let new_filesize = |
|
| 656 |
- filesize |
|
| 657 |
- .checked_add(extra_file_size as u64) |
|
| 658 |
- .ok_or_else(|| {
|
|
| 659 |
- CodeSignError::macho(&self.path, "__LINKEDIT filesize overflow") |
|
| 660 |
- })?; |
|
| 661 |
- let new_vmsize = vmsize.max(page_ceil_u64(new_filesize)); |
|
| 662 |
- |
|
| 663 |
- LittleEndian::write_u64( |
|
| 664 |
- &mut self.data[filesize_offset..filesize_offset + 8], |
|
| 665 |
- new_filesize, |
|
| 666 |
- ); |
|
| 667 |
- LittleEndian::write_u64( |
|
| 668 |
- &mut self.data[vmsize_offset..vmsize_offset + 8], |
|
| 669 |
- new_vmsize, |
|
| 670 |
- ); |
|
| 671 |
- } |
|
| 672 |
- _ => {
|
|
| 673 |
- return Err(CodeSignError::macho( |
|
| 674 |
- &self.path, |
|
| 675 |
- "__LINKEDIT command was not a segment command", |
|
| 676 |
- )); |
|
| 677 |
- } |
|
| 678 |
- } |
|
| 679 |
- |
|
| 680 |
- Ok(()) |
|
| 621 |
+ self.grow_linkedit_segment(command_offset, extra_file_size) |
|
| 681 | 622 |
} |
| 682 | 623 |
} |
| 683 | 624 |
|
| ... | ... |
@@ -1010,6 +951,44 @@ mod tests {
|
| 1010 | 951 |
); |
| 1011 | 952 |
} |
| 1012 | 953 |
|
| 954 |
+ #[test] |
|
| 955 |
+ fn signature_growth_keeps_linkedit_vmsize_large_enough() {
|
|
| 956 |
+ let mut macho = minimal_macho_with_empty_signature(); |
|
| 957 |
+ let linkedit_cmd = 32 + 72; |
|
| 958 |
+ let code_sig_cmd = linkedit_cmd + 72; |
|
| 959 |
+ let linkedit_fileoff = 512usize; |
|
| 960 |
+ let old_signature_size = 1usize; |
|
| 961 |
+ let dataoff = linkedit_fileoff + PAGE_SIZE - old_signature_size; |
|
| 962 |
+ |
|
| 963 |
+ macho.resize(dataoff + old_signature_size, 0); |
|
| 964 |
+ write_segment_64( |
|
| 965 |
+ &mut macho, |
|
| 966 |
+ linkedit_cmd, |
|
| 967 |
+ "__LINKEDIT", |
|
| 968 |
+ linkedit_fileoff as u64, |
|
| 969 |
+ PAGE_SIZE as u64, |
|
| 970 |
+ ); |
|
| 971 |
+ LittleEndian::write_u32( |
|
| 972 |
+ &mut macho[code_sig_cmd + 8..code_sig_cmd + 12], |
|
| 973 |
+ dataoff as u32, |
|
| 974 |
+ ); |
|
| 975 |
+ LittleEndian::write_u32( |
|
| 976 |
+ &mut macho[code_sig_cmd + 12..code_sig_cmd + 16], |
|
| 977 |
+ old_signature_size as u32, |
|
| 978 |
+ ); |
|
| 979 |
+ |
|
| 980 |
+ let entitlements = Dictionary::new(); |
|
| 981 |
+ let config = MachOSigningConfig::new("com.example.test", "TEAMID", &entitlements, None);
|
|
| 982 |
+ let signed = sign_macho_data(Path::new("PageBoundaryBinary"), &macho, &config).unwrap();
|
|
| 983 |
+ |
|
| 984 |
+ let filesize = LittleEndian::read_u64(&signed[linkedit_cmd + 48..linkedit_cmd + 56]); |
|
| 985 |
+ let vmsize = LittleEndian::read_u64(&signed[linkedit_cmd + 32..linkedit_cmd + 40]); |
|
| 986 |
+ |
|
| 987 |
+ assert!(filesize > PAGE_SIZE as u64); |
|
| 988 |
+ assert!(vmsize >= filesize); |
|
| 989 |
+ assert_eq!(vmsize, page_ceil_u64(filesize)); |
|
| 990 |
+ } |
|
| 991 |
+ |
|
| 1013 | 992 |
#[test] |
| 1014 | 993 |
fn cms_reservation_is_zero_padding_after_superblob() {
|
| 1015 | 994 |
let macho = minimal_macho_with_empty_signature(); |
| 1016 | 995 |