wickwirew / Runtime

A Swift Runtime library for viewing type info, and the dynamic getting and setting of properties.
MIT License
1.08k stars 94 forks source link

Runtime tests fall over when run with address sanitizer #87

Closed adam-fowler closed 3 years ago

adam-fowler commented 3 years ago

If you run

swift test --sanitize=address

You get the following error

Test Case '-[RuntimeTests.GetSetClassTests testGet]' started.
=================================================================
==68277==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffeeb309100 at pc 0x00010accef1f bp 0x7ffeeb3080f0 sp 0x7ffeeb3080e8
READ of size 8 at 0x7ffeeb309100 thread T0
    #0 0x10accef1e in closure #1 in closure #1 in withExistentialValuePointer<A, B>(of:_:) Pointers.swift:54
    #1 0x10accf1b1 in thunk for @callee_guaranteed (@unowned UnsafePointer<ExistentialContainer>) -> (@unowned ExistentialContainer, @error @owned Error) <compiler-generated>
    #2 0x10accf493 in partial apply for thunk for @callee_guaranteed (@unowned UnsafePointer<ExistentialContainer>) -> (@unowned ExistentialContainer, @error @owned Error) <compiler-generated>
    #3 0x7fff2ca94c9e in UnsafePointer.withMemoryRebound<A, B>(to:capacity:_:)+0xe (libswiftCore.dylib:x86_64+0x13ac9e)
    #4 0x10accdc85 in closure #1 in withExistentialValuePointer<A, B>(of:_:) Pointers.swift:54
    #5 0x10accecc1 in partial apply for closure #1 in withExistentialValuePointer<A, B>(of:_:) <compiler-generated>
    #6 0x7fff2ca8ec9b in withUnsafePointer<A, B>(to:_:)+0xb (libswiftCore.dylib:x86_64+0x134c9b)
    #7 0x10acccece in withExistentialValuePointer<A, B>(of:_:) Pointers.swift:53
    #8 0x10accbf6d in withValuePointer<A, B>(of:_:) Pointers.swift:35
    #9 0x10acc53ac in PropertyInfo.get(from:) PropertyInfo.swift:59
    #10 0x10acc46fe in PropertyInfo.get<A>(from:) PropertyInfo.swift:50
    #11 0x10acf03de in GetSetClassTests.testGet() GetSetClassTests.swift:63
    #12 0x10acf0b72 in @objc GetSetClassTests.testGet() <compiler-generated>
    #13 0x7fff2065856b in __invoking___+0x8b (CoreFoundation:x86_64h+0x6456b)
    #14 0x7fff2065840f in -[NSInvocation invoke]+0x12e (CoreFoundation:x86_64h+0x6440f)
    #15 0x105759bfc in __24-[XCTestCase invokeTest]_block_invoke_3+0x33 (XCTest:x86_64+0x30bfc)
    #16 0x10582d3e8 in +[XCTSwiftErrorObservation observeErrorsInBlock:]+0x44 (XCTest:x86_64+0x1043e8)
    #17 0x105759afe in __24-[XCTestCase invokeTest]_block_invoke_2+0x76 (XCTest:x86_64+0x30afe)
    #18 0x1057f0b75 in -[XCTMemoryChecker _assertInvalidObjectsDeallocatedAfterScope:]+0x40 (XCTest:x86_64+0xc7b75)
    #19 0x105764a3d in -[XCTestCase assertInvalidObjectsDeallocatedAfterScope:]+0x3c (XCTest:x86_64+0x3ba3d)
    #20 0x105759a44 in __24-[XCTestCase invokeTest]_block_invoke.262+0xc6 (XCTest:x86_64+0x30a44)
    #21 0x1057d15d7 in -[XCTestCase(XCTIssueHandling) _caughtUnhandledDeveloperExceptionPermittingControlFlowInterruptions:caughtInterruptionException:whileExecutingBlock:]+0xb2 (XCTest:x86_64+0xa85d7)
    #22 0x105759507 in -[XCTestCase invokeTest]+0x40c (XCTest:x86_64+0x30507)
    #23 0x10575b17d in __26-[XCTestCase performTest:]_block_invoke_2+0x2a (XCTest:x86_64+0x3217d)
    #24 0x1057d15d7 in -[XCTestCase(XCTIssueHandling) _caughtUnhandledDeveloperExceptionPermittingControlFlowInterruptions:caughtInterruptionException:whileExecutingBlock:]+0xb2 (XCTest:x86_64+0xa85d7)
    #25 0x10575b0b4 in __26-[XCTestCase performTest:]_block_invoke.393+0x55 (XCTest:x86_64+0x320b4)
    #26 0x1057e5462 in +[XCTContext runInContextForTestCase:markAsReportingBase:block:]+0xdb (XCTest:x86_64+0xbc462)
    #27 0x10575a8c1 in -[XCTestCase performTest:]+0x2bd (XCTest:x86_64+0x318c1)
    #28 0x1057ac0ae in -[XCTest runTest]+0x38 (XCTest:x86_64+0x830ae)
    #29 0x10575264c in -[XCTestSuite runTestBasedOnRerunPolicy:testRun:]+0x9a (XCTest:x86_64+0x2964c)
    #30 0x1057524c1 in __27-[XCTestSuite performTest:]_block_invoke+0xfc (XCTest:x86_64+0x294c1)
    #31 0x105751d15 in __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke+0x17 (XCTest:x86_64+0x28d15)
    #32 0x1057e5462 in +[XCTContext runInContextForTestCase:markAsReportingBase:block:]+0xdb (XCTest:x86_64+0xbc462)
    #33 0x105751ccc in -[XCTestSuite _performProtectedSectionForTest:testSection:]+0x9e (XCTest:x86_64+0x28ccc)
    #34 0x105751fd0 in -[XCTestSuite performTest:]+0x121 (XCTest:x86_64+0x28fd0)
    #35 0x1057ac0ae in -[XCTest runTest]+0x38 (XCTest:x86_64+0x830ae)
    #36 0x10575264c in -[XCTestSuite runTestBasedOnRerunPolicy:testRun:]+0x9a (XCTest:x86_64+0x2964c)
    #37 0x1057524c1 in __27-[XCTestSuite performTest:]_block_invoke+0xfc (XCTest:x86_64+0x294c1)
    #38 0x105751d15 in __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke+0x17 (XCTest:x86_64+0x28d15)
    #39 0x1057e5462 in +[XCTContext runInContextForTestCase:markAsReportingBase:block:]+0xdb (XCTest:x86_64+0xbc462)
    #40 0x105751ccc in -[XCTestSuite _performProtectedSectionForTest:testSection:]+0x9e (XCTest:x86_64+0x28ccc)
    #41 0x105751fd0 in -[XCTestSuite performTest:]+0x121 (XCTest:x86_64+0x28fd0)
    #42 0x1057ac0ae in -[XCTest runTest]+0x38 (XCTest:x86_64+0x830ae)
    #43 0x10575264c in -[XCTestSuite runTestBasedOnRerunPolicy:testRun:]+0x9a (XCTest:x86_64+0x2964c)
    #44 0x1057524c1 in __27-[XCTestSuite performTest:]_block_invoke+0xfc (XCTest:x86_64+0x294c1)
    #45 0x105751d15 in __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke+0x17 (XCTest:x86_64+0x28d15)
    #46 0x1057e5462 in +[XCTContext runInContextForTestCase:markAsReportingBase:block:]+0xdb (XCTest:x86_64+0xbc462)
    #47 0x105751ccc in -[XCTestSuite _performProtectedSectionForTest:testSection:]+0x9e (XCTest:x86_64+0x28ccc)
    #48 0x105751fd0 in -[XCTestSuite performTest:]+0x121 (XCTest:x86_64+0x28fd0)
    #49 0x1057ac0ae in -[XCTest runTest]+0x38 (XCTest:x86_64+0x830ae)
    #50 0x1058052ce in __44-[XCTTestRunSession runTestsAndReturnError:]_block_invoke_2+0x93 (XCTest:x86_64+0xdc2ce)
    #51 0x1057e5462 in +[XCTContext runInContextForTestCase:markAsReportingBase:block:]+0xdb (XCTest:x86_64+0xbc462)
    #52 0x105805233 in __44-[XCTTestRunSession runTestsAndReturnError:]_block_invoke+0x6e (XCTest:x86_64+0xdc233)
    #53 0x1058053b4 in __44-[XCTTestRunSession runTestsAndReturnError:]_block_invoke.87+0x5f (XCTest:x86_64+0xdc3b4)
    #54 0x10577b684 in -[XCTestObservationCenter _observeTestExecutionForBlock:]+0x144 (XCTest:x86_64+0x52684)
    #55 0x105804fab in -[XCTTestRunSession runTestsAndReturnError:]+0x1d8 (XCTest:x86_64+0xdbfab)
    #56 0x105736338 in -[XCTestDriver _runTests]+0x1d1 (XCTest:x86_64+0xd338)
    #57 0x1057e01b7 in _XCTestMain+0x6b (XCTest:x86_64+0xb71b7)
    #58 0x1048fa530 in main+0x194 (xctest:x86_64+0x100006530)
    #59 0x7fff20598620 in start+0x0 (libdyld.dylib:x86_64+0x15620)

Address 0x7ffeeb309100 is located in stack of thread T0 at offset 256 in frame
    #0 0x10acc4d0f in PropertyInfo.get(from:) PropertyInfo.swift:57

  This frame has 4 object(s):
    [32, 80) ''
    [112, 120) ''
    [144, 192) ''
    [224, 256) '' <== Memory access at offset 256 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow Pointers.swift:54 in closure #1 in closure #1 in withExistentialValuePointer<A, B>(of:_:)
Shadow bytes around the buggy address:
  0x1fffdd6611d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffdd6611e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffdd6611f0: 00 00 00 00 ca ca ca ca 00 00 00 cb cb cb cb cb
  0x1fffdd661200: f1 f1 f1 f1 00 00 00 00 00 00 f2 f2 f2 f2 00 f2
  0x1fffdd661210: f2 f2 00 00 00 00 00 00 f2 f2 f2 f2 00 00 00 00
=>0x1fffdd661220:[f3]f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffdd661230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffdd661240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffdd661250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffdd661260: 00 00 00 00 ca ca ca ca 00 00 cb cb cb cb cb cb
  0x1fffdd661270: ca ca ca ca 00 00 cb cb cb cb cb cb f1 f1 f1 f1
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==68277==ABORTING

Looks like this is because you are assuming you can use the pointee value outside of a withMemoryRebound. Although if I fix this the address sanitizer just fails elsewhere.

On Linux both thread and address sanitizers fail

adam-fowler commented 3 years ago

Here are fixed versions of the functions using pointee outside of the withMemoryRebound closure from Pointers.swift. I can add a PR for these if you want.

func withClassValuePointer<Value, Result>(
    of value: inout Value,
    _ body: (UnsafeMutableRawPointer) throws -> Result) throws -> Result {
    return try withUnsafePointer(to: &value) {
        return try $0.withMemoryRebound(to: UnsafeMutableRawPointer.self, capacity: 1) {
            try body($0.pointee)
        }
    }
}

func withExistentialValuePointer<Value, Result>(
    of value: inout Value,
    _ body: (UnsafeMutableRawPointer) throws -> Result) throws -> Result {
    return try withUnsafePointer(to: &value) { ptr in
        try ptr.withMemoryRebound(to: ExistentialContainer.self, capacity: 1) {
            let container = $0.pointee
            let info = try metadata(of: container.type)
            if info.kind == .class || info.size > ExistentialContainerBuffer.size() {
                return try ptr.withMemoryRebound(to: UnsafeMutableRawPointer.self, capacity: 1) {
                    let base = $0.pointee
                    if info.kind == .struct {
                        return try body(base.advanced(by: existentialHeaderSize))
                    } else {
                        return try body(base)
                    }
                }
            } else {
                return try body($0.mutable.raw)
            }
        }
    }
}
wickwirew commented 3 years ago

Ah good catch. Yea I originally started doing that in a couple places when reading the metadata, and those addresses would always be static so it wasn't too much of an issue. Obviously got to use to it and it should not be done here 😅. But Id be happy to merge a PR that fixes it!