New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-12675] Runtime crash with certain generic constraints #55119
Comments
Comment by Daniel Sweeney (JIRA) I broke this down a little, just to make sure I knew exactly where it was freaking out: let MaybeS = self as? KnownBidirectional.Type
let S = MaybeS!
//crash hard here, performAction uses generic type of a, a is SameLastElements<Proxy<[Int]>>
// s is KnownBidirectional.Type, call should be to static performAction
// where T is AttemptIfBidirectional
let r = S.performAction(a) Stepping in to the debugger in assembly at S.performAction(a) In the debugger it's going to: dis -s 0x1000013ac
static BidirectionalMarker.attempt(_:): I don't know if that's the entry point, the debugger seems to drop me there when I step in. I stepped through this instruction by instruction until the next branch. Theres a short run that puts an address together, puts it in r9, and jumps there. It blows up at the jump. The address in r9 is: (lldb) dis -s 0x00000001000018e0
sr12675`protocol witness for static KnownBidirectional.performAction<A>(_:) in conformance <> BidirectionalMarker<A>:
I'm not sure if that's the entry point. You get a segmentation fault if you run it outside the debugger. In the debugger it makes lldb freak out, which is interesting. I'd like to look at this further if that's OK. |
Comment by Daniel Sweeney (JIRA) Looking at this further, if you build with -sanitize=address,undefined and run you get: UndefinedBehaviorSanitizer:DEADLYSIGNAL
==83583==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address (pc 0x7fff69e6cdf9 bp 0x7ffee80b54a0 sp 0x7ffee80b5480 T11625043)
==83583==The signal is caused by a READ memory access.
==83583==Hint: this fault was caused by a dereference of a high value address (see registers below). Dissassemble the provided pc to learn which register value was used.
#​0 0x7fff69e6cdf8 in Array.count.getter (libswiftCore.dylib:x86_64+0x12df8)
#​1 0x7fff69e6f098 in protocol witness for Collection.endIndex.getter in conformance [A] (libswiftCore.dylib:x86_64+0x15098)
#​2 0x7fff69e67bce in Collection.isEmpty.getter (libswiftCore.dylib:x86_64+0xdbce)
#​3 0x7fff69e93535 in BidirectionalCollection.last.getter (libswiftCore.dylib:x86_64+0x39535)
#​4 0x107b4de7c in SameLastElements.action<A>(_:) (main:x86_64+0x100004e7c)
#​5 0x107b4e021 in protocol witness for AttemptIfBidirectional.action<A>(_:) in conformance SameLastElements<A> (main:x86_64+0x100005021)
#​6 0x107b4bcbd in static BidirectionalMarker<>.performAction<A>(_:) (main:x86_64+0x100002cbd)
#​7 0x107b4c43b in protocol witness for static KnownBidirectional.performAction<A>(_:) in conformance <> BidirectionalMarker<A> (main:x86_64+0x10000343b)
#​8 0x107b4abf2 in static BidirectionalMarker.attempt(_:) (main:x86_64+0x100001bf2)
#​9 0x107b4a416 in AttemptIfBidirectional.attemptAction() (main:x86_64+0x100001416)
#​10 0x107b52c5c in crasher() (main:x86_64+0x100009c5c)
#​11 0x107b52623 in main (main:x86_64+0x100009623)
#​12 0x7fff6a830cc8 in start (libdyld.dylib:x86_64+0x1acc8)
==83583==Register values:
rax = 0x0000000000000000 rbx = 0x00007fff83a818f8 rcx = 0x0000000000000200 rdx = 0x0000000000000000
rdi = 0x00007fff83a818f8 rsi = 0x00007fff83a818f8 rbp = 0x00007ffee80b54a0 rsp = 0x00007ffee80b5480
r8 = 0x00007fff6a1cdd54 r9 = 0x00000fffffffffff r10 = 0x0000000000000000 r11 = 0xffffffffffffffff
r12 = 0x00007ffee80b54e0 r13 = 0x00007ffee80b5740 r14 = 0xe000006060000003 r15 = 0xe000006060000003
UndefinedBehaviorSanitizer can not provide additional info.
==83583==ABORTING
zsh: abort ./main
(there are more frames there that I cut off.) If I have it right, r15 is the address, it's not mapped. I'm not sure what's populating it exactly. Without address sanitization the debugger loses track of something important and crashes and you never get the backtrace, which is a problem. I was thinking there might be stack corruption itself but I don't think that's happening (the stack and frame pointers look OK, etc.) Another hypothesis is that we're losing one of the levels of indirection in the runtime. But it looks from the above like it's going to Array.count.getter, which matches the type of the input arrays. The disassembly right around the fault is 0x7fff69e6cdeb <+59>: callq 0x7fff6a1450a0 ; _swift_isClassOrObjCExistentialType
0x7fff69e6cdf0 <+64>: andq %r14, %r15
0x7fff69e6cdf3 <+67>: testb $0x1, %al
0x7fff69e6cdf5 <+69>: cmoveq %r14, %r15
0x7fff69e6cdf9 <+73>: movq 0x10(%r15), %rax
where it calls isClassOrObjCExtistentialType, then does some things, then does a move off of r15 which fails, so possibly isClassOrObjCExtistentialType is not reporting back the truth for some reason. I have to think about how to test that. |
Comment by Daniel Sweeney (JIRA) I just compiled and ran this in the 05-04 Swift 5.3 and 05-05 Swift Development toolchains and still got the runtime crash. |
Comment by Daniel Sweeney (JIRA) Continuing to work on this. Some notes:
So the prolog code in .performAction believes that there's a pointer in one of the stack variables, but is wrong. Those are the symptoms but I'm still not sure what is the cause and where is the fix. Something is losing track of what or where it is. Examining the SIL and IR along with the assembly is not telling me much. There's nothing obviously missing. I'm reasoning through the pattern of abstraction in the code trying to see if something is going awry somewhere. Some speculations are that the call chain from
is losing track either because of the instance->static transition or protocol->extension transition. But that does not explain why the problem occurs with the generic constraints only. I'm missing something here. |
Comment by Daniel Sweeney (JIRA) This one is still crashing in Xcode 12.0 with the Xcode 12.0 toolchain. I am still working on this although it has been a while since I have had anything to log here. |
Environment
Swift 5.1.3
Xcode 11.3.1
MacOS 10.14.6
Additional Detail from JIRA
md5: 42fa052efdf3e8990b7d28bc1fee3b41
Issue Description:
When using a marker protocol to achieve dynamic dispatch (as described in the thread Test if a type conforms to a non-existential protocol on the Swift forums), if nontrivial constraints are place on the generic type, then the program will crash at runtime.
I'm sure this example could be made shorter, but it's what I have at the moment that demonstrates the crash. In particular, the constraint
P.Wrapped.Element: Equatable
is what causes the crash. Any non-trivial constraint there (ie. a constraint which is not also implied by the constraint onT
in theaction
method) causes a crash.Note that it is not necessary to actually use the
Equatable
conformance. We could make theaction
method simply returntrue
, without ever calling==
, and it would still crash.The point is, the constraint
T == Wrapped
is sufficient to let the program use the constraints onT
when handling values of typeWrapped
, but if there are additional constraints onWrapped
itself, the program crashes even though those constraints are only used on values of typeWrapped
(or even not used at all!)The text was updated successfully, but these errors were encountered: