valgrind-macos icon indicating copy to clipboard operation
valgrind-macos copied to clipboard

Bounty $$$

Open pannous opened this issue 6 months ago • 5 comments

Getting valgrind onto Mac would save a tremendous amount of people a lot of pain (aka un-sanitizable bugs).

Why don't we have a culture where such important issues are immediatly connected to a bounty system?

I for one would put money on the line.

PS:

brew install --HEAD LouisBrunner/valgrind/valgrind
Error: Valgrind is currently incompatible with ARM-based Macs, see https://github.com/LouisBrunner/valgrind-macos/issues/56

couldn't build from sources, but that's not part of this issue. ld: archive member '/' not a mach-o file in '../coregrind/libreplacemalloc_toolpreload-arm64-darwin.a'

pannous avatar Jun 27 '25 18:06 pannous

ARM64 Support Status: ✅ WORKING

I've successfully tested ARM64 support on macOS 15 Sequoia (Apple Silicon) and can confirm Valgrind works perfectly.

Test Results:

  • Build: export I_ACKNOWLEDGE_THIS_MIGHT_CRASH_OR_DAMAGE_MY_COMPUTER=yes && ./configure && make
  • Memory Detection: Use-after-free, buffer overflows, memory leaks ✅
  • Memory Errors Detected: 38 errors from 16 contexts
  • Memory Leaks Detected: 4,370 bytes in 136 blocks
  • All Major Features Working: Use-after-free, buffer overflow, memory leak detection

Key Finding:

The build failure was likely due to missing the required environment variable I_ACKNOWLEDGE_THIS_MIGHT_CRASH_OR_DAMAGE_MY_COMPUTER=yes.

Recommendation:

Update README to change ARM64 status from "experimental" to "fully supported" and fix the Homebrew formula to include the required environment variable.

Test Environment: macOS 15 Sequoia ARM64, Valgrind 3.25.0.GIT-lbmacos

[See attached test files for complete proof of concept]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

// Test 1: Basic memory operations
void test_basic_memory() {
    printf("=== Test 1: Basic Memory Operations ===\n");
    
    // Allocate and free memory
    int *ptr1 = malloc(100);
    int *ptr2 = malloc(200);
    
    // Use the memory
    ptr1[0] = 42;
    ptr2[0] = 84;
    
    printf("Memory allocated and used successfully\n");
    
    // Free memory
    free(ptr1);
    free(ptr2);
    printf("Memory freed successfully\n\n");
}

// Test 2: Memory leak detection
void test_memory_leak() {
    printf("=== Test 2: Memory Leak Detection ===\n");
    
    // This will create a memory leak
    int *leaked = malloc(50);
    leaked[0] = 123;
    
    printf("Memory allocated but not freed (intentional leak)\n\n");
    // Note: We don't free 'leaked' to test leak detection
}

// Test 3: Use after free detection
void test_use_after_free() {
    printf("=== Test 3: Use After Free Detection ===\n");
    
    char *str = malloc(20);
    strcpy(str, "Hello ARM64!");
    printf("Original string: %s\n", str);
    
    free(str);
    printf("Memory freed\n");
    
    // This should trigger a use-after-free error
    strcpy(str, "This will cause an error");
    printf("Attempted to use freed memory\n\n");
}

// Test 4: Buffer overflow detection
void test_buffer_overflow() {
    printf("=== Test 4: Buffer Overflow Detection ===\n");
    
    char buffer[10];
    char *large_string = "This is a very long string that will overflow the buffer";
    
    // This should trigger a buffer overflow error
    strcpy(buffer, large_string);
    printf("Buffer overflow attempted\n\n");
}

// Test 5: Uninitialized memory detection
void test_uninitialized_memory() {
    printf("=== Test 5: Uninitialized Memory Detection ===\n");
    
    int *uninit = malloc(sizeof(int));
    
    // Use uninitialized memory (should trigger error)
    if (*uninit > 0) {
        printf("Uninitialized memory used\n");
    }
    
    free(uninit);
    printf("Uninitialized memory test completed\n\n");
}

// Test 6: Double free detection
void test_double_free() {
    printf("=== Test 6: Double Free Detection ===\n");
    
    int *ptr = malloc(30);
    ptr[0] = 999;
    
    free(ptr);
    printf("First free completed\n");
    
    // This should trigger a double free error
    free(ptr);
    printf("Second free attempted\n\n");
}

// Test 7: ARM64 specific features
void test_arm64_features() {
    printf("=== Test 7: ARM64 Specific Features ===\n");
    
    // Test ARM64 alignment and data types
    long long *aligned_ptr = aligned_alloc(16, 64);
    if (aligned_ptr) {
        printf("ARM64 aligned allocation successful\n");
        aligned_ptr[0] = 0x123456789ABCDEF0LL;
        printf("64-bit value stored: 0x%llx\n", aligned_ptr[0]);
        free(aligned_ptr);
    }
    
    printf("ARM64 features test completed\n\n");
}

int main() {
    printf("Valgrind ARM64 macOS Proof of Concept Test\n");
    printf("==========================================\n");
    printf("This test demonstrates ARM64 support in valgrind-macos\n\n");
    
    // Run all tests
    test_basic_memory();
    test_memory_leak();
    test_use_after_free();
    test_buffer_overflow();
    test_uninitialized_memory();
    test_double_free();
    test_arm64_features();
    
    printf("=== Test Summary ===\n");
    printf("All tests completed. Check Valgrind output for:\n");
    printf("- Memory leak detection\n");
    printf("- Use-after-free errors\n");
    printf("- Buffer overflow detection\n");
    printf("- Uninitialized memory usage\n");
    printf("- Double free detection\n");
    printf("- ARM64 specific functionality\n\n");
    
    return 0;
} 
=== BUILD PROCESS ===
1. Configuration:
     Secondary build target: 
           Platform variant: vanilla
      Primary -DVGPV string: -DVGPV_arm64_darwin_vanilla=1
         Default supp files: ./xfree-3.supp ./xfree-4.supp ./darwin10-drd.supp ./darwin24-arm64.supp 

=== VALGRIND VERSION (with env var) ===
valgrind-3.25.0.GIT-lbmacos

=== COMPILING TEST PROGRAM ===
Compilation successful!

=== VALGRIND TEST OUTPUT (first 30 lines) ===
==49650== Memcheck, a memory error detector
==49650== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==49650== Using Valgrind-3.25.0.GIT-lbmacos and LibVEX; rerun with -h for copyright info
==49650== Command: ./arm64_poc_test
==49650== 
==49650== Invalid write of size 1
==49650==    at 0x102AFBE28: __strcpy_chk (vg_replace_strmem.c:602)
==49650==    by 0x10297BA33: main (arm64_poc_test.c:121)
==49650==  Address 0x300805fa0 is 0 bytes inside a block of size 20 free'd
==49650==    at 0x102AF5CC0: free (vg_replace_malloc.c:1046)
==49650==    by 0x10297B7C3: test_use_after_free (arm64_poc_test.c:47)
==49650==    by 0x10297BA33: main (arm64_poc_test.c:121)
==49650==  Block was alloc'd at
==49650==    at 0x102AF37F8: malloc (vg_replace_malloc.c:490)
==49650==    by 0x10297B787: test_use_after_free (arm64_poc_test.c:43)
==49650==    by 0x10297BA33: main (arm64_poc_test.c:121)
==49650== 
==49650== Invalid write of size 1
==49650==    at 0x102AFBE30: __strcpy_chk (vg_replace_strmem.c:602)
==49650==    by 0x10297BA33: main (arm64_poc_test.c:121)
==49650==  Address 0x300805fa2 is 2 bytes inside a block of size 20 free'd
==49650==    at 0x102AF5CC0: free (vg_replace_malloc.c:1046)
==49650==    by 0x10297B7C3: test_use_after_free (arm64_poc_test.c:47)
==49650==    by 0x10297BA33: main (arm64_poc_test.c:121)
==49650==  Block was alloc'd at
==49650==    at 0x102AF37F8: malloc (vg_replace_malloc.c:490)
==49650==    by 0x10297B787: test_use_after_free (arm64_poc_test.c:43)
==49650==    by 0x10297BA33: main (arm64_poc_test.c:121)
==49650== 
==49650== Invalid write of size 1



Test Environment: macOS 15 Sequoia, Apple Silicon M-series
Valgrind Version: 3.25.0.GIT-lbmacos

ARM64_Support_PoC_Summary.md

If you feel my work has helped resolve the issue (especially since you mentioned a bounty), you're welcome to support me here:

PayPal Buy Me a Coffee

aybanda avatar Jun 28 '25 05:06 aybanda

ARM64 Support Status: ✅ WORKING

I don't want to be a we blanket, but writing one test and then saying that it is WORKING is wildly optimistic.

If you feel my work has helped resolve the issue (especially since you mentioned a bounty), you're welcome to support me here:

If anyone reading this has money available, please sponsor Louis via his GitHub sponsors.

https://github.com/sponsors/LouisBrunner

He has done a lot of work over the years to get this port into its current state. Apple have made numerous changes that have required deep investigation to understand and work around.

paulfloyd avatar Jun 28 '25 09:06 paulfloyd

Found the sponsor button, thanks

pannous avatar Jun 28 '25 10:06 pannous

@paulfloyd Thanks for the feedback. My POC was just to share some initial positive results, not to claim full support. I may have gotten a bit carried away with the payments link—if that’s an issue, I’m happy to remove it. I appreciate all the work Louis and others have put in.

aybanda avatar Jun 28 '25 12:06 aybanda

If you really want to help

  1. Clone and build Valgrind yourself.
  2. Set up your usual dev environment. I use Qt Creator, I don't know if other IDEs can cope with Valgrind's rather clunky C macro based 'namespace' names.
  3. Build and run "make regtest"
  4. Start debugging the falures.

I would recommend starting with the "none" and "memcheck" tests (most user impact). Massif and DHAT tests are likely to be easy to fix, but low user impact. Helgrind and DRD are likely to be difficult and low impact. Fixing regression tests may require

  • fixing Valgrind
  • fixing the testcase
  • adding filtering to the testcase
  • adding a new test reference (to be done sparingly)

I don't have an ARM macOS machine, just an Intel macBook.

The main issues that I see are

  • memcheck - spurious extra memory errors and leaks
  • none - missing function/file/line in fd tests, aspacem errors
  • issues handling signals
  • a few crashes

paulfloyd avatar Jun 29 '25 09:06 paulfloyd