Malloc double free for ptr - error

Hello,

I'm confused in using pure Swift without more than import Foundation in result of get malloc double free ptr message at run a less than 200 (think simple) lines of code.

full error message (cli with time swift run -c debug )

HashSequence(14561,0x700008dee000) malloc: double free for ptr 0x7fdff380e000
HashSequence(14561,0x700008dee000) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort swift run -c debug

or in Xcode

HashSequence(14618,0x70000ecd2000) malloc: pointer 0x1001ac000 being freed was not allocated
HashSequence(14618,0x70000ecd2000) malloc: *** set a breakpoint in malloc_error_break to debug

Because Java is to slow and I want more abstraction like in C I use Swift (for this side project). I do not want in my business code "working with RAM allocation / deallocation" and so I hoped Swift is the abstraction layer for me.

My questions at this time:
Is it normal to get a malloc error in (Standard) Swift, if I work with basic Swift features?

Thx
Sebastian


System: Xcode 14.2, Swift 5.7.1, macOS 13.0.1 (Intel)

Yes, Swift still uses malloc to implement most of its memory management on macOS. I’m still a bit surprised you’re seeing memory problems, but you haven’t shown any code, so for all I know you’re using Unmanaged or UnsafePointer for all your work.

I do not use Unmanaged or UnsafePointer directly.
I have reduced the code to post here with error; - needed struct PearsonHash is on GitHub:

I get the malloc error after a add in func case4 the lines for OperationQueue as same as in func case5.

import Foundation

@available(macOS 10.15.4, *)
@main
public struct HashSequence {
    
    public static func main () {
        let instance = HashSequence()
        instance.computeTest()
    }
    
    private func computeTest () {
        let _ = collision(length: 4, hash: 0) // Laufzeit rund 5:03 Minuten
    }
    
    public func collision (length : Int, hash expected : UInt8) -> [[UInt8]] {
        var result : [[UInt8]] = []
        switch length {
        case 4:
            result = case4(hash: expected)
        default : print ("NOT YET IMPLEMENTED")
        }
        return result
    }
    
    private func case4 (hash expected : UInt8) -> [[UInt8]] {
        var result : [[UInt8]] = []

        let operations2 = OperationQueue()

        for b0 in 0...255 as ClosedRange<UInt8>{
            let parallel = BlockOperation(block: {
                for b1 in 0...255 as ClosedRange<UInt8>{
                    for b2 in 0...255 as ClosedRange<UInt8>{
                        for b3 in 0...255 as ClosedRange<UInt8>{
                            let data = [b0,b1,b2,b3]
                            if let _ = PearsonHash.isHash(input: data, expectedHash: expected) {
                                result.append(data)
                            }
                        }
                    }
                }
            })
            operations2.addOperation(parallel)
        }
        operations2.waitUntilAllOperationsAreFinished()
        return result
    }
    
    private func case5 (hash expected : UInt8) -> [[UInt8]]{
        var result : [[UInt8]] = []

        let operations = OperationQueue()

        for b0 in 0...255 as ClosedRange<UInt8>{
            let parallel = BlockOperation(block: {
                for b1 in 0...255 as ClosedRange<UInt8>{
                    for b2 in 0...255 as ClosedRange<UInt8>{
                        for b3 in 0...255 as ClosedRange<UInt8>{
                            for b4 in 0...255 as ClosedRange<UInt8>{
                                let data = [b0,b1,b2,b3,b4]
                                if let _ = PearsonHash.isHash(input: data, expectedHash: expected) {
                                    //result.append(data)
                                }
                            }
                        }
                    }
                }
            })
            operations.addOperation(parallel)
        }
        operations.waitUntilAllOperationsAreFinished()
        
        return result
    }
}

Ah, concurrency! Yes, it is not safe to mutate the same variable from different threads / operation queues; in this case perhaps two operations try to update the array at the same time, realize they need to reallocate it, and free the old buffer without waiting for any sort of synchronization.

The simplest solution here is to use an NSLock; another would be to create more operations that get added to a serial queue rather than a parallel queue, to publish the results to the common array (operation queues are concurrency-safe to add to by default). I can’t think of an alternative that doesn’t require either custom operations or dipping down to UnsafePointers, or restructuring the code altogether.

1 Like