From e342646ee3ff42164af08335e8d60d29ef7c65fb Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Wed, 3 Dec 2025 08:32:29 -0500 Subject: [PATCH 1/2] Ensure strsep operates on a copy of PATH. (#5289) (#5296) strsep modifies its first argument, which means that if we don't make a copy, any PATH processing that may occur after _CFProcessPath executes breaks because strsep will effectively change PATH to be only the first list item. For example, swiftpm's tool finding fails because it searches PATH, and if the tool in question doesn't reside in the first PATH item, things break. This only occurs on BSD and only when the program name, i.e., argv[0], is not an absolute path. --- Sources/CoreFoundation/CFPlatform.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Sources/CoreFoundation/CFPlatform.c b/Sources/CoreFoundation/CFPlatform.c index 8112cec632..e460134e96 100644 --- a/Sources/CoreFoundation/CFPlatform.c +++ b/Sources/CoreFoundation/CFPlatform.c @@ -266,10 +266,12 @@ const char *_CFProcessPath(void) { } // Search PATH. - if (argv0) { - char *paths = getenv("PATH"); + char *path = getenv("PATH"); + if (argv0 && path) { + char *paths = strdup(path); + char *remaining = paths; char *p = NULL; - while ((p = strsep(&paths, ":")) != NULL) { + while ((p = strsep(&remaining, ":")) != NULL) { char pp[PATH_MAX]; int l = snprintf(pp, PATH_MAX, "%s/%s", p, argv0); if (l >= PATH_MAX) { @@ -283,11 +285,13 @@ const char *_CFProcessPath(void) { _CFSetProgramNameFromPath(res); free(argv0); free(res); + free(paths); return __CFProcessPath; } free(res); } free(argv0); + free(paths); } // See if the shell will help. From 979bd60f9ac89f562e74f518af24967e262ed533 Mon Sep 17 00:00:00 2001 From: Jeremy Schonfeld Date: Thu, 11 Dec 2025 21:41:52 -0500 Subject: [PATCH 2/2] Fix buffer overflow in _NSCFString.getBytes (#5287) (#5315) * Fix buffer overflow in _NSCFString.getBytes * Fix unit test failure * Fix test_dateParseAndFormatWithJapaneseCalendar --- Sources/Foundation/NSCFString.swift | 19 ++++++++---- .../TestPropertyListSerialization.swift | 31 +++++++++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/Sources/Foundation/NSCFString.swift b/Sources/Foundation/NSCFString.swift index 40ee579f42..fb93a5c39a 100644 --- a/Sources/Foundation/NSCFString.swift +++ b/Sources/Foundation/NSCFString.swift @@ -154,19 +154,25 @@ internal func _CFSwiftStringGetBytes(_ str: AnyObject, encoding: CFStringEncodin // TODO: Don't treat many encodings like they are UTF8 case CFStringEncoding(kCFStringEncodingUTF8), CFStringEncoding(kCFStringEncodingISOLatin1), CFStringEncoding(kCFStringEncodingMacRoman), CFStringEncoding(kCFStringEncodingASCII), CFStringEncoding(kCFStringEncodingNonLossyASCII): let encodingView = (str as! NSString).substring(with: NSRange(range)).utf8 - if let buffer = buffer { - for (idx, character) in encodingView.enumerated() { + var converted = 0 + for (idx, character) in encodingView.enumerated() { + if encoding == CFStringEncoding(kCFStringEncodingASCII) && !Unicode.ASCII.isASCII(character) { break } + if let buffer, maxBufLen > 0 { + if idx >= maxBufLen { break } buffer.advanced(by: idx).initialize(to: character) } + converted += 1 } - usedBufLen?.pointee = encodingView.count - convertedLength = encodingView.count + usedBufLen?.pointee = converted + convertedLength = converted case CFStringEncoding(kCFStringEncodingUTF16): let encodingView = (str as! NSString)._swiftObject.utf16 let start = encodingView.startIndex + var converted = 0 if let buffer = buffer { for idx in 0..= maxBufLen { break } // Since character is 2 bytes but the buffer is in term of 1 byte values, we have to split it up let character = encodingView[encodingView.index(start, offsetBy: idx + range.location)] #if _endian(big) @@ -178,11 +184,12 @@ internal func _CFSwiftStringGetBytes(_ str: AnyObject, encoding: CFStringEncodin #endif buffer.advanced(by: idx * 2).initialize(to: byte0) buffer.advanced(by: (idx * 2) + 1).initialize(to: byte1) + converted += 1 } } // Every character was 2 bytes - usedBufLen?.pointee = range.length * 2 - convertedLength = range.length + usedBufLen?.pointee = converted * 2 + convertedLength = converted default: fatalError("Attempted to get bytes of a Swift string using an unsupported encoding") diff --git a/Tests/Foundation/TestPropertyListSerialization.swift b/Tests/Foundation/TestPropertyListSerialization.swift index c01e0daf1c..d7ddb31fdc 100644 --- a/Tests/Foundation/TestPropertyListSerialization.swift +++ b/Tests/Foundation/TestPropertyListSerialization.swift @@ -7,6 +7,8 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +import CoreFoundation + class TestPropertyListSerialization : XCTestCase { func test_BasicConstruction() { let dict = NSMutableDictionary(capacity: 0) @@ -80,4 +82,33 @@ class TestPropertyListSerialization : XCTestCase { XCTAssertEqual(nserror.userInfo[NSDebugDescriptionErrorKey] as? String, "Cannot parse a NULL or zero-length data") } } + + func test_decodeOverflowUnicodeString() throws { + var native = "ФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФ" + let ns = native.withCString { + NSString(utf8String: $0)! + } + do { + var buffer = Array(repeating: 1, count: native.utf8.count) + buffer.withUnsafeMutableBufferPointer { bufferPtr in + var used: CFIndex = 0 + CFStringGetBytes(unsafeBitCast(ns, to: CFString.self), CFRangeMake(0, ns.length), CFStringBuiltInEncodings.UTF8.rawValue, 0xF, false, bufferPtr.baseAddress!, 10, &used) + XCTAssertEqual(used, 10) + } + for i in 10 ..< buffer.count { + XCTAssertEqual(buffer[i], 1) + } + } + do { + var buffer = Array(repeating: 1, count: native.utf8.count) + buffer.withUnsafeMutableBufferPointer { bufferPtr in + var used: CFIndex = 0 + CFStringGetBytes(unsafeBitCast(ns, to: CFString.self), CFRangeMake(0, ns.length), CFStringBuiltInEncodings.UTF16.rawValue, 0xF, false, bufferPtr.baseAddress!, 10, &used) + XCTAssertEqual(used, 10) + } + for i in 10 ..< buffer.count { + XCTAssertEqual(buffer[i], 1) + } + } + } }