mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Change the connection graph builder to map an existential address and its opened address onto the same node. Fixes <rdar://59559805> miscompile; use-after-free Immediate bug: EscapeAnalysis::mayReleaseContent incorrectly returns 'false' when comparing a store into an existential's value with a release of the existential. How this was exposed: mayReleaseContent was made more aggressive so that only the connection graph node representing the released object is considered to be "released". This fails for existentials because a separate connection graph node is created for the value within the existential. This is just one manifestation of a broader bug. Representing an existential as two logically distinct objects could also result in incorrect escaping information and incorrect alias analysis. Note that copying directly into an existential address and storing into the opened value in fact modify the same memory. Furthermore, when opening an existential, no local reference count is used to keep the opened value "alive" independent from the existential (this is another assumption made by mayReleaseContent). This is always how existentials were represented in the connection graph, but issues had never been exposed.
49 lines
2.4 KiB
Plaintext
49 lines
2.4 KiB
Plaintext
// RUN: %target-sil-opt -enable-sil-verify-all -release-hoisting %s | %FileCheck %s
|
|
// REQUIRES: CPU=x86_64
|
|
// REQUIRES: OS=macosx
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
import SwiftShims
|
|
|
|
// Test <rdar://59559805> miscompile; use-after-free
|
|
// Avoid hoisting strong_release of an existential
|
|
// over a store to the value.
|
|
protocol ArrayElementProtocol {}
|
|
|
|
struct ArrayElementStruct : ArrayElementProtocol {
|
|
@_hasStorage @_hasInitialValue var dummy: Int { get set }
|
|
}
|
|
|
|
// CHECK-LABEL: sil @testArrayStorageInitAfterFree : $@convention(thin) () -> () {
|
|
// CHECK: [[ARRAY:%.*]] = alloc_ref [stack] [tail_elems $ArrayElementProtocol * undef : $Builtin.Word] $_ContiguousArrayStorage<ArrayElementProtocol>
|
|
// CHECK: [[CAST:%.*]] = upcast [[ARRAY]] : $_ContiguousArrayStorage<ArrayElementProtocol> to $__ContiguousArrayStorageBase
|
|
// CHECK: [[TAIL:%.*]] = ref_tail_addr [[CAST]] : $__ContiguousArrayStorageBase, $ArrayElementProtocol
|
|
// CHECK: [[ADR:%.*]] = init_existential_addr [[TAIL]] : $*ArrayElementProtocol, $ArrayElementStruct
|
|
// CHECK: store %{{.*}} to [[ADR]] : $*ArrayElementStruct
|
|
// CHECK: strong_release [[ARRAY]] : $_ContiguousArrayStorage<ArrayElementProtocol>
|
|
// CHECK-LABEL: } // end sil function 'testArrayStorageInitAfterFree'
|
|
sil @testArrayStorageInitAfterFree : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_ref [stack] [tail_elems $ArrayElementProtocol * undef : $Builtin.Word] $_ContiguousArrayStorage<ArrayElementProtocol>
|
|
%1 = upcast %0 : $_ContiguousArrayStorage<ArrayElementProtocol> to $__ContiguousArrayStorageBase
|
|
%2 = struct $_SwiftArrayBodyStorage (undef : $Int, undef : $UInt)
|
|
%3 = struct $_ArrayBody (%2 : $_SwiftArrayBodyStorage)
|
|
%4 = ref_element_addr %1 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity
|
|
store %3 to %4 : $*_ArrayBody
|
|
%6 = ref_tail_addr %1 : $__ContiguousArrayStorageBase, $ArrayElementProtocol
|
|
%7 = value_to_bridge_object undef : $Builtin.Int64
|
|
%8 = struct $_StringObject (undef : $UInt64, %7 : $Builtin.BridgeObject)
|
|
%9 = struct $_StringGuts (%8 : $_StringObject)
|
|
%10 = struct $String (%9 : $_StringGuts)
|
|
%11 = struct $ArrayElementStruct (undef : $Int)
|
|
%12 = init_existential_addr %6 : $*ArrayElementProtocol, $ArrayElementStruct
|
|
store %11 to %12 : $*ArrayElementStruct
|
|
strong_release %0 : $_ContiguousArrayStorage<ArrayElementProtocol>
|
|
dealloc_ref [stack] %0 : $_ContiguousArrayStorage<ArrayElementProtocol>
|
|
%16 = tuple ()
|
|
return %16 : $()
|
|
}
|