Fix the codesign_alloca implementation
Dadoum

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