@@ -56,6 +56,81 @@ def test_zero_arch_file
5656 end
5757 end
5858
59+ def test_invalid_fat_arch_alignment_32
60+ # Build a valid Fat file and then corrupt the alignment field to exceed MAX_FAT_ARCH_ALIGN
61+ machos = SINGLE_ARCHES . map { |a | MachO ::MachOFile . new ( fixture ( a , "hello.bin" ) ) }
62+ fat = MachO ::FatFile . new_from_machos ( *machos )
63+ fat_bin = fat . serialize
64+
65+ # FAT_MAGIC (32-bit) FatArch structure: cputype(4) + cpusubtype(4) + offset(4) + size(4) + align(4) = 20 bytes
66+ # First FatArch starts at offset 8 (FatHeader is 8 bytes)
67+ # align is at offset 8 + 16 = 24
68+ fat_header_size = MachO ::Headers ::FatHeader . bytesize
69+ align_offset = fat_header_size + 16 # cputype(4) + cpusubtype(4) + offset(4) + size(4)
70+
71+ # Set alignment to MAX_FAT_ARCH_ALIGN + 1 (16)
72+ fat_bin [ align_offset , 4 ] = [ MachO ::Headers ::MAX_FAT_ARCH_ALIGN + 1 ] . pack ( "N" )
73+
74+ assert_raises MachO ::FatArchAlignmentError do
75+ MachO ::FatFile . new_from_bin ( fat_bin )
76+ end
77+ end
78+
79+ def test_invalid_fat_arch_alignment_64
80+ # Build a valid Fat64 file and then corrupt the alignment field to exceed MAX_FAT_ARCH_ALIGN
81+ machos = SINGLE_ARCHES . map { |a | MachO ::MachOFile . new ( fixture ( a , "hello.bin" ) ) }
82+ fat = MachO ::FatFile . new_from_machos ( *machos , :fat64 => true )
83+ fat_bin = fat . serialize
84+
85+ # FAT_MAGIC_64 (64-bit) FatArch64 structure: cputype(4) + cpusubtype(4) + offset(8) + size(8) + align(4) + reserved(4) = 32 bytes
86+ # First FatArch64 starts at offset 8 (FatHeader is 8 bytes)
87+ # align is at offset 8 + 24 = 32
88+ fat_header_size = MachO ::Headers ::FatHeader . bytesize
89+ align_offset = fat_header_size + 24 # cputype(4) + cpusubtype(4) + offset(8) + size(8)
90+
91+ # Set alignment to MAX_FAT_ARCH_ALIGN + 1 (16)
92+ fat_bin [ align_offset , 4 ] = [ MachO ::Headers ::MAX_FAT_ARCH_ALIGN + 1 ] . pack ( "N" )
93+
94+ assert_raises MachO ::FatArchAlignmentError do
95+ MachO ::FatFile . new_from_bin ( fat_bin )
96+ end
97+ end
98+
99+ def test_max_valid_fat_arch_alignment_accepted
100+ # Build a valid Fat file and set alignment to exactly MAX_FAT_ARCH_ALIGN
101+ machos = SINGLE_ARCHES . map { |a | MachO ::MachOFile . new ( fixture ( a , "hello.bin" ) ) }
102+ fat = MachO ::FatFile . new_from_machos ( *machos )
103+ fat_bin = fat . serialize
104+
105+ fat_header_size = MachO ::Headers ::FatHeader . bytesize
106+ align_offset = fat_header_size + 16
107+
108+ # Set alignment to MAX_FAT_ARCH_ALIGN (15) - should be accepted
109+ fat_bin [ align_offset , 4 ] = [ MachO ::Headers ::MAX_FAT_ARCH_ALIGN ] . pack ( "N" )
110+
111+ # Should parse without raising
112+ parsed = MachO ::FatFile . new_from_bin ( fat_bin )
113+ assert_equal MachO ::Headers ::MAX_FAT_ARCH_ALIGN , parsed . fat_archs . first . align
114+ end
115+
116+ def test_truncated_fat_arch_32
117+ # FAT_MAGIC + nfat_arch=1 + FatArch with only 16 bytes (cputype, cpusubtype, offset, size) missing align (4 bytes)
118+ # Total = 8 + 16 = 24 bytes instead of 28 bytes
119+ bin = [ MachO ::Headers ::FAT_MAGIC , 1 , MachO ::Headers ::CPU_TYPE_I386 , 3 , 24 , 0 ] . pack ( "N6" )
120+ assert_raises MachO ::TruncatedFileError do
121+ MachO ::FatFile . new_from_bin ( bin )
122+ end
123+ end
124+
125+ def test_truncated_fat_arch_64
126+ # FAT_MAGIC_64 + nfat_arch=1 + FatArch64 with 24 bytes (cputype, cpusubtype, offset(8), size(8)) missing align(4) + reserved(4)
127+ # Total = 8 + 24 = 32 bytes instead of 40 bytes
128+ bin = [ MachO ::Headers ::FAT_MAGIC_64 , 1 ] . pack ( "N2" ) + [ MachO ::Headers ::CPU_TYPE_I386 , 3 , 24 , 0 ] . pack ( "N2Q>2" )
129+ assert_raises MachO ::TruncatedFileError do
130+ MachO ::FatFile . new_from_bin ( bin )
131+ end
132+ end
133+
59134 def test_mismatch_cpu_arch_file
60135 assert_raises MachO ::CPUTypeMismatchError do
61136 MachO ::FatFile . new ( "test/bin/llvm/macho-invalid-fat_cputype" )
0 commit comments