From 00f65dd4b3db4bc3f33105e321df1e22f83d062d Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Wed, 20 May 2026 08:00:04 -0400 Subject: [PATCH 1/7] Fixes #11, add macOS support for pure Ruby and C extensions --- .github/workflows/build.yml | 37 +++++++++++----- .github/workflows/test.yml | 32 +++++++++++++- CHANGELOG.md | 8 ++++ README.md | 48 ++++++++++++++++++++- ext/cruzzy/extconf.rb | 85 ++++++++++++++++++++++++++++++++++--- lib/ruzzy.rb | 5 ++- 6 files changed, 194 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3162ab1..7ccce1b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,27 +9,44 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: # https://www.ruby-lang.org/en/downloads/branches/ - ruby-version: - - "3.2" - - "3.3" - - "3.4" - - "4.0" + include: + - os: ubuntu-latest + ruby-version: "3.2" + ldshared-flags: "-shared" + - os: ubuntu-latest + ruby-version: "3.3" + ldshared-flags: "-shared" + - os: ubuntu-latest + ruby-version: "3.4" + ldshared-flags: "-shared" + - os: ubuntu-latest + ruby-version: "4.0" + ldshared-flags: "-shared" + - os: macos-latest + ruby-version: "4.0" + ldshared-flags: "-dynamic -bundle -undefined dynamic_lookup" steps: - uses: actions/checkout@v4 - name: Set up Ruby ${{ matrix.ruby-version }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} + - name: Install Homebrew LLVM + if: runner.os == 'macOS' + run: | + brew install llvm + echo "/opt/homebrew/opt/llvm/bin" >> "$GITHUB_PATH" - run: gem build - run: gem install --verbose ruzzy-*.gem env: RUZZY_DEBUG: "1" MAKE: "make --environment-overrides V=1" - CC: "clang" - CXX: "clang++" - LDSHARED: "clang -shared" - LDSHAREDXX: "clang++ -shared" + CC: clang + CXX: clang++ + LDSHARED: clang ${{ matrix.ldshared-flags }} + LDSHAREDXX: clang++ ${{ matrix.ldshared-flags }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be42f31..6d6de02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ on: workflow_dispatch: jobs: - test: + test-linux: runs-on: ubuntu-latest strategy: matrix: @@ -48,3 +48,33 @@ jobs: --env LD_PRELOAD=$(docker run --entrypoint ruby ruzzy -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ --entrypoint rake \ ruzzy test + + test-macos: + runs-on: macos-latest + strategy: + matrix: + include: + - ruby-version: "4.0" + llvm-version: "21" + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + - name: Install Homebrew LLVM + run: | + brew install llvm@${{ matrix.llvm-version }} + echo "/opt/homebrew/opt/llvm@${{ matrix.llvm-version }}/bin" >> "$GITHUB_PATH" + - run: gem build + - run: gem install --verbose ruzzy-*.gem + env: + RUZZY_DEBUG: "1" + MAKE: "make --environment-overrides V=1" + CC: clang + CXX: clang++ + LDSHARED: clang -dynamic -bundle -undefined dynamic_lookup + LDSHAREDXX: clang++ -dynamic -bundle -undefined dynamic_lookup + - name: Run tests + run: | + DYLD_INSERT_LIBRARIES=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ + rake test diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fcde15..52df58f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- macOS support for pure Ruby and C extension fuzzing ([#11](https://github.com/trailofbits/ruzzy/issues/11)) + +### Changed + +- Fixed argv0 handling of libFuzzer re-exec commands ([#30](https://github.com/trailofbits/ruzzy/issues/30)) + ## [0.8.0] - 2026-04-27 ### Added diff --git a/README.md b/README.md index 4e3fbc0..fdb67d4 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Table of contents: - [Fuzzing Ruby C extensions](#fuzzing-ruby-c-extensions) - [API](#api) - [FuzzedDataProvider](#fuzzeddataprovider) +- [Notes for macOS users](#notes-for-macos-users) - [Trophy case](#trophy-case) - [Developing](#developing) - [Compiling](#compiling) @@ -26,7 +27,7 @@ Table of contents: # Installing -Currently, Ruzzy only supports Linux x86-64 and AArch64/ARM64. If you'd like to run Ruzzy on a Mac or Windows, you can build the [`Dockerfile`](https://github.com/trailofbits/ruzzy/blob/main/Dockerfile) and/or use the [development environment](#developing). Ruzzy requires a recent version of `clang` (tested back to `14.0.0`), preferably the [latest release](https://github.com/llvm/llvm-project/releases). +Ruzzy supports Linux (x86-64, AArch64/ARM64) and macOS (Apple Silicon). On Windows, you can build the [`Dockerfile`](https://github.com/trailofbits/ruzzy/blob/main/Dockerfile) and/or use the [development environment](#developing). Ruzzy requires a recent version of `clang` (tested back to `14.0.0`), preferably the [latest release](https://github.com/llvm/llvm-project/releases). For macOS-specific setup, see [notes for macOS users](#notes-for-macos-users). Install Ruzzy with the following command: @@ -280,6 +281,51 @@ Ruzzy.fuzz(test_one_input) All methods return default values (`0`, `""`, `false`, `min`) when data is exhausted. +# Notes for macOS users + +Ruzzy on macOS requires Homebrew-installed LLVM (Apple Clang does not include libFuzzer) and a non-system Ruby (the system Ruby at `/usr/bin/ruby` is SIP-protected, which strips `DYLD_*` environment variables before Ruby starts). + +## Prerequisites + +```bash +brew install llvm ruby +``` + +Any non-system Ruby works (`brew`, `rbenv`, `asdf`), but see the [caveats](#caveats) below for shim-based version managers. + +## Installing + +Use the Homebrew Clang paths and macOS-appropriate linker flags: + +```bash +MAKE="make --environment-overrides V=1" \ +CC="$(brew --prefix llvm)/bin/clang" \ +CXX="$(brew --prefix llvm)/bin/clang++" \ +LDSHARED="$(brew --prefix llvm)/bin/clang -dynamic -bundle -undefined dynamic_lookup" \ +LDSHAREDXX="$(brew --prefix llvm)/bin/clang++ -dynamic -bundle -undefined dynamic_lookup" \ + gem install ruzzy +``` + +## Running + +Use `DYLD_INSERT_LIBRARIES` instead of `LD_PRELOAD`: + +```bash +DYLD_INSERT_LIBRARIES=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ + ruby -e 'require "ruzzy"; Ruzzy.dummy' +``` + +`Ruzzy::ASAN_PATH` and `Ruzzy::UBSAN_PATH` resolve to `.dylib` files on macOS. + +## Caveats + +- **Version manager shims (`asdf`, `rbenv`) strip `DYLD_*` env vars.** These shims use `#!/usr/bin/env bash` and `/usr/bin/env` is SIP-protected, so macOS strips `DYLD_INSERT_LIBRARIES` before Ruby starts. Either use Homebrew Ruby (which has no shim) or invoke the absolute path to the installed Ruby binary: + ```bash + DYLD_INSERT_LIBRARIES=$(/path/to/ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ + /path/to/ruby your_fuzzer.rb + ``` +- **Recent LLVM required.** Some older versions of Homebrew LLVM (notably 19.x) have a bug where `DYLD_INSERT_LIBRARIES`'ing the ASan dylib hangs the process during startup. If you see Ruzzy hang on launch, update Homebrew LLVM (`brew upgrade llvm`). + # Trophy case Bugs found using Ruzzy: diff --git a/ext/cruzzy/extconf.rb b/ext/cruzzy/extconf.rb index 260fcae..49082f6 100644 --- a/ext/cruzzy/extconf.rb +++ b/ext/cruzzy/extconf.rb @@ -9,6 +9,10 @@ LOGGER = Logger.new($stderr) LOGGER.level = ENV.key?('RUZZY_DEBUG') ? Logger::DEBUG : Logger::INFO +HOST_OS = RbConfig::CONFIG['host_os'] +MACOS = !!(HOST_OS =~ /darwin/) +DLEXT = MACOS ? 'dylib' : 'so' + # These ENV variables really shouldn't be used because we don't support # compilers other than clang, like gcc, etc. Instead prefer to properly include # clang in your PATH. But they're here if you really need them. Also note that @@ -19,9 +23,10 @@ CC = ENV.fetch('CC', 'clang') CXX = ENV.fetch('CXX', 'clang++') AR = ENV.fetch('AR', 'ar') -LD = ENV.fetch('LD', 'ld') +LD = ENV.fetch('LD', MACOS ? '/usr/bin/ld' : 'ld') FUZZER_NO_MAIN_LIB_ENV = 'FUZZER_NO_MAIN_LIB' +LOGGER.debug("Ruby OS: #{HOST_OS}") LOGGER.debug("Ruby CC: #{RbConfig::CONFIG['CC']}") LOGGER.debug("Ruby CXX: #{RbConfig::CONFIG['CXX']}") LOGGER.debug("Ruby AR: #{RbConfig::CONFIG['AR']}") @@ -38,6 +43,44 @@ def get_clang_file_name(file_name) end def merge_sanitizer_libfuzzer_lib(sanitizer_lib, fuzzer_no_main_lib, merged_output, *preinits) + if MACOS + # The same weak-symbol problem the Atheris doc below describes also occurs + # on macOS: if only the bare sanitizer dylib is DYLD_INSERT_LIBRARIES'd, + # its weak __sanitizer_cov_* stubs land in the global symbol table first, + # and a later-loaded instrumented C extension's sancov UNDEFs bind to + # those no-ops instead of libFuzzer's strong implementation. So macOS + # needs the same merge concept — libFuzzer and the sanitizer in the same + # preloaded image — even though the mechanics differ. + # + # The macOS sanitizer ships as a dylib (libclang_rt.asan_osx_dynamic.dylib), + # not a static archive, so we can't ar-strip preinits or --whole-archive + # merge. Instead, build a thin wrapper dylib that statically pulls in + # libFuzzer via -force_load and lists the sanitizer dylib as a runtime + # dependency. DYLD_INSERT_LIBRARIES of the wrapper auto-loads the + # sanitizer dylib via dyld dep resolution, so both ASan's weak sancov + # stubs and libFuzzer's strong sancov implementations enter the global + # namespace at the same load step — strong wins, just like the Linux + # merge guarantees. + LOGGER.debug("Building macOS wrapper dylib at #{merged_output} (libFuzzer #{fuzzer_no_main_lib} + #{sanitizer_lib})") + + _, status = Open3.capture2( + CXX, + '-dynamiclib', + "-Wl,-force_load,#{fuzzer_no_main_lib}", + sanitizer_lib, + '-lpthread', + '-lc++', + "-Wl,-install_name,@rpath/#{File.basename(merged_output)}", + '-o', + merged_output + ) + unless status.success? + LOGGER.error("The #{CXX} dylib build command failed.") + exit(1) + end + return + end + # https://github.com/google/atheris/blob/master/native_extension_fuzzing.md#why-this-is-necessary Tempfile.create do |file| LOGGER.debug("Creating #{sanitizer_lib} sanitizer archive at #{file.path}") @@ -90,7 +133,8 @@ def merge_sanitizer_libfuzzer_lib(sanitizer_lib, fuzzer_no_main_lib, merged_outp fuzzer_no_main_libs = [ 'libclang_rt.fuzzer_no_main.a', 'libclang_rt.fuzzer_no_main-aarch64.a', - 'libclang_rt.fuzzer_no_main-x86_64.a' + 'libclang_rt.fuzzer_no_main-x86_64.a', + 'libclang_rt.fuzzer_no_main_osx.a' ] fuzzer_no_main_lib = fuzzer_no_main_libs.map { |lib| get_clang_file_name(lib) }.find(&:itself) @@ -104,7 +148,8 @@ def merge_sanitizer_libfuzzer_lib(sanitizer_lib, fuzzer_no_main_lib, merged_outp asan_libs = [ 'libclang_rt.asan.a', 'libclang_rt.asan-aarch64.a', - 'libclang_rt.asan-x86_64.a' + 'libclang_rt.asan-x86_64.a', + 'libclang_rt.asan_osx_dynamic.dylib' ] asan_lib = asan_libs.map { |lib| get_clang_file_name(lib) }.find(&:itself) @@ -116,7 +161,7 @@ def merge_sanitizer_libfuzzer_lib(sanitizer_lib, fuzzer_no_main_lib, merged_outp merge_sanitizer_libfuzzer_lib( asan_lib, fuzzer_no_main_lib, - 'asan_with_fuzzer.so', + "asan_with_fuzzer.#{DLEXT}", 'asan_preinit.cc.o', 'asan_preinit.cpp.o' ) @@ -124,7 +169,8 @@ def merge_sanitizer_libfuzzer_lib(sanitizer_lib, fuzzer_no_main_lib, merged_outp ubsan_libs = [ 'libclang_rt.ubsan_standalone.a', 'libclang_rt.ubsan_standalone-aarch64.a', - 'libclang_rt.ubsan_standalone-x86_64.a' + 'libclang_rt.ubsan_standalone-x86_64.a', + 'libclang_rt.ubsan_osx_dynamic.dylib' ] ubsan_lib = ubsan_libs.map { |lib| get_clang_file_name(lib) }.find(&:itself) @@ -136,7 +182,7 @@ def merge_sanitizer_libfuzzer_lib(sanitizer_lib, fuzzer_no_main_lib, merged_outp merge_sanitizer_libfuzzer_lib( ubsan_lib, fuzzer_no_main_lib, - 'ubsan_with_fuzzer.so', + "ubsan_with_fuzzer.#{DLEXT}", 'ubsan_init_standalone_preinit.cc.o', 'ubsan_init_standalone_preinit.cpp.o' ) @@ -144,7 +190,32 @@ def merge_sanitizer_libfuzzer_lib(sanitizer_lib, fuzzer_no_main_lib, merged_outp # The LOCAL_LIBS variable allows linking arbitrary libraries into Ruby C # extensions. It is supported by the Ruby mkmf library and C extension Makefile. # For more information, see https://github.com/ruby/ruby/blob/master/lib/mkmf.rb. -$LOCAL_LIBS = fuzzer_no_main_lib +# +# On macOS we deliberately skip statically linking libFuzzer into cruzzy.bundle. +# Doing so would produce two libFuzzer instances in the process at runtime: +# one inside cruzzy.bundle (statically linked), one inside the +# DYLD_INSERT_LIBRARIES'd asan_with_fuzzer.dylib (force_load'd in the merge +# step). cruzzy's call to LLVMFuzzerRunDriver is a direct branch to its local +# copy because macOS's ld64 emits non-preemptible calls to symbols it +# resolves at link time. Meanwhile a sancov-instrumented C extension loaded +# later (e.g. dummy.bundle) has its sancov refs as flat-namespace UNDEFs +# that dyld resolves at runtime, picking the wrapper's strong symbols. +# Result: the fuzz driver and the instrumented code register with different +# libFuzzer instances that share no state, so libFuzzer sees no coverage +# feedback (corpus stays at 1 entry, "Is the code instrumented for +# coverage?" warning). +# +# Linux is unaffected because ELF shared objects default to semantic +# interposition: cruzzy.so's call to LLVMFuzzerRunDriver goes through the +# PLT and resolves to whichever copy is first in the global symbol table, +# i.e. the LD_PRELOAD'd asan_with_fuzzer.so. Both copies converge on the +# preloaded instance, so the duplicate is harmless. +# +# With macOS's mkmf default of `-undefined dynamic_lookup`, leaving +# $LOCAL_LIBS unset lets cruzzy.bundle ship with UNDEF refs to +# LLVMFuzzerRunDriver and __sanitizer_cov_*. These resolve at runtime to +# the single libFuzzer instance in the preloaded wrapper. +$LOCAL_LIBS = fuzzer_no_main_lib unless MACOS $LIBS << ' -lstdc++' $DLDFLAGS << " -fuse-ld=#{LD}" diff --git a/lib/ruzzy.rb b/lib/ruzzy.rb index 6215c5e..a19017c 100644 --- a/lib/ruzzy.rb +++ b/lib/ruzzy.rb @@ -14,8 +14,9 @@ module Ruzzy ARGV0 = ENV.fetch('RUZZY_ARGV0', $PROGRAM_NAME) DEFAULT_ARGS = [ARGV0] + ARGV EXT_PATH = Pathname.new(__FILE__).parent.parent / 'ext' / 'cruzzy' - ASAN_PATH = (EXT_PATH / 'asan_with_fuzzer.so').to_s - UBSAN_PATH = (EXT_PATH / 'ubsan_with_fuzzer.so').to_s + DLEXT = RbConfig::CONFIG['host_os'] =~ /darwin/ ? 'dylib' : 'so' + ASAN_PATH = (EXT_PATH / "asan_with_fuzzer.#{DLEXT}").to_s + UBSAN_PATH = (EXT_PATH / "ubsan_with_fuzzer.#{DLEXT}").to_s def fuzz(test_one_input, args = DEFAULT_ARGS) c_fuzz(test_one_input, args) From 571c28eef0ac947b68af023593fa5b258ae13c2a Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Wed, 20 May 2026 08:04:14 -0400 Subject: [PATCH 2/7] Add dev dependencies in macOS CI --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d6de02..dd3b6c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,7 @@ jobs: brew install llvm@${{ matrix.llvm-version }} echo "/opt/homebrew/opt/llvm@${{ matrix.llvm-version }}/bin" >> "$GITHUB_PATH" - run: gem build - - run: gem install --verbose ruzzy-*.gem + - run: gem install --verbose --development ruzzy-*.gem env: RUZZY_DEBUG: "1" MAKE: "make --environment-overrides V=1" From 48deaf2ea8a21b5c5804324cca6139baa8061526 Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Wed, 20 May 2026 08:43:42 -0400 Subject: [PATCH 3/7] Run with arm64 not arm64e in macOS CI --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dd3b6c0..7a12a90 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,5 +76,5 @@ jobs: LDSHAREDXX: clang++ -dynamic -bundle -undefined dynamic_lookup - name: Run tests run: | - DYLD_INSERT_LIBRARIES=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ - rake test + DYLD_INSERT_LIBRARIES=$(arch -arm64 ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ + arch -arm64 rake test From e5341989b95c05dfeac27a28088185d4a62625c6 Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Wed, 20 May 2026 08:50:02 -0400 Subject: [PATCH 4/7] Try loader hack to bypass SIP --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7a12a90..5cb6b71 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,5 +76,7 @@ jobs: LDSHAREDXX: clang++ -dynamic -bundle -undefined dynamic_lookup - name: Run tests run: | + LOADER=$(arch -arm64 ruby -rrake -e 'puts Gem.loaded_specs["rake"].full_gem_path')/lib/rake/rake_test_loader.rb \ DYLD_INSERT_LIBRARIES=$(arch -arm64 ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ - arch -arm64 rake test + ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0:abort_on_error=0" \ + arch -arm64 ruby -w -Ilib "$LOADER" test/test_*.rb From 01635b900577282204e4381bf22dbce1d5e32e8a Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Wed, 20 May 2026 09:01:52 -0400 Subject: [PATCH 5/7] More SIP avoidance hacks --- .github/workflows/test.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5cb6b71..5d5bf74 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -58,13 +58,11 @@ jobs: llvm-version: "21" steps: - uses: actions/checkout@v4 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby-version }} - - name: Install Homebrew LLVM + - name: Install Homebrew LLVM and Ruby run: | - brew install llvm@${{ matrix.llvm-version }} - echo "/opt/homebrew/opt/llvm@${{ matrix.llvm-version }}/bin" >> "$GITHUB_PATH" + brew install llvm@${{ matrix.llvm-version }} ruby@${{ matrix.ruby-version }} + echo "$(brew --prefix llvm@${{ matrix.llvm-version }})/bin" >> "$GITHUB_PATH" + echo "$(brew --prefix ruby@${{ matrix.ruby-version }})/bin" >> "$GITHUB_PATH" - run: gem build - run: gem install --verbose --development ruzzy-*.gem env: @@ -76,7 +74,8 @@ jobs: LDSHAREDXX: clang++ -dynamic -bundle -undefined dynamic_lookup - name: Run tests run: | - LOADER=$(arch -arm64 ruby -rrake -e 'puts Gem.loaded_specs["rake"].full_gem_path')/lib/rake/rake_test_loader.rb \ - DYLD_INSERT_LIBRARIES=$(arch -arm64 ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ + # Need this rake loader hack to avoid SIP-protected programs that strip DYLD_* + LOADER=$(ruby -rrake -e 'puts Gem.loaded_specs["rake"].full_gem_path')/lib/rake/rake_test_loader.rb \ + DYLD_INSERT_LIBRARIES=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0:abort_on_error=0" \ - arch -arm64 ruby -w -Ilib "$LOADER" test/test_*.rb + ruby -w -Ilib "$LOADER" test/test_*.rb From e1265953ed9e269622578bbe96e262c15410c9c9 Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Wed, 20 May 2026 09:04:51 -0400 Subject: [PATCH 6/7] Fix comment location --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5d5bf74..a8cfb14 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,8 +73,8 @@ jobs: LDSHARED: clang -dynamic -bundle -undefined dynamic_lookup LDSHAREDXX: clang++ -dynamic -bundle -undefined dynamic_lookup - name: Run tests + # Need this rake loader hack to avoid SIP-protected programs that strip DYLD_* run: | - # Need this rake loader hack to avoid SIP-protected programs that strip DYLD_* LOADER=$(ruby -rrake -e 'puts Gem.loaded_specs["rake"].full_gem_path')/lib/rake/rake_test_loader.rb \ DYLD_INSERT_LIBRARIES=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0:abort_on_error=0" \ From 42fb3556b02d27f25b198d80b6429732839f4f59 Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Wed, 20 May 2026 09:16:17 -0400 Subject: [PATCH 7/7] Fix ENV variable initialization --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a8cfb14..7b1244a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -75,7 +75,7 @@ jobs: - name: Run tests # Need this rake loader hack to avoid SIP-protected programs that strip DYLD_* run: | - LOADER=$(ruby -rrake -e 'puts Gem.loaded_specs["rake"].full_gem_path')/lib/rake/rake_test_loader.rb \ + LOADER=$(ruby -rrake -e 'puts Gem.loaded_specs["rake"].full_gem_path')/lib/rake/rake_test_loader.rb DYLD_INSERT_LIBRARIES=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \ ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0:abort_on_error=0" \ ruby -w -Ilib "$LOADER" test/test_*.rb