Skip to content

Commit 008699e

Browse files
committed
Fix bounds check in bridged ASCII String comparison
1 parent b6d0362 commit 008699e

File tree

3 files changed

+71
-6
lines changed

3 files changed

+71
-6
lines changed

stdlib/public/core/StringStorage.swift

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ internal protocol _AbstractStringStorage : _NSCopying {
2121
var count: Int { get }
2222
var isASCII: Bool { get }
2323
var start: UnsafePointer<UInt8> { get }
24-
var length: Int { get } // In UTF16 code units.
24+
var UTF16Length: Int { get }
2525
}
2626

2727
internal let _cocoaASCIIEncoding:UInt = 1 /* NSASCIIStringEncoding */
@@ -141,16 +141,26 @@ extension _AbstractStringStorage {
141141
}
142142
// At this point we've proven that it is an NSString of some sort, but not
143143
// one of ours.
144-
if length != _stdlib_binary_CFStringGetLength(other) {
145-
return 0
146-
}
144+
147145
defer { _fixLifetime(other) }
146+
147+
let otherUTF16Length = _stdlib_binary_CFStringGetLength(other)
148+
148149
// CFString will only give us ASCII bytes here, but that's fine.
149150
// We already handled non-ASCII UTF8 strings earlier since they're Swift.
150151
if let otherStart = _cocoaUTF8Pointer(other) {
152+
//We know that otherUTF16Length is also its byte count at this point
153+
if count != otherUTF16Length {
154+
return 0
155+
}
151156
return (start == otherStart ||
152157
(memcmp(start, otherStart, count) == 0)) ? 1 : 0
153158
}
159+
160+
if UTF16Length != otherUTF16Length {
161+
return 0
162+
}
163+
154164
/*
155165
The abstract implementation of -isEqualToString: falls back to -compare:
156166
immediately, so when we run out of fast options to try, do the same.
@@ -221,7 +231,7 @@ final internal class __StringStorage
221231
#if _runtime(_ObjC)
222232

223233
@objc(length)
224-
final internal var length: Int {
234+
final internal var UTF16Length: Int {
225235
@_effects(readonly) @inline(__always) get {
226236
return asString.utf16.count // UTF16View special-cases ASCII for us.
227237
}
@@ -701,7 +711,7 @@ final internal class __SharedStringStorage
701711
#if _runtime(_ObjC)
702712

703713
@objc(length)
704-
final internal var length: Int {
714+
final internal var UTF16Length: Int {
705715
@_effects(readonly) get {
706716
return asString.utf16.count // UTF16View special-cases ASCII for us.
707717
}

test/stdlib/Inputs/FoundationBridge/FoundationBridge.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,14 @@ BOOL identityOfData(NSData *data);
7575
- (BOOL)verifyKeysInRange:(NSRange)range existInDictionary:(NSDictionary *)dictionary;
7676
@end
7777

78+
#pragma mark - NSString bridging
79+
80+
static inline NSString *getNSStringEqualTestString() {
81+
return [NSString stringWithUTF8String:"[email protected]"];
82+
}
83+
84+
static inline BOOL NSStringBridgeTestEqual(NSString * _Nonnull a, NSString * _Nonnull b) {
85+
return [a isEqual:b];
86+
}
87+
7888
NS_ASSUME_NONNULL_END

test/stdlib/TestNSString.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
2+
// Licensed under Apache License v2.0 with Runtime Library Exception
3+
//
4+
// See https://swift.org/LICENSE.txt for license information
5+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// RUN: %empty-directory(%t)
10+
//
11+
// RUN: %target-clang %S/Inputs/FoundationBridge/FoundationBridge.m -c -o %t/FoundationBridgeObjC.o -g
12+
// RUN: %target-build-swift %s -I %S/Inputs/FoundationBridge/ -Xlinker %t/FoundationBridgeObjC.o -sanitize=address -o %t/TestNSString
13+
// RUN: %target-codesign %t/TestNSString
14+
15+
// RUN: %target-run %t/TestNSString > %t.txt
16+
// REQUIRES: executable_test
17+
// REQUIRES: asan_runtime
18+
// REQUIRES: objc_interop
19+
20+
import Foundation
21+
import FoundationBridgeObjC
22+
23+
#if FOUNDATION_XCTEST
24+
import XCTest
25+
class TestNSStringSuper : XCTestCase { }
26+
#else
27+
import StdlibUnittest
28+
class TestNSStringSuper { }
29+
#endif
30+
31+
class TestNSString : TestNSStringSuper {
32+
33+
func test_equalOverflow() {
34+
let cyrillic = "чебурашка@ящик-с-апельсинами.рф"
35+
let other = getNSStringEqualTestString()
36+
print(NSStringBridgeTestEqual(cyrillic, other))
37+
}
38+
39+
}
40+
41+
#if !FOUNDATION_XCTEST
42+
var NSStringTests = TestSuite("TestNSString")
43+
NSStringTests.test("test_equalOverflow") { TestNSString().test_equalOverflow() }
44+
runAllTests()
45+
#endif

0 commit comments

Comments
 (0)