Fix patchelf concurrency issues. Show subprocess output in errors#1410
Merged
Conversation
giordano
reviewed
Nov 29, 2025
82e6562 to
45aa1d5
Compare
Remove nested Threads.@threads over libraries since the outer loop over files already provides parallelism. Nested threading caused: 1. Race conditions with patchelf/install_name_tool on the same file 2. ConcurrencyViolationError in OutputCollectors when multiple sandbox operations ran in parallel
This reverts commit 04d4f81.
cb6c03e to
5280b43
Compare
Wrap the entire read-modify-verify cycle in with_patchelf_lock() for Linux/BSD ELF operations. This prevents concurrent ObjectFile reads from failing with EOFError when another thread is running patchelf on the same file. Split update_linkage into _update_linkage_macho and _update_linkage_elf, and ensure_soname into _ensure_soname_macho and _ensure_soname_elf for cleaner platform separation.
Sys.isbsd(platform) returns true for macOS since Darwin is BSD-based, but macOS uses install_name_tool not patchelf. Check Sys.isapple first.
5280b43 to
bb5f705
Compare
giordano
reviewed
Nov 29, 2025
| return | ||
| end | ||
|
|
||
| # macOS uses install_name_tool (check first since Sys.isbsd is true for macOS too) |
Member
There was a problem hiding this comment.
I don't understand this comment
Suggested change
| # macOS uses install_name_tool (check first since Sys.isbsd is true for macOS too) | |
| # macOS uses install_name_tool |
Member
Author
There was a problem hiding this comment.
Claude initially didn't realize isbsd covered macos. This is just a correction comment that I should've dropped
| end | ||
|
|
||
| function _ensure_soname_macho(prefix::Prefix, path::AbstractString, platform::AbstractPlatform; | ||
| verbose::Bool = false, autofix::Bool = false, subdir::AbstractString="") |
Member
There was a problem hiding this comment.
The fact that this function takes autofix as argument is code smell: we shouldn't call this function to start with if autofixis false.
giordano
reviewed
Nov 29, 2025
Comment on lines
+88
to
+104
| if !retval | ||
| @lock AUDITOR_LOGGING_LOCK @warn("Unable to set SONAME on $(rel_path)") | ||
| return false | ||
| end | ||
|
|
||
| # Read the SONAME back in and ensure it's set properly | ||
| new_soname = get_soname(path) | ||
| if new_soname != soname | ||
| @lock AUDITOR_LOGGING_LOCK @warn("Set SONAME on $(rel_path) to $(soname), but read back $(string(new_soname))!") | ||
| return false | ||
| end | ||
|
|
||
| if verbose | ||
| @lock AUDITOR_LOGGING_LOCK @info("Set SONAME of $(rel_path) to \"$(soname)\"") | ||
| end | ||
|
|
||
| return true |
Member
There was a problem hiding this comment.
I'm not really sure defining the two functions buys us anything, I'm going to remove them.
giordano
reviewed
Nov 29, 2025
| return true | ||
| end | ||
|
|
||
| # macOS uses install_name_tool (check first since Sys.isbsd is true for macOS too) |
Member
There was a problem hiding this comment.
Fun, also weird comments are duplicated
ac7aa14 to
f939257
Compare
f939257 to
8440c20
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes two issues in the auditor:
Improved error reporting: When patchelf or other subprocess commands fail, the error now includes the subprocess output, making debugging much easier.
Fixed race condition in patchelf operations: The auditor uses nested
@threadsloops—iterating over files in parallel, and within each file, iterating over libraries in parallel. When multiple threads try to relink different libraries in the same binary, they callpatchelfon the same file concurrently, corrupting the ELF headers and causing "Missing ELF header" errors.Why this wasn't an issue before
In Julia ≤1.7, nested
@threadsloops effectively serialized because the default:staticscheduling pinned iterations to threads and didn't support nesting. Starting in Julia 1.8,@threadsdefaults to:dynamicscheduling (JuliaLang/julia#43919, #44136), which enables true nested parallelism—exposing the latent race condition.i.e. in 1.7 the inner
@threadsbecomes a standard for loop.julia +1.7 -t6
julia +1.12 -t6
Problem
The inner loop in
check_dynamic_linkageruns in parallel:Each iteration may call patchelf (via update_linkage, relink_to_rpath, or ensure_soname) on the same binary file (path(oh)). Without synchronization, concurrent patchelf writes corrupt the file.
Solution
Add per-file locking via with_patchelf_lock(path) to serialize patchelf operations on the same binary
Capture subprocess output and include it in error messages when commands fail