Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
cstkingkey opened this issue Oct 14, 2023 · 9 comments
Labels
apple-codesign apple-codesign crate and rcodesign CLI tool bug Something isn't working

Comments

@cstkingkey
Copy link

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 indygreg added bug Something isn't working question Further information is requested apple-codesign apple-codesign crate and rcodesign CLI tool labels Nov 4, 2023
@indygreg
Copy link
Owner

indygreg commented Nov 4, 2023

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
Copy link
Author

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 indygreg removed the question Further information is requested label Nov 5, 2023
@indygreg
Copy link
Owner

indygreg commented Nov 5, 2023

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

@indygreg
Copy link
Owner

indygreg commented Nov 5, 2023

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
Copy link
Author

cstkingkey commented Nov 6, 2023

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 added a commit that referenced this issue Nov 6, 2023
…nt start offset

We add functionality to our crude Mach-O writer for customizing the
start offset of the __TEXT segment. This allows us to implement a
failing test for #91 which reproduces the bug.
@indygreg
Copy link
Owner

indygreg commented Nov 6, 2023

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.

@indygreg indygreg reopened this Nov 6, 2023
@indygreg indygreg changed the title Mach-O segment corruption Mach-O with __TEXT segment having non 0x0 start offset results in invalid signature Nov 6, 2023
@cstkingkey
Copy link
Author

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
Copy link
Collaborator

roblabla commented Nov 7, 2023

@cstkingkey I recently encountered an issue using rust's built-in strip when cross-compiling from linux to macos: rust-lang/rust#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
Copy link
Owner

indygreg commented Nov 9, 2023

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
apple-codesign apple-codesign crate and rcodesign CLI tool bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants