From f01b2b423283c62a2d6916c6baf4e97da31911ba Mon Sep 17 00:00:00 2001 From: Thomas Powell Date: Mon, 13 Oct 2025 15:00:11 -0400 Subject: [PATCH 1/3] Tests to try an replicate a core dump on CI Signed-off-by: Thomas Powell --- .github/workflows/unit.yml | 6 +- spec/array_spec.rb | 145 +++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index fd1d63b..d138d62 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -9,12 +9,12 @@ name: unit jobs: test: - runs-on: ${{ matrix.os }}-latest + runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos] + os: [macos-12, macos-15] ruby: ['3.1', '3.4'] - name: Unit test on Ruby ${{ matrix.os }} with Ruby ${{ matrix.ruby }} + name: Unit test on ${{ matrix.os }} with Ruby ${{ matrix.ruby }} steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 diff --git a/spec/array_spec.rb b/spec/array_spec.rb index a7cb059..4692ae4 100644 --- a/spec/array_spec.rb +++ b/spec/array_spec.rb @@ -98,4 +98,149 @@ expect(values).to eq({ 0 => CF::Boolean::TRUE, 1 => CF::String.from_string('123') }) end end + + # Test to simulate the crash that occurs in Chef's macos_userdefaults resource + # when processing array values on Intel macOS + describe 'crash simulation for array processing on Intel macOS' do + context 'with an array of file paths similar to Chef macos_userdefaults' do + let(:file_paths) do + [ + "/Library/Managed Installs/fake.log", + "/Library/Managed Installs/also_fake.log" + ] + end + + it 'should successfully create an immutable array from string paths' do + cf_strings = file_paths.map { |path| CF::String.from_string(path) } + expect { CF::Array.immutable(cf_strings) }.not_to raise_error + end + + it 'should successfully iterate over an array of file paths without segfault' do + cf_strings = file_paths.map { |path| CF::String.from_string(path) } + array = CF::Array.immutable(cf_strings) + + collected_values = [] + expect { + array.each do |value| + collected_values << value + end + }.not_to raise_error + + expect(collected_values.length).to eq(2) + expect(collected_values[0]).to be_a(CF::String) + expect(collected_values[1]).to be_a(CF::String) + end + + it 'should successfully convert array to ruby without segfault' do + cf_strings = file_paths.map { |path| CF::String.from_string(path) } + array = CF::Array.immutable(cf_strings) + + expect { array.to_ruby }.not_to raise_error + expect(array.to_ruby).to eq(file_paths) + end + + it 'should handle CFArrayApplyFunction callback correctly' do + cf_strings = file_paths.map { |path| CF::String.from_string(path) } + array = CF::Array.immutable(cf_strings) + + # This directly tests the each method which calls CFArrayApplyFunction + # where the segfault occurs at 0x0000000000000000 + values_from_callback = [] + expect { + range = CF::Range.new + range[:location] = 0 + range[:length] = array.length + callback = lambda do |value, _| + # value should be a valid pointer, not null + expect(value).not_to be_nil + expect(value.null?).to be false + values_from_callback << CF::Base.typecast(value) + end + CF.CFArrayApplyFunction(array, range, callback, nil) + }.not_to raise_error + + expect(values_from_callback.length).to eq(2) + end + end + + context 'with a mutable array of file paths' do + let(:file_paths) do + [ + "/Library/Managed Installs/fake.log", + "/Library/Managed Installs/also_fake.log" + ] + end + + it 'should successfully append and iterate over file paths in mutable array' do + array = CF::Array.mutable + + file_paths.each do |path| + cf_string = CF::String.from_string(path) + expect { array << cf_string }.not_to raise_error + end + + expect(array.length).to eq(2) + + collected_values = [] + expect { + array.each do |value| + collected_values << value.to_ruby + end + }.not_to raise_error + + expect(collected_values).to eq(file_paths) + end + end + + context 'with refinements (simulating Chef usage pattern)' do + using CF::Refinements + + let(:file_paths) do + [ + "/Library/Managed Installs/fake.log", + "/Library/Managed Installs/also_fake.log" + ] + end + + it 'should convert Ruby array to CF array using refinements without segfault' do + cf_array = nil + expect { + cf_array = file_paths.to_cf + }.not_to raise_error + + expect(cf_array).to be_a(CF::Array) + expect(cf_array.length).to eq(2) + end + + it 'should iterate over CF array created from refinements without segfault' do + cf_array = file_paths.to_cf + + collected_values = [] + expect { + cf_array.each do |value| + collected_values << value + end + }.not_to raise_error + + expect(collected_values.length).to eq(2) + expect(collected_values.map(&:to_ruby)).to eq(file_paths) + end + + it 'should successfully use array in preferences-like scenario' do + # This simulates the usage pattern in Chef's macos_userdefaults + cf_array = file_paths.to_cf + + # Test that we can retrieve individual elements + expect { cf_array[0] }.not_to raise_error + expect { cf_array[1] }.not_to raise_error + + expect(cf_array[0].to_ruby).to eq(file_paths[0]) + expect(cf_array[1].to_ruby).to eq(file_paths[1]) + + # Test that we can convert back to Ruby + expect { cf_array.to_ruby }.not_to raise_error + expect(cf_array.to_ruby).to eq(file_paths) + end + end + end end \ No newline at end of file From e02d3e4d60763b4c169f04a4a273d027e76ceff5 Mon Sep 17 00:00:00 2001 From: Thomas Powell Date: Mon, 13 Oct 2025 15:11:00 -0400 Subject: [PATCH 2/3] Add more intel Signed-off-by: Thomas Powell --- .github/workflows/unit.yml | 40 +++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index d138d62..20bf4ab 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -12,9 +12,43 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-12, macos-15] - ruby: ['3.1', '3.4'] - name: Unit test on ${{ matrix.os }} with Ruby ${{ matrix.ruby }} + include: + # Intel runners - oldest supported (macos-12) + - os: macos-12 + ruby: '3.1' + architecture: 'Intel' + - os: macos-12 + ruby: '3.4' + architecture: 'Intel' + # Intel runners - newest (macos-13) + - os: macos-13 + ruby: '3.1' + architecture: 'Intel' + - os: macos-13 + ruby: '3.4' + architecture: 'Intel' + # Intel runners - macos-15-xlarge (Intel on macOS 15) + - os: macos-15-intel + ruby: '3.1' + architecture: 'Intel' + - os: macos-15-intel + ruby: '3.4' + architecture: 'Intel' + # Apple Silicon runners - oldest (macos-14) + - os: macos-14 + ruby: '3.1' + architecture: 'Apple Silicon' + - os: macos-14 + ruby: '3.4' + architecture: 'Apple Silicon' + # Apple Silicon runners - newest (macos-15) + - os: macos-15 + ruby: '3.1' + architecture: 'Apple Silicon' + - os: macos-15 + ruby: '3.4' + architecture: 'Apple Silicon' + name: Unit test on ${{ matrix.os }} (${{ matrix.architecture }}) with Ruby ${{ matrix.ruby }} steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 From 2b1b2036ce116ffd80081bd2551af909e59c85cb Mon Sep 17 00:00:00 2001 From: Thomas Powell Date: Mon, 13 Oct 2025 15:40:50 -0400 Subject: [PATCH 3/3] Test on multiple macOS Signed-off-by: Thomas Powell --- .github/workflows/unit.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 20bf4ab..05ed31a 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -13,21 +13,13 @@ jobs: strategy: matrix: include: - # Intel runners - oldest supported (macos-12) - - os: macos-12 - ruby: '3.1' - architecture: 'Intel' - - os: macos-12 - ruby: '3.4' - architecture: 'Intel' - # Intel runners - newest (macos-13) + # Intel runners - os: macos-13 ruby: '3.1' architecture: 'Intel' - os: macos-13 ruby: '3.4' architecture: 'Intel' - # Intel runners - macos-15-xlarge (Intel on macOS 15) - os: macos-15-intel ruby: '3.1' architecture: 'Intel'