Skip to content

Usage of pointee on C++ std::optional<bool> causes a miscompile #82765

@egorzhdan

Description

@egorzhdan

Swift is silently miscompiling .pointee on a C++ std::optional value on Linux:

TestSuite.test("abc") {
  var optBoolT = StdOptionalBool(true)
  var optBoolF = StdOptionalBool(true)
  expectTrue(optBoolT.pointee) // passes
  expectTrue(optBoolF.pointee) // fails
}

Looking at SILGen, looks like we have a use-after-free in the implicitly generated getter for var pointee:

// std.optional<CBool>.pointee.getter
// Isolation: unspecified
sil shared [transparent] [serialized] [ossa] @$sSo3stdO0021optionalCBool_pgFEdJaV7pointeeSbvg : $@convention(method) (std.optional<CBool>) -> Bool {
// %0 "self"                                      // users: %3, %1
bb0(%0 : $std.optional<CBool>):
  debug_value %0, let, name "self", argno 1       // id: %1
  %2 = alloc_stack $std.optional<CBool>           // users: %6, %5, %3
  store %0 to [trivial] %2                        // id: %3
  // function_ref _ZNKOSt8optionalIbEdeEv
  %4 = function_ref @_ZNKOSt8optionalIbEdeEv : $@convention(cxx_method) (@in_guaranteed std.optional<CBool>) -> UnsafePointer<Bool> // user: %5
  %5 = apply %4(%2) : $@convention(cxx_method) (@in_guaranteed std.optional<CBool>) -> UnsafePointer<Bool> // users: %11, %8
  dealloc_stack %2                                // id: %6
  // function_ref UnsafePointer<>.pointee.unsafeAddressor
  %7 = function_ref @$sSPsRi_zrlE7pointeexvlu : $@convention(method) <τ_0_0 where τ_0_0 : ~Copyable> (UnsafePointer<τ_0_0>) -> UnsafePointer<τ_0_0> // user: %8
  %8 = apply %7<Bool>(%5) : $@convention(method) <τ_0_0 where τ_0_0 : ~Copyable> (UnsafePointer<τ_0_0>) -> UnsafePointer<τ_0_0> // user: %9
  %9 = struct_extract %8, #UnsafePointer._rawValue // user: %10
  %10 = pointer_to_address %9 to [strict] $*Bool  // user: %11

Notice that we're creating an implicit copy of the std::optional, getting a pointer out of it, and then deallocating the implicit copy (dealloc_stack %2) before retrieving the value out of the pointer (%9 = struct_extract %8).

I discovered this while working on #74146 but this issue isn't specific to that patch.

Metadata

Metadata

Assignees

Labels

c++ interopFeature: Interoperability with C++

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions