Skip to content

[SR-15240] async stack corruption when passing enums with associated values #57562

@tayloraswift

Description

@tayloraswift
Previous ID SR-15240
Radar rdar://problem/83635955
Original Reporter @Kelvin13
Type Bug
Status Resolved
Resolution Done
Environment

$ swiftc --version
Swift version 5.6-dev (LLVM a29f52d415422f3, Swift db90ea2)
Target: x86_64-unknown-linux-gnu

Additional Detail from JIRA
Votes 4
Component/s Compiler
Labels Bug, Concurrency
Assignee None
Priority Medium

md5: 87a891916f6d0d016927266c747ddfcd

Issue Description:

the following test program should give each user “guest” permissions, but gives them “admin” permissions instead!

// async-stack-corruption.swift 

struct Users
{
    enum Access 
    {
        case guest
        case admin(Int)
        case developer(Int, Int, Int, Int)
    }
    actor State  
    {
        init()
        {
        }
        func set(permissions:(user:Int, access:Access?)) 
        {
            print(permissions)
        }
    }
    
    let state:State = .init()
    
    func set(permissions:(user:Int, access:Access?)) async 
    {
        await self.state.set(permissions: permissions)
    }
}
@main 
enum Main 
{
    static 
    func main() async
    {
        let users:Users             = .init()
        let stream:AsyncStream<Int> = .init 
        {
            for i in 0 ..< 10
            {
                $0.yield(i) 
            }
            $0.finish()
        }
        for await i:Int in stream 
        {
            await users.set(permissions: (i, .guest))
        }
    }
}
$ swiftc -parse-as-library async-stack-corruption.swift 
$ ./async-stack-corruption 
(user: 0, access: Optional(main.Users.Access.admin(0)))
(user: 1, access: Optional(main.Users.Access.admin(0)))
(user: 2, access: Optional(main.Users.Access.admin(0)))
(user: 3, access: Optional(main.Users.Access.admin(0)))
(user: 4, access: Optional(main.Users.Access.admin(0)))
(user: 5, access: Optional(main.Users.Access.admin(0)))
(user: 6, access: Optional(main.Users.Access.admin(0)))
(user: 7, access: Optional(main.Users.Access.admin(0)))
(user: 8, access: Optional(main.Users.Access.admin(0)))
(user: 9, access: Optional(main.Users.Access.admin(0)))

Removing the associated value from the `admin` case will instead give them `developer` permissions!

The problem does not occur when the async stream is replaced with a normal `for` loop. Removing the user index causes the program to crash with a segmentation fault instead.

Fortunately, this issue is not present in the RELEASE-5.5 binary, only in the nightlies.

This issue is present in the 5.5-RELEASE toolchain. A modified reproduction is given below:

struct Users
{
    enum Access 
    {
        case guest
        case admin(Int)
        case developer(Int, Int, Int, Int)
    }
    
    private 
    actor User 
    {
        init()
        {
        }
        
        func set(permissions:(Int, Access?))
        {
            print(permissions)
        }
    }
    
    private 
    let users:[Int: User] = [0: .init()]
    
    func set(permissions:(Int, Access?)) async 
    {
        print(permissions)
        guard let user:User = self.users[permissions.0]
        else 
        {
            print(" \(permissions.0) ")
            return  
        }
        await user.set(permissions: permissions)
    }
}

@main 
enum Main 
{
    static 
    func main() async
    {
        let coordinator:Users = .init()
        let stream:AsyncStream<Int> = .init 
        {
            for i in 0 ..< 10
            {
                $0.yield(i) 
            }
            $0.finish()
        }

        for await i:Int in stream
        {
            if i != 0 
            {
                continue 
            }
            await coordinator.set(permissions: (i, .guest))
        }
    }
}

$ swiftc --version 
Swift version 5.5 (swift-5.5-RELEASE) 
Target: x86_64-unknown-linux-gnu 
$ swiftc -O -parse-as-library async-stack-corruption-5.5.swift 
$ ./async-stack-corruption-5.5 
(0, Optional(main.Users.Access.admin(144))) 
(0, Optional(main.Users.Access.admin(144)))

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.compilerThe Swift compiler itselfconcurrencyFeature: umbrella label for concurrency language features

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions