From e7ba16381cf978cd4a5131c673b6320c93f73b6b Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 4 Nov 2025 11:43:43 -0800 Subject: [PATCH 1/5] Make sure we don't compare too many bytes if a non-native string being compared to a native one has the same utf16 count but a different utf8 count --- stdlib/public/core/StringStorageBridge.swift | 7 ++++++- test/stdlib/StringBridge.swift | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/StringStorageBridge.swift b/stdlib/public/core/StringStorageBridge.swift index 8d9a6903f6738..850cd83d858b4 100644 --- a/stdlib/public/core/StringStorageBridge.swift +++ b/stdlib/public/core/StringStorageBridge.swift @@ -166,7 +166,12 @@ extension _AbstractStringStorage { // CFString will only give us ASCII bytes here, but that's fine. // We already handled non-ASCII UTF8 strings earlier since they're Swift. if let asciiEqual = unsafe withCocoaASCIIPointer(other, work: { (ascii) -> Bool in - return unsafe (start == ascii || (memcmp(start, ascii, self.count) == 0)) + // otherUTF16Length is the same as the byte count here IFF it's ASCII + // self.count could still be utf8 + if count != otherUTF16Length { + return 0 + } + return unsafe (start == ascii || (memcmp(start, ascii, otherUTF16Length) == 0)) }) { return asciiEqual ? 1 : 0 } diff --git a/test/stdlib/StringBridge.swift b/test/stdlib/StringBridge.swift index 6754af6bf116a..443a1d8e62531 100644 --- a/test/stdlib/StringBridge.swift +++ b/test/stdlib/StringBridge.swift @@ -195,4 +195,11 @@ StringBridgeTests.test("lengthOfBytes(using:)") { expectEqual(utf8AsMacRomanLen, 43) } +StringBridgeTests.test("Equal UTF16 lengths but unequal UTF8") { + let utf8 = "aaaaaaaaaaaaaaü" //Native, UTF16 count: 31, UTF8 count: 58 + let nsascii = "2166002315@874404110.1042078977" as NSString //Non-native, UTF16 count: 31, UTF8 count: 31 + let nsutf8 = String(decoding: utf8.utf8, as: UTF8.self) as NSString //bridged native + expectTrue(nsutf8.isEqual(nsascii)) +} + runAllTests() From 70f4d7e6d7cedba0f5dfcf0e405b1396198a3987 Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 4 Nov 2025 12:43:23 -0800 Subject: [PATCH 2/5] Review comment --- stdlib/public/core/StringStorageBridge.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/StringStorageBridge.swift b/stdlib/public/core/StringStorageBridge.swift index 850cd83d858b4..f13e536a7ed13 100644 --- a/stdlib/public/core/StringStorageBridge.swift +++ b/stdlib/public/core/StringStorageBridge.swift @@ -169,7 +169,7 @@ extension _AbstractStringStorage { // otherUTF16Length is the same as the byte count here IFF it's ASCII // self.count could still be utf8 if count != otherUTF16Length { - return 0 + return false } return unsafe (start == ascii || (memcmp(start, ascii, otherUTF16Length) == 0)) }) { From f03c1fe16b840a6b048e5b894c02a3b51eb9d395 Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 4 Nov 2025 12:43:42 -0800 Subject: [PATCH 3/5] Clarify an explanation --- stdlib/public/core/StringStorageBridge.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/StringStorageBridge.swift b/stdlib/public/core/StringStorageBridge.swift index f13e536a7ed13..857b1c79fd053 100644 --- a/stdlib/public/core/StringStorageBridge.swift +++ b/stdlib/public/core/StringStorageBridge.swift @@ -166,7 +166,7 @@ extension _AbstractStringStorage { // CFString will only give us ASCII bytes here, but that's fine. // We already handled non-ASCII UTF8 strings earlier since they're Swift. if let asciiEqual = unsafe withCocoaASCIIPointer(other, work: { (ascii) -> Bool in - // otherUTF16Length is the same as the byte count here IFF it's ASCII + // otherUTF16Length is the same as the byte count here since it's ASCII // self.count could still be utf8 if count != otherUTF16Length { return false From b4118a2b4012af673feb92a81b2bbda2b81972c2 Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 4 Nov 2025 13:00:28 -0800 Subject: [PATCH 4/5] Fix test oversight --- test/stdlib/StringBridge.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/stdlib/StringBridge.swift b/test/stdlib/StringBridge.swift index 443a1d8e62531..3705504e4e710 100644 --- a/test/stdlib/StringBridge.swift +++ b/test/stdlib/StringBridge.swift @@ -196,7 +196,7 @@ StringBridgeTests.test("lengthOfBytes(using:)") { } StringBridgeTests.test("Equal UTF16 lengths but unequal UTF8") { - let utf8 = "aaaaaaaaaaaaaaü" //Native, UTF16 count: 31, UTF8 count: 58 + let utf8 = "чебурашка@ящик-с-апельсинами.рф" //Native, UTF16 count: 31, UTF8 count: 58 let nsascii = "2166002315@874404110.1042078977" as NSString //Non-native, UTF16 count: 31, UTF8 count: 31 let nsutf8 = String(decoding: utf8.utf8, as: UTF8.self) as NSString //bridged native expectTrue(nsutf8.isEqual(nsascii)) From a23ed0dd7ab64e41ededf516bcd888560e4865d5 Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 4 Nov 2025 20:31:42 -0800 Subject: [PATCH 5/5] Most ridiculous possible test mistake --- test/stdlib/StringBridge.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/stdlib/StringBridge.swift b/test/stdlib/StringBridge.swift index 3705504e4e710..f3cf4a4a92168 100644 --- a/test/stdlib/StringBridge.swift +++ b/test/stdlib/StringBridge.swift @@ -199,7 +199,7 @@ StringBridgeTests.test("Equal UTF16 lengths but unequal UTF8") { let utf8 = "чебурашка@ящик-с-апельсинами.рф" //Native, UTF16 count: 31, UTF8 count: 58 let nsascii = "2166002315@874404110.1042078977" as NSString //Non-native, UTF16 count: 31, UTF8 count: 31 let nsutf8 = String(decoding: utf8.utf8, as: UTF8.self) as NSString //bridged native - expectTrue(nsutf8.isEqual(nsascii)) + expectFalse(nsutf8.isEqual(nsascii)) } runAllTests()