From ada28386012f57f45504bbc5b30c7ab41e4f7162 Mon Sep 17 00:00:00 2001 From: Max Radermacher Date: Mon, 11 May 2026 17:07:50 -0500 Subject: [PATCH] Add mutex for CGDataProviderDirectCallbacks --- .../Util/CGDataProvider+SSK.swift | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/SignalServiceKit/Util/CGDataProvider+SSK.swift b/SignalServiceKit/Util/CGDataProvider+SSK.swift index 48e37d4726..8dd9f18504 100644 --- a/SignalServiceKit/Util/CGDataProvider+SSK.swift +++ b/SignalServiceKit/Util/CGDataProvider+SSK.swift @@ -20,10 +20,12 @@ extension FileHandle: CGDataProviderFileHandle { extension CGDataProvider { // Class-bound wrapper around a FileHandle private class FileHandleWrapper { - let fileHandle: any CGDataProviderFileHandle + // It may be possible for `getBytesAtPosition` to be invoked from multiple + // threads concurrently, so add a lock to ensure mutual exclusion. + let fileHandle: TSMutex init(_ fileHandle: any CGDataProviderFileHandle) { - self.fileHandle = fileHandle + self.fileHandle = TSMutex(initialState: fileHandle) } } @@ -37,17 +39,19 @@ extension CGDataProvider { return 0 } let fileHandle = Unmanaged.fromOpaque(info).takeUnretainedValue().fileHandle - do { - if offset != (try fileHandle.offset()) { - try fileHandle.seek(toOffset: UInt64(offset)) + return fileHandle.withLock { + do { + if offset != (try $0.offset()) { + try $0.seek(toOffset: UInt64(offset)) + } + let data = try $0.readNonOptional(upToCount: byteCount) + data.withUnsafeBytes { bytes in + buffer.copyMemory(from: bytes.baseAddress!, byteCount: bytes.count) + } + return data.count + } catch { + return 0 } - let data = try fileHandle.readNonOptional(upToCount: byteCount) - data.withUnsafeBytes { bytes in - buffer.copyMemory(from: bytes.baseAddress!, byteCount: bytes.count) - } - return data.count - } catch { - return 0 } }, releaseInfo: { info in