Skip to content
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-4529] Optimizer emits crashing code for this constructor #47106

Open
drewcrawford opened this issue Apr 7, 2017 · 4 comments
Open

[SR-4529] Optimizer emits crashing code for this constructor #47106

drewcrawford opened this issue Apr 7, 2017 · 4 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software optimized only Flag: An issue whose reproduction requires optimized compilation run-time crash Bug → crash: Swift code crashed during execution

Comments

@drewcrawford
Copy link
Contributor

Previous ID SR-4529
Radar None
Original Reporter @drewcrawford
Type Bug

Attachment: Download

Environment

Xcode Version 8.3.1 (8E1000a)
Apple Swift version 3.1 (swiftlang-802.0.51 clang-802.0.41)

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, 3.1Regression, OptimizedOnly, RunTimeCrash
Assignee @gottesmm
Priority Medium

md5: ecd38b896e8a316a6476e34f0786b80c

Issue Description:

In the attached sample project, the Swift optimizer miscompiles an initializer, causing runtime memory UB. I believe this behavior is new in Swift 3.1.

1. Use release mode
2. Enable guard malloc
3. Run program

The relevant Swift:

enum MyErrors: Error {
    case foo
}

protocol PDD: class {
}
    
fileprivate final class DI: PDD {
    var o: TD? = nil
}

func c(d: PDD?, r: String) throws {
    if UUID().uuidString == "This function cannot throw but the optimizer cannot prove that" {
        throw MyErrors.foo
    }
    print("side effect")
}

final class TD {
    
    fileprivate let i: DI?
    
    init(d:Any?) throws {
        if let _ = d {
            i = DI()
        }
        else {
            i = nil
        }
        //print(i.debugDescription) //work around
        try c(d: i, r: "foo")
        i?.o = self
    }
}

In studying the disassembly, Swift seems to be using (retaining?) self.i after free (swift_unknownRelease seems to be free here in practice), and this use-after-free occurs in practically every path through the function. My notes in the margin, I'm new to the runtime memory internals so corrections appreciated:

0x10b5b0450 <+112>: callq  0x10b5b05d0               ; function signature specialization <Arg[0] = Dead, Arg[1] = Dead> of OptimizerBug.c (d : Swift.Optional<OptimizerBug.PDD>, r : Swift.String) throws -> () at Example.swift
    0x10b5b0455 <+117>: movq   (%r15), %r12
    0x10b5b0458 <+120>: testq  %r12, %r12                ;if r12 != 0 {
->  0x10b5b045b <+123>: je     0x10b5b0486               
    0x10b5b045d <+125>: movq   $0x0, (%r15)                   ;r15 = 0
    0x10b5b0464 <+132>: testq  %rbx, %rbx                     ;if rbx!=0 { //RBX == self.i
    0x10b5b0467 <+135>: je     0x10b5b0471               
    0x10b5b0469 <+137>: movq   %rbx, %rdi                         
    0x10b5b046c <+140>: callq  0x10b5b08e8                        ;swift_unknownRelease(rbx) //dealloc
    0x10b5b0471 <+145>: movq   %rbx, %rdi                      }
    0x10b5b0474 <+148>: callq  0x10b5afdc0                     ; swift_rt_swift_retain(rbx) //WAT
    0x10b5b0479 <+153>: movq   %r14, %rdi
    0x10b5b047c <+156>: callq  0x10b5aa770                     ; swift_rt_swift_release(r14)
    0x10b5b0481 <+161>: movq   %r12, (%r15)
    0x10b5b0484 <+164>: jmp    0x10b5b04c9                     ; return
    0x10b5b0486 <+166>: testq  %rbx, %rbx                 ;} if rbx!=0 { //TAKEN, rbx == self.i
    0x10b5b0489 <+169>: je     0x10b5b0493                   ;
    0x10b5b048b <+171>: movq   %rbx, %rdi
    0x10b5b048e <+174>: callq  0x10b5b08e8                   ; swift_unknownRelease(rbx) //dealloc
    0x10b5b0493 <+179>: movq   0x10(%r14), %rax              ; rax = *(r14+10) //rax = self.i, WAT
    0x10b5b0497 <+183>: testq  %rax, %rax                ;} if rax != 0 { //RAX == self.i
    0x10b5b049a <+186>: je     0x10b5b04be               
    0x10b5b049c <+188>: movq   0x10(%rax), %r15              ;r15 = *(rax-10)
    0x10b5b04a0 <+192>: movq   %r14, 0x10(%rax)              ;*(rax-10) = r14
    0x10b5b04a4 <+196>: movq   %r14, %rdi 
    0x10b5b04a7 <+199>: callq  0x10b5afdc0                   ; swift_rt_swift_retain(r14)
    0x10b5b04ac <+204>: movq   %rbx, %rdi
    0x10b5b04af <+207>: callq  0x10b5afdc0                   ; swift_rt_swift_retain(rbx) //WAT
    0x10b5b04b4 <+212>: movq   %r15, %rdi
    0x10b5b04b7 <+215>: callq  0x10b5aa770                   ; swift_rt_swift_release(r15)
    0x10b5b04bc <+220>: jmp    0x10b5b04c6                   ;return
    0x10b5b04be <+222>: movq   %rbx, %rdi                ;}
    0x10b5b04c1 <+225>: callq  0x10b5afdc0               
    0x10b5b04c6 <+230>: movq   %r14, %rax
    0x10b5b04c9 <+233>: popq   %rbx
    0x10b5b04ca <+234>: popq   %r12
    0x10b5b04cc <+236>: popq   %r14
    0x10b5b04ce <+238>: popq   %r15
    0x10b5b04d0 <+240>: popq   %rbp
    0x10b5b04d1 <+241>: retq

In the workaround disassembly, there are zero calls to unknownRelease, and the values passed to swift_rt_swift_release are treated dead as expected.

    0x10e9e1171 <+321>: callq  0x10e9e1580               ; function signature specialization <Arg[0] = Dead, Arg[1] = Dead> of OptimizerBug.c (d : Swift.Optional<OptimizerBug.PDD>, r : Swift.String) throws -> () at Example.swift
    0x10e9e1176 <+326>: movq   (%r15), %rbx
    0x10e9e1179 <+329>: testq  %rbx, %rbx
    0x10e9e117c <+332>: je     0x10e9e1193               ; <+355> at Example.swift:41
    0x10e9e117e <+334>: movq   $0x0, (%r15)
    0x10e9e1185 <+341>: movq   -0x38(%rbp), %rdi
    0x10e9e1189 <+345>: callq  0x10e9db4f0               ; swift_rt_swift_release
    0x10e9e118e <+350>: movq   %rbx, (%r15)
    0x10e9e1191 <+353>: jmp    0x10e9e11bb               ; <+395> at Example.swift:43
    0x10e9e1193 <+355>: movq   -0x38(%rbp), %rbx
    0x10e9e1197 <+359>: movq   0x10(%rbx), %rax
    0x10e9e119b <+363>: testq  %rax, %rax
    0x10e9e119e <+366>: je     0x10e9e11b8               ; <+392> at Example.swift:43
    0x10e9e11a0 <+368>: movq   0x10(%rax), %r14
    0x10e9e11a4 <+372>: movq   %rbx, 0x10(%rax)
    0x10e9e11a8 <+376>: movq   %rbx, %rdi
    0x10e9e11ab <+379>: callq  0x10e9e0a10               ; swift_rt_swift_retain
    0x10e9e11b0 <+384>: movq   %r14, %rdi
    0x10e9e11b3 <+387>: callq  0x10e9db4f0               ; swift_rt_swift_release
->  0x10e9e11b8 <+392>: movq   %rbx, %rax
    0x10e9e11bb <+395>: addq   $0x28, %rsp
    0x10e9e11bf <+399>: popq   %rbx
    0x10e9e11c0 <+400>: popq   %r12
    0x10e9e11c2 <+402>: popq   %r13
    0x10e9e11c4 <+404>: popq   %r14
    0x10e9e11c6 <+406>: popq   %r15
    0x10e9e11c8 <+408>: popq   %rbp
    0x10e9e11c9 <+409>: retq   
@drewcrawford
Copy link
Contributor Author

cc: @gparker42

@gparker42
Copy link
Mannequin

gparker42 mannequin commented Apr 11, 2017

Smells like a lifetime analysis problem in the compiler optimizer.

@gottesmm
Copy link
Member

Greg, just an FYI, I already have a top issue on my plate so this is going to take me a little bit to get to.

@drewcrawford
Copy link
Contributor Author

Still fresh on

Xcode Version 8.3.2 (8E2002)
Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42)

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added the crash Bug: A crash, i.e., an abnormal termination of software label Dec 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software optimized only Flag: An issue whose reproduction requires optimized compilation run-time crash Bug → crash: Swift code crashed during execution
Projects
None yet
Development

No branches or pull requests

3 participants