commit f46d3c17738e810ae2964e5691842c90c19ce200
parent 4b204dc57ee22c8bbd6b4e2a5912c297abf0ca8b
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 16 Jul 2026 10:28:13 -0700
strip: reject malformed Mach-O debug ranges
Diffstat:
3 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/doc/RELEASE_AUDIT_2026_6_0.md b/doc/RELEASE_AUDIT_2026_6_0.md
@@ -500,7 +500,7 @@ Implementation checkpoint (updated 2026-07-16):
| KIT-P2-001 | **Complete** | `test-driver-stack-protector`: 133 pass, 0 fail; cJSON build/test passes |
| KIT-P1-015 | **Complete** | 21 multi-source dependency assertions pass in the driver suite |
| KIT-P2-003 | **Complete** | `test-driver-diagnostics`: 15 pass, 0 fail |
-| KIT-P1-007 | **Complete** | `test-driver-strip`: object API 23/0, relocatable/archive 5/0, linked ELF/Mach-O/PE matrix 91/0 |
+| KIT-P1-007 | **Complete** | `test-driver-strip`: object API 26/0, relocatable/archive 5/0, linked ELF/Mach-O/PE matrix 91/0 |
| KIT-P1-017 | In progress | implementation under focused CLI audit |
| KIT-P2-002 | In progress | implementation under focused CLI audit |
| KIT-P3-003 | **Complete** | `test-link-macho-symbols` passes aarch64 and x86-64 Kit/platform oracles |
diff --git a/src/obj/macho/rewrite.c b/src/obj/macho/rewrite.c
@@ -87,12 +87,29 @@ static KitStatus macho_validate(MachoRewriteView* v, const KitContext* ctx,
if (size < 8 || pos + size > end)
return macho_bad(v, "load-command size");
if (kind == LC_SEGMENT_64) {
- u32 nsects;
+ u32 nsects, j;
if (size < MACHO_SEGCMD64_SIZE)
return macho_bad(v, "segment command");
nsects = obj_rw_u32(cmd + 64, 0);
if ((u64)MACHO_SEGCMD64_SIZE + (u64)nsects * MACHO_SECT64_SIZE > size)
return macho_bad(v, "segment sections");
+ for (j = 0; j < nsects; ++j) {
+ const u8* sec = cmd + MACHO_SEGCMD64_SIZE +
+ (size_t)j * MACHO_SECT64_SIZE;
+ u64 sec_size = obj_rw_u64(sec + 40, 0);
+ u32 sec_off = obj_rw_u32(sec + 48, 0);
+ u32 reloc_off = obj_rw_u32(sec + 56, 0);
+ u32 nreloc = obj_rw_u32(sec + 60, 0);
+ u32 sec_type = obj_rw_u32(sec + 64, 0) & SECTION_TYPE;
+ int zerofill = sec_type == S_ZEROFILL || sec_type == 0x0cu ||
+ sec_type == S_THREAD_LOCAL_ZEROFILL;
+ if (!zerofill && sec_size &&
+ !obj_rw_range(v->rw.len, sec_off, sec_size))
+ return macho_bad(v, "section contents");
+ if (nreloc &&
+ !obj_rw_range(v->rw.len, reloc_off, (u64)nreloc * 8u))
+ return macho_bad(v, "section relocations");
+ }
} else if (kind == LC_SYMTAB) {
u32 symoff, nsyms, stroff, strsize;
if (size < MACHO_SYMTAB_CMD_SIZE)
diff --git a/test/api/object_rewrite_test.c b/test/api/object_rewrite_test.c
@@ -28,6 +28,11 @@ static void put32(uint8_t* p, uint32_t v) {
p[3] = (uint8_t)(v >> 24);
}
+static void put64(uint8_t* p, uint64_t v) {
+ put32(p, (uint32_t)v);
+ put32(p + 4, (uint32_t)(v >> 32));
+}
+
static uint32_t get32(const uint8_t* p) {
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
@@ -204,6 +209,42 @@ static void check_strip_all(KitUnit* u, const uint8_t* pe) {
kit_writer_close(out);
}
+static void check_malformed_macho_section(KitUnit* u) {
+ uint8_t macho[184];
+ KitSlice input;
+ KitLinkedRewriteOptions opts;
+ KitWriter* out = NULL;
+ size_t out_len = 99u;
+ KitStatus st;
+ memset(macho, 0, sizeof macho);
+ put32(macho, 0xfeedfacfu); /* MH_MAGIC_64 */
+ put32(macho + 12, 2u); /* MH_EXECUTE */
+ put32(macho + 16, 1u); /* ncmds */
+ put32(macho + 20, 152u); /* sizeofcmds */
+ put32(macho + 32, 0x19u); /* LC_SEGMENT_64 */
+ put32(macho + 36, 152u);
+ memcpy(macho + 40, "__DWARF", 7u);
+ put32(macho + 96, 1u); /* nsects */
+ memcpy(macho + 104, "__debug_info", 12u);
+ memcpy(macho + 120, "__DWARF", 7u);
+ put64(macho + 144, 16u); /* section size */
+ put32(macho + 152, 4096u); /* impossible file offset */
+ put32(macho + 168, 0x02000000u); /* S_ATTR_DEBUG */
+ input.data = macho;
+ input.len = sizeof macho;
+ memset(&opts, 0, sizeof opts);
+ opts.strip_level = KIT_LINKED_STRIP_DEBUG;
+ CU_EXPECT(u, kit_writer_mem(&u->heap, &out) == KIT_OK && out,
+ "malformed Mach-O memory writer");
+ st = kit_obj_rewrite_linked(&u->ctx, KIT_SLICE_LIT("bad-macho"), &input,
+ &opts, out, NULL);
+ CU_EXPECT(u, st == KIT_MALFORMED,
+ "out-of-range Mach-O debug section is malformed (%d)", (int)st);
+ (void)kit_writer_mem_bytes(out, &out_len);
+ CU_EXPECT(u, out_len == 0u, "malformed Mach-O emits no bytes");
+ kit_writer_close(out);
+}
+
int main(void) {
KitUnit u;
uint8_t pe[PE_SIZE];
@@ -212,6 +253,7 @@ int main(void) {
check_signed_transaction(&u, pe);
check_strip_debug(&u, pe);
check_strip_all(&u, pe);
+ check_malformed_macho_section(&u);
kit_unit_summary(&u, "object_rewrite_test");
return kit_unit_status(&u);
}