Skip to content

KeyPath: Fix out-of-bounds access when instantiating keypaths with optional chaining components. #12725

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions stdlib/public/core/KeyPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2199,13 +2199,14 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern(
// Scan the pattern to figure out the dynamic capability of the key path.
// Start off assuming the key path is writable.
var capability: KeyPathKind = .value
var didChain = false

let bufferPtr = pattern.advanced(by: keyPathObjectHeaderSize)
var buffer = KeyPathBuffer(base: bufferPtr)
var size = buffer.data.count + MemoryLayout<Int>.size
var alignmentMask = MemoryLayout<Int>.alignment - 1

scanComponents: while true {
while true {
let header = buffer.pop(RawKeyPathComponent.Header.self)

func popOffset() {
Expand Down Expand Up @@ -2290,8 +2291,8 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern(
case .optionalChain,
.optionalWrap:
// Chaining always renders the whole key path read-only.
capability = .readOnly
break scanComponents
didChain = true
break

case .optionalForce:
// No effect.
Expand All @@ -2306,6 +2307,11 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern(
alignment: MemoryLayout<Int>.alignment)
}

// Chaining always renders the whole key path read-only.
if didChain {
capability = .readOnly
}

// Grab the class object for the key path type we'll end up with.
func openRoot<Root>(_: Root.Type) -> AnyKeyPath.Type {
func openLeaf<Leaf>(_: Leaf.Type) -> AnyKeyPath.Type {
Expand Down
20 changes: 20 additions & 0 deletions test/stdlib/KeyPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -666,5 +666,25 @@ keyPath.test("subscripts") {
expectEqual(base[keyPath: ints_be], (17 + 38).bigEndian)
}

// SR-6096

protocol Protocol6096 {}
struct Value6096<ValueType> {}
extension Protocol6096 {
var asString: String? {
return self as? String
}
}
extension Value6096 where ValueType: Protocol6096 {
func doSomething() {
_ = \ValueType.asString?.endIndex
}
}
extension Int: Protocol6096 {}

keyPath.test("optional chaining component that needs generic instantiation") {
Value6096<Int>().doSomething()
}

runAllTests()