Skip to content

fix: resolved invalid binaries caused by NUL padding in copy_and_replace_cstring_placeholder (#2342) - #2503

Open
doraem-on wants to merge 8 commits into
conda:mainfrom
doraem-on:fix-1351-missing-component
Open

fix: resolved invalid binaries caused by NUL padding in copy_and_replace_cstring_placeholder (#2342)#2503
doraem-on wants to merge 8 commits into
conda:mainfrom
doraem-on:fix-1351-missing-component

Conversation

@doraem-on

@doraem-on doraem-on commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes #2342

Motivation and Context:
This PR fixes the issue where copy_and_replace_cstring_placeholder resulted in invalid binaries due to \0 string padding, specifically breaking Go and Rust packages built with rattler-build.

When replacing a longer prefix placeholder with a shorter target prefix, we must pad the size difference so the total binary byte size doesn't change.

The core issue with the old approach (NUL padding at the end):
Padding the end of the replaced C-string with \0 bytes caused two major bugs in modern binaries:

  1. Broken SHF_MERGE Optimization: The linker optimizes overlapping string suffixes to save space. Because the old code shifted the suffix to the left and padded \0 at the end, any references pointing to the optimized suffix offset would now point to trailing NUL bytes (\0\0\0) instead of the characters.
  2. Embedded NULs: Rust's CString::new() would panic because the statically sized slice bounds (&[u8]) now enclosed embedded NULs in the middle of length-prefixed strings.

The Implementation:
Applying Slash (/) padding universally breaks backward compatibility for standard C/C++ binaries that strictly rely on \0 string terminators to identify path lengths in memory. To solve this securely:

  1. Explicit Opt-in Flag (c_string): We've added an optional c_string boolean flag to the paths.json entry schema (exposed in both Rust and the Python bindings).
  2. NUL Padding Default: If c_string is None or True, the prefix replacement algorithm safely falls back to standard NUL padding. This preserves 100% legacy behavior and guarantees absolute backward compatibility for existing C/C++ packages.
  3. Slash Padding Opt-in: If c_string: false, we pad the difference directly on the target prefix using slashes (/). For example, PLACEHOLDER/dep becomes /short///////dep.
    • Since multiple slashes resolve to a single directory separator on UNIX, the path remains semantically identical.
    • The suffix dep stays at its exact original memory offset, preserving all linker optimizations.
    • It eliminates injected NULs, fixing the Rust/Go binary corruption. rattler-build can now selectively enable this exclusively for modern languages.

How Has This Been Tested?

  • Updated unit tests in crates/rattler/src/install/link.rs to rigorously test both padding behaviors down to the byte array.
  • Verified both the NUL truncation logic and the Slash padding replacements function independently depending on the flag.
  • Ensured pytest --doctest-modules passes for Python bindings by conditionally outputting the c_string flag only when explicitly passed.
  • Ran the full workspace test suite cargo test --workspace to ensure no regressions.
  • Formatted and linted both Rust (cargo fmt, cargo clippy) and Python (ruff format, ruff check).

AI Disclosure

  • This PR contains AI-generated content.
    • I have tested any AI-generated content in my PR.
    • I take responsibility for any AI-generated content in my PR.
      Tools: Gemini

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added sufficient tests to cover my changes.

@doraem-on doraem-on changed the title Fix: Resolved invalid binaries caused by NUL padding in copy_and_replace_cstring_placeholder (Fixes #2342) fix: resolve invalid binaries caused by NUL padding in copy_and_replace_cstring_placeholder Jun 11, 2026
@doraem-on doraem-on changed the title fix: resolve invalid binaries caused by NUL padding in copy_and_replace_cstring_placeholder fix: resolved invalid binaries caused by NUL padding in copy_and_replace_cstring_placeholder (#2342) Jun 11, 2026
@wolfv

wolfv commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Hi @doraem-on thank you for working on this! I think it's a really interesting approach. Do you have any idea on how compatible this is? Are we confident this works everywhere?

We have thought that maybe we could add a flag to paths.json / rattler-build to enable this only when Rust / Go are involved for the time being.

@doraem-on

doraem-on commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Hi @wolfv,

Thanks for bringing up the compatibility concerns. You are absolutely right—applying slash padding would break compatibility with older C/C++ packages that strictly rely on \0 string terminators to identify path lengths in memory.

To fix this securely, I've implemented the exact approach you suggested:

I've added an optional c_string boolean flag to the paths.json entry schema (exposed across both Rust and the Python bindings).

  • The prefix replacement algorithm now defaults strictly back to NUL padding if the flag is unset (or explicitly set to true). This perfectly preserves legacy behaviour and guarantees absolute backward compatibility for all existing C/C++ packages.
  • Slash padding is now strictly an opt-in feature (c_string: false), which means rattler-build can selectively enable this exclusively for modern Rust and Go binaries to prevent breaking their string payloads/checksums without affecting the broader ecosystem!
  • I also added comprehensive byte-level tests to explicitly assert both the NUL truncation logic and Slash padding paths to prevent future regressions.

The changes have been pushed to the branch and updated description ,let me know what you think of this implementation and really grateful for your time in reviewing the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

copy_and_replace_cstring_placeholder can result in invalid binaries

2 participants