From fb3c710212bea5da22558cdc9222ac7bdfac77b0 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 30 Apr 2025 01:34:55 -0700 Subject: [PATCH] [concurrency] Fix an off by one error in generic handling in local capture checking code. Otherwise, we break memory safety by accessing a generic parameter that does not exist resulting in undefined behavior. rdar://150351351 --- lib/Sema/TypeCheckConcurrency.cpp | 2 +- test/Concurrency/metatype_crasher.swift | 65 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 test/Concurrency/metatype_crasher.swift diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 0fe0d7f771a..23022600bc7 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -3040,7 +3040,7 @@ namespace { // If the local function is generic and this is one of its generic // parameters, ignore it. if (genericSig.getNextDepth() > 0 && - genericDepth < genericSig.getNextDepth() - 1) + genericDepth >= genericSig.getNextDepth()) continue; if (type->isTypeParameter() && innermostGenericDC) diff --git a/test/Concurrency/metatype_crasher.swift b/test/Concurrency/metatype_crasher.swift new file mode 100644 index 00000000000..e78d256dfac --- /dev/null +++ b/test/Concurrency/metatype_crasher.swift @@ -0,0 +1,65 @@ +// RUN: %target-typecheck-verify-swift -swift-version 6 + +// REQUIRES: concurrency + +struct MyType : Sendable { + enum MyError: Error { + case error + } + + public init(_ h: T) { self.handler = h } + + public var handler: T + + public func handle( + forOperation operationID: String, + using handlerMethod: @Sendable @escaping (T) + -> ((OperationInput) async throws -> OperationOutput), + deserializer: @Sendable @escaping ( + ) throws -> OperationInput, + serializer: @Sendable @escaping (OperationOutput) throws + -> () + ) async throws -> () where OperationInput: Sendable, + OperationOutput: Sendable + { + @Sendable + func wrappingErrors( + work: () async throws -> R, + mapError: (any Error) -> any Error + ) async throws -> R { + fatalError() + } + @Sendable + func makeError( + input: OperationInput? = nil, + output: OperationOutput? = nil, + error: any Error + ) -> any Error { + fatalError() + } + var next: @Sendable () async throws // expected-warning {{}} + -> () = { + let input: OperationInput = try await wrappingErrors { + try deserializer() + } mapError: { error in + makeError(error: error) + } + let output: OperationOutput = try await wrappingErrors { + let method = handlerMethod(handler) + return try await wrappingErrors { + try await method(input) + } mapError: { error in + MyError.error + } + } mapError: { error in + makeError(input: input, error: error) + } + return try await wrappingErrors { + try serializer(output) + } mapError: { error in + makeError(input: input, output: output, error: error) + } + } + return try await next() + } +}