Skip to content

Assertion `object->metadata->isClassObject()' destroying ErrorObject for !SWIFT_OBJC_INTEROP #81144

@rjmansfield

Description

@rjmansfield

Description

A de-initing ErrorObject with a weak reference and a side table will end up calling swift_unownedRelease which asserts on non-class objects.

https://github.com/swiftlang/swift/blob/main/stdlib/public/runtime/HeapObject.cpp#L977

  //
  // Note: This shortcut is NOT an optimization.
  // Some allocations passed to swift_deallocObject() are not compatible
  // with swift_unownedRelease() because they do not have ClassMetadata.

  if (object->refCounts.canBeFreedNow()) {
    // object state DEINITING -> DEAD
    swift_slowDealloc(object, allocatedSize, allocatedAlignMask);
  } else {
    // object state DEINITING -> DEINITED
    swift_unownedRelease(object);
  }

The ErrorObject has a side table and is on the doDecrementSlow slow path, object->refCounts.canBeFreedNow() will always return false:


  DEINITING with side table
  Weak variable load returns nil.
  Weak variable store stores nil.
  canBeFreedNow() is always false, so it never transitions directly to DEAD.
  Everything else is the same as DEINITING.

The control flow is examplePackageTests.xctestexample() -> swift_release -> doDecrementSlow -> _swift_release_dealloc -> _destroyErrorObject -> swift_deallocObject -> swift_deallocObjectImpl -> swift_unownedRelease

Backtrace at assert:

    frame #5: 0x00007ffff5e2b71b libc.so.6`__assert_fail_base(fmt="%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion="object->metadata->isClassObject()", file="/home/ryan_mansfield/swift/swift/stdlib/public/runtime/HeapObject.cpp", line=559, function="void swift::swift_unownedRelease(HeapObject *)") at assert.c:94:3
    frame #6: 0x00007ffff5e3ce96 libc.so.6`__GI___assert_fail(assertion="object->metadata->isClassObject()", file="/home/ryan_mansfield/swift/swift/stdlib/public/runtime/HeapObject.cpp", line=559, function="void swift::swift_unownedRelease(HeapObject *)") at assert.c:103:3
    frame #7: 0x00007ffff7c0c183 libswiftCore.so`swift_deallocObjectImpl(swift::HeapObject*, unsigned long, unsigned long, bool) [inlined] swift_unownedRelease(object=0x00007fffe4009050) at HeapObject.cpp:559:3
    frame #8: 0x00007ffff7c0c164 libswiftCore.so`swift_deallocObjectImpl(object=0x00007fffe4009050, allocatedSize=48, allocatedAlignMask=7, isDeiniting=<unavailable>) at HeapObject.cpp:986:5
    frame #9: 0x00007ffff7c0bbe0 libswiftCore.so`_swift_release_dealloc(object=0x00007fffe4009050) at HeapObject.cpp:759:3
  * frame #10: 0x00007ffff7c0c9c3 libswiftCore.so`bool swift::RefCounts<swift::SideTableRefCountBits>::doDecrementSlow<(swift::PerformDeinit)1>(this=<unavailable>, oldbits=<unavailable>, dec=<unavailable>) at RefCount.h:1052:7
    frame #11: 0x0000555555588578 examplePackageTests.xctest`example() at exampleTests.swift:24:1
    frame #12: 0x0000555555587841 examplePackageTests.xctest`$s12exampleTests0A04TestfMp_7__localfMu_ #1 @Sendable (_0=nil) in $s12exampleTests0A04TestfMp_16Z4872561636a65b3fMu_@Sendable () at @__swiftmacro_12exampleTests0A04TestfMp_.swift:4:100
    frame #13: 0x000055555558766c examplePackageTests.xctest`$s12exampleTests0A04TestfMp_16Z4872561636a65b3fMu_@Sendable () at @__swiftmacro_12exampleTests0A04TestfMp_.swift:6:13
    frame #14: 0x00007ffff62adb0c libTesting.so`closure #1 in closure #1 in closure #1 in static Runner._runTestCase(_:within:) [inlined] closure #1 @Sendable (testCase=<unavailable>) async throws -> () in closure #1 @Sendable () async throws -> () in closure #1 () async throws -> () in closure #1 () async -> () in static Testing.Runner._runTestCase(_: Testing.Test.Case, within: Testing.Runner.Plan.Step) async throws -> () at Runner.swift:373:32 [opt]
    frame #15: 0x00007ffff62adb0c libTesting.so`closure #1 in closure #1 in closure #1 in static Runner._runTestCase(_:within:) [inlined] static Testing.Runner._applyScopingTraits(test=<unavailable>, testCase=<unavailable>, body=<unavailable>) async throws -> ()) async throws -> () at Runner.swift:91:24 [opt]
    frame #16: 0x00007ffff62adb0c libTesting.so`closure #1 in closure #1 in closure #1 in static Runner._runTestCase(, step=<unavailable>, testCase=Testing.Test.Case @ 0x0000507000178a40) at Runner.swift:372:21 [opt]

(lldb) p object->metadata->dump()
TargetMetadata.
Kind: ErrorObject.
Value Witnesses: 0x7ffff7df7640.
(lldb) p object->dump()
HeapObject: 0x7fffe4009050
HeapMetadata Pointer: 0x7ffff7df7098.
Strong Ref Count: 1.
Unowned Ref Count: 1.
Weak Ref Count: 2.
Uses Native Retain: Not a class. N/A.
RefCount Side Table: 0x7fffe8002fe0.
Is Deiniting: true.

Reproduction

This can be observed by throwing an ErrorObject from a swift test

import Testing

@testable import example

public struct myStringError: Equatable, Error {

  public let description: String

  public init(_ description: String) {
    self.description = description
  }
}

func functhrows() throws {
  throw myStringError("error")
}

@Test func example() {
  do {
    #expect(throws: (any Error).self) {
      try functhrows()
    }
  }
}

$ swift test
Building for debugging...
[1/1] Write swift-version-7CABD121BF667B0B.txt
Build complete! (0.14s)
Test Suite 'All tests' started at 2025-04-28 13:49:19.454
Test Suite 'debug.xctest' started at 2025-04-28 13:49:19.456
Test Suite 'debug.xctest' passed at 2025-04-28 13:49:19.456
	 Executed 0 tests, with 0 failures (0 unexpected) in 0.0 (0.0) seconds
Test Suite 'All tests' passed at 2025-04-28 13:49:19.456
	 Executed 0 tests, with 0 failures (0 unexpected) in 0.0 (0.0) seconds
◇ Test run started.
↳ Testing Library Version: 6.3-dev (6023a5fb9a6ff4f)
↳ Target Platform: x86_64-unknown-linux-gnu
◇ Test example() started.
examplePackageTests.xctest: /home/ryan_mansfield/swift/swift/stdlib/public/runtime/HeapObject.cpp:559: void swift::swift_unownedRelease(HeapObject *): Assertion `object->metadata->isClassObject()' failed.

*** Signal 6: Backtracing from 0x7ff941a9b9fc... done ***

*** Program crashed: Aborted at 0x3a07e1530000a66b ***

Platform: x86_64 Linux (Ubuntu 22.04 LTS)

Thread 0 "examplePackageT":

  0  0x00007ff941a4780a sigsuspend + 74 in libc.so.6
...

Thread 1:

  0  0x00007ff941a96117 __futex_abstimed_wait_cancelable64 + 231 in libc.so.6
...

Thread 2 crashed:

  0  0x00007ff941a9b9fc pthread_kill@@GLIBC_2.34 + 300 in libc.so.6
...

Thread 3:

  0  0x00007ff941b2ae2e epoll_wait + 94 in libc.so.6
...

Thread 4:

  0  0x00007ff941a96117 __futex_abstimed_wait_cancelable64 + 231 in libc.so.6
...

Thread 5:

  0  0x00007ff941a96117 __futex_abstimed_wait_cancelable64 + 231 in libc.so.6
...


Registers:

rax 0x0000000000000000  0
rdx 0x00007ff93ea7c640  40 c6 a7 3e f9 7f 00 00 40 16 00 38 f9 7f 00 00  @Ƨ>ù···@··8ù···
rcx 0x00007ff941a9b9fc  41 89 c5 41 f7 dd 3d 00 f0 ff ff b8 00 00 00 00  A·ÅA÷Ý=·ðÿÿ¸····
rbx 0x0000000000000006  6
rsi 0x000000000000a66b  42603
rdi 0x000000000000a66f  42607
rbp 0x000000000000a66f  42607
rsp 0x00007ff93ea7b710  dc 9e 01 30 f9 7f 00 00 b0 9d 01 30 f9 7f 00 00  Ü··0ù···°··0ù···
 r8 0x00007ff93ea7b7e0  20 00 00 00 00 00 00 00 a0 06 c2 41 f9 7f 00 00   ······· ·ÂAù···
 r9 0x0000000000000000  0
r10 0x0000000000000008  8
r11 0x0000000000000246  582
r12 0x0000000000000006  6
r13 0x0000000000000016  22
r14 0x00007ff943a29344  6f 62 6a 65 63 74 2d 3e 6d 65 74 61 64 61 74 61  object->metadata
r15 0x0000000000000007  7
rip 0x00007ff941a9b9fc  41 89 c5 41 f7 dd 3d 00 f0 ff ff b8 00 00 00 00  A·ÅA÷Ý=·ðÿÿ¸····

rflags 0x0000000000000246  ZF PF

cs 0x0033  fs 0x0000  gs 0x0000


Images (22 omitted):

0x00007ff941a05000–0x00007ff941bc1341 cd410b710f0f094c6832edd95931006d883af48e libc.so.6 /usr/lib/x86_64-linux-gnu/libc.so.6

Backtrace took 0.01s

Expected behavior

No assertion. The test does not assert on macOS.

Environment

Happens on

$ swiftc -v
Swift version 6.2-dev (LLVM 0dc2e2691f4f2cd, Swift 5d36643)
Target: x86_64-unknown-linux-gnu

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.triage neededThis issue needs more specific labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions