Merge pull request #23429 from Homebrew/ruby-macho-x86-signing

Use codesign to sign patched binaries on Intel
This commit is contained in:
Mike McQuaid
2026-08-04 15:42:40 +00:00
committed by GitHub
4 changed files with 74 additions and 2 deletions
+4 -1
View File
@@ -64,7 +64,10 @@ module OS
check_access_directories
]
# Developer tools are checked when building from source.
# We need the developer tools for `codesign` on Intel:
# https://github.com/Homebrew/brew/issues/23418
checks << "check_for_installed_developer_tools" unless ::Hardware::CPU.arm?
checks.freeze
end
+37
View File
@@ -108,6 +108,43 @@ module OS
def codesign_patched_binary(file)
return if MacOS.version < :big_sur
unless ::Hardware::CPU.arm?
# Intel macOS rejects ruby-macho's ad-hoc signatures on larger
# binaries and does not require unsigned binaries to be signed,
# so use `codesign` to re-sign only the binaries whose existing
# signature our modifications have just broken:
# https://github.com/Homebrew/brew/issues/23418
result = system_command("codesign", args: ["--verify", file], print_stderr: false)
return unless result.stderr.match?(/invalid signature/i)
odebug "Codesigning #{file}"
return if quiet_system("codesign", "--sign", "-", "--force",
"--preserve-metadata=entitlements,requirements,flags,runtime",
file)
# If the codesigning fails, it may be a bug in Apple's codesign utility.
# A known workaround is to copy the file to another inode, then move it back
# erasing the previous file. Then sign again.
Dir::Tmpname.create("workaround") do |tmppath|
FileUtils.cp file, tmppath
FileUtils.mv tmppath, file, force: true
end
odebug "Codesigning (2nd try) #{file}"
result = system_command("codesign", args: [
"--sign", "-", "--force",
"--preserve-metadata=entitlements,requirements,flags,runtime",
file
], print_stderr: false)
return if result.success?
onoe <<~EOS
Failed applying an ad-hoc signature to #{file}:
#{result.stderr}
EOS
return
end
require "macho"
odebug "Codesigning #{file}"
@@ -68,6 +68,12 @@ RSpec.describe Homebrew::Diagnostic::Checks do
expect(checks.fatal_preinstall_checks).not_to include("check_for_installed_developer_tools")
end
it "requires developer tools on Intel" do
allow(Hardware::CPU).to receive(:arm?).and_return(false)
expect(checks.fatal_preinstall_checks).to include("check_for_installed_developer_tools")
end
end
describe "#fatal_build_from_source_checks" do
+27 -1
View File
@@ -43,12 +43,38 @@ RSpec.describe Keg do
allow(MacOS).to receive(:version).and_return(MacOSVersion.new("11"))
end
it "signs patched binaries using ruby-macho" do
it "signs patched binaries using ruby-macho on Apple Silicon" do
allow(Hardware::CPU).to receive(:arm?).and_return(true)
expect(keg).not_to receive(:system_command).with("codesign", any_args)
expect(keg).not_to receive(:quiet_system).with("codesign", any_args)
expect(MachO).to receive(:codesign!).with(file)
keg.codesign_patched_binary(file)
end
it "re-signs binaries whose signature has been broken using codesign on Intel" do
allow(Hardware::CPU).to receive(:arm?).and_return(false)
expect(MachO).not_to receive(:codesign!)
expect(keg).to receive(:system_command)
.with("codesign", args: ["--verify", file], print_stderr: false)
.and_return(instance_double(SystemCommand::Result, stderr: "#{file}: invalid signature"))
expect(keg).to receive(:quiet_system)
.with("codesign", "--sign", "-", "--force",
"--preserve-metadata=entitlements,requirements,flags,runtime", file)
.and_return(true)
keg.codesign_patched_binary(file)
end
it "does not sign unsigned binaries on Intel" do
allow(Hardware::CPU).to receive(:arm?).and_return(false)
expect(MachO).not_to receive(:codesign!)
expect(keg).to receive(:system_command)
.with("codesign", args: ["--verify", file], print_stderr: false)
.and_return(instance_double(SystemCommand::Result, stderr: "#{file}: code object is not signed at all"))
expect(keg).not_to receive(:quiet_system).with("codesign", any_args)
keg.codesign_patched_binary(file)
end
end
end