From 580c5ef22241b37d9a165195027144357dfb3b9e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 4 Sep 2026 09:31:06 +0200 Subject: [PATCH 1/2] fix(apple): Bound Mach-O load command parsing Keep each load command within sizeofcmds and validate command-specific sizes before reading segment or UUID data. This mirrors the bounds-checked load command walk used by the native crash daemon. Close: NATIVE-220 --- src/modulefinder/sentry_modulefinder_apple.c | 27 +++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/modulefinder/sentry_modulefinder_apple.c b/src/modulefinder/sentry_modulefinder_apple.c index b7c8d72666..cde5b437eb 100644 --- a/src/modulefinder/sentry_modulefinder_apple.c +++ b/src/modulefinder/sentry_modulefinder_apple.c @@ -50,15 +50,25 @@ add_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) sentry_value_set_by_key( module, "image_addr", sentry__value_new_addr((uint64_t)info.dli_fbase)); - const struct load_command *cmd = (const struct load_command *)(header + 1); + const char *commands = (const char *)(header + 1); + size_t pos = 0; bool has_size = false; bool has_uuid = false; - for (size_t i = 0; cmd && (i < header->ncmds) && (!has_uuid || !has_size); - ++i, - cmd - = (const struct load_command *)((const char *)cmd + cmd->cmdsize)) { - if (cmd->cmd == CMD_SEGMENT) { + for (size_t i = 0; i < header->ncmds && (!has_uuid || !has_size); i++) { + if (sizeof(struct load_command) > header->sizeofcmds - pos) { + break; + } + + const struct load_command *cmd + = (const struct load_command *)(commands + pos); + if (cmd->cmdsize < sizeof(struct load_command) + || cmd->cmdsize > header->sizeofcmds - pos) { + break; + } + + if (cmd->cmd == CMD_SEGMENT + && cmd->cmdsize >= sizeof(mach_segment_command_type)) { const mach_segment_command_type *seg = (const mach_segment_command_type *)cmd; @@ -67,7 +77,8 @@ add_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) sentry_value_new_int32((uint32_t)seg->vmsize)); has_size = true; } - } else if (cmd->cmd == LC_UUID) { + } else if (cmd->cmd == LC_UUID + && cmd->cmdsize >= sizeof(struct uuid_command)) { const struct uuid_command *ucmd = (const struct uuid_command *)cmd; sentry_uuid_t uuid = sentry_uuid_from_bytes((const char *)ucmd->uuid); @@ -75,6 +86,8 @@ add_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) module, "debug_id", sentry__value_new_uuid(&uuid)); has_uuid = true; } + + pos += cmd->cmdsize; } sentry__mutex_lock(&g_mutex); From 4773e26594c9f1c071c1b176ebeda41716ffe56d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 4 Sep 2026 14:46:58 +0200 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0237927f9..69343da643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - `sentry_set_trace` omits `parent_span_id` when the caller does not provide one, instead of serializing it as `null`. ([#2047](https://github.com/getsentry/sentry-native/pull/2047)) - Native/Linux i386: write valid thread stack descriptors to minidumps when stack addresses use the upper half of the 32-bit address space. ([#2054](https://github.com/getsentry/sentry-native/pull/2054)) - Guard size arithmetic when parsing envelopes and Linux OS release data, copying slices, and allocating memory during crash handling. ([#2059](https://github.com/getsentry/sentry-native/pull/2059)) +- macOS: prevent out-of-bounds reads while parsing Mach-O load commands. ([#2065](https://github.com/getsentry/sentry-native/pull/2065)) **Thank you**: