indygreg / apple-platform-rs

Rust crates supporting Apple platform development
565 stars 38 forks source link

Mach-O with `__TEXT` segment having non 0x0 start offset results in invalid signature #91

Closed cstkingkey closed 10 months ago

cstkingkey commented 10 months ago

today I run into this error: Error: error writing Mach-O: Mach-O segment corruption: cursor at 0x1060 but segment begins at 0x1000 (please report this bug)

indygreg commented 10 months ago

Which version of rcodesign is this? We had some bugs in this area in earlier releases that should now be fixed. Steps to reproduce would be greatly appreciated. You can email/dropbox/gdrive a binary to me at gregory.szorc@gmail.com.

cstkingkey commented 10 months ago

Which version of rcodesign is this? We had some bugs in this area in earlier releases that should now be fixed. Steps to reproduce would be greatly appreciated. You can email/dropbox/gdrive a binary to me at gregory.szorc@gmail.com.

the version is 0.22.0. the binary is sent to your email. the issue is present with am64 binary, not with aarch64 binary.

indygreg commented 10 months ago

I received the binary and can reproduce the failure. Thank you so much!

indygreg commented 10 months ago

OK I see what is happening.

Here are the initial Mach-O segments in your binary:

segments count: 4
segment #0; __PAGEZERO; offsets=0x0-0x0 (0-0); addresses=0x0-0x100001000; vm/file size 4294971392/0; section count 0
segment #1; __TEXT; offsets=0x1000-0x458000 (4096-4554752); addresses=0x100001000-0x100458000; vm/file size 4550656/4550656; section count 12
segment #1; section #0: __text; offsets=0x1300-0x32c880 (4864-3328128); addresses=0x100001300-0x10032c880; size 3323264; align=6; flags=2147484672
segment #1; section #1: __stubs; offsets=0x32c880-0x32d144 (3328128-3330372); addresses=0x10032c880-0x10032d144; size 2244; align=1; flags=2147484680
segment #1; section #2: __stub_helper; offsets=0x32d144-0x32dff0 (3330372-3334128); addresses=0x10032d144-0x10032dff0; size 3756; align=2; flags=2147484672

What's happening is that when we finish writing out the Mach-O header and load commands, we're at 0x1060 / 4192. But the __TEXT segments begins at 0x1000 and that is tripping up our code validating that segments don't overlap.

Most Mach-O binaries I've seen have TEXT begin at 0x0 and they leave plenty of space for an additional LC_CODE_SIGNATURE if needed. Yours leaves enough space before the text section but the __TEXT segment begins at 0x1000.

I suppose we need to tweak the logic to look at the section offsets.

Out of curiosity, can you share details on how this binary was built/linked? I'm just curious what tools in the wild are emitting __TEXT segments in this manner.

cstkingkey commented 10 months ago

OK I see what is happening.

Here are the initial Mach-O segments in your binary:

segments count: 4
segment #0; __PAGEZERO; offsets=0x0-0x0 (0-0); addresses=0x0-0x100001000; vm/file size 4294971392/0; section count 0
segment #1; __TEXT; offsets=0x1000-0x458000 (4096-4554752); addresses=0x100001000-0x100458000; vm/file size 4550656/4550656; section count 12
segment #1; section #0: __text; offsets=0x1300-0x32c880 (4864-3328128); addresses=0x100001300-0x10032c880; size 3323264; align=6; flags=2147484672
segment #1; section #1: __stubs; offsets=0x32c880-0x32d144 (3328128-3330372); addresses=0x10032c880-0x10032d144; size 2244; align=1; flags=2147484680
segment #1; section #2: __stub_helper; offsets=0x32d144-0x32dff0 (3330372-3334128); addresses=0x10032d144-0x10032dff0; size 3756; align=2; flags=2147484672

What's happening is that when we finish writing out the Mach-O header and load commands, we're at 0x1060 / 4192. But the __TEXT segments begins at 0x1000 and that is tripping up our code validating that segments don't overlap.

Most Mach-O binaries I've seen have TEXT begin at 0x0 and they leave plenty of space for an additional LC_CODE_SIGNATURE if needed. Yours leaves enough space before the text section but the __TEXT segment begins at 0x1000.

I suppose we need to tweak the logic to look at the section offsets.

Out of curiosity, can you share details on how this binary was built/linked? I'm just curious what tools in the wild are emitting __TEXT segments in this manner.

I‘m using osxcross (https://github.com/tpoechtrager/osxcross.git) with clang 17.0.2 with centos as the host, which is specified as the linker in the rust toolchain.

indygreg commented 10 months ago

I added a failing test reproducing the failure then made a code change to hopefully fix this. I confirmed the binary you sent me now appears to sign correctly. However, codesign -v is saying it isn't signed correctly. I'll need to look into this further.

cstkingkey commented 10 months ago

it's the cargo strip option causing the issue. Macos refuse to run the binary complaining it's malformed, though the codesign can sign the binary without error.

roblabla commented 10 months ago

@cstkingkey I recently encountered an issue using rust's built-in strip when cross-compiling from linux to macos: https://github.com/rust-lang/rust/issues/114411 . I believe you're encountering the same issue.

Essentially, cargo will simply run strip path/to/binary when stripping a Mach-O, but that obviously fails on linux where the strip binary is from GNU binutils and only works on ELFs. Worse: The GNU binutils version of strip will sometimes still try to strip the MachO, and corrupt it in the process.

When cross-compiling from linux to macos, it's safer to strip the binary yourself, outside of the cargo command.

indygreg commented 10 months ago

Oh interesting. So you are saying the Mach-O binary is already corrupted in the email you sent me? If so, should we close this issue?