Swift 5.5 爆嚴(yán)重堆棧損壞 BUG
最近,開發(fā)者 taylorswift 在 Swift 論壇發(fā)表了題為 《Swift 5.5 has serious stack corruption bugs!》的帖子,作者發(fā)現(xiàn)了幾個(gè)與 async/await 相關(guān)的堆棧損壞 BUG,這些 BUG 可以在使用最新的 toolchain 進(jìn)行編譯時(shí)重現(xiàn),并且方法很簡單。作者目前確認(rèn)了 5.5-RELEASE 的 toolchain 中至少存在 4 個(gè)相關(guān) BUG。讓我們一起來看看這些問題。
使用異步閉包返回的值時(shí)堆棧損壞
異步閉包的返回值在混亂但確定性的內(nèi)存偏移處被破壞。以下代碼可以復(fù)現(xiàn)這個(gè)問題:
// async-return-value-corruption.swift
@main
enum Main
{
actor A
{
init()
{
}
func a(_ f:@Sendable () async -> (Int, (Int, Int, Int, Int))?)
async -> Void
{
guard let (head, tail):(Int, (Int, Int, Int, Int)) = await f()
else
{
return
}
print((head, tail))
return
}
}
static
func p() async -> Bool
{
true
}
static
func main() async
{
while true
{
let a:A = .init()
async let task:Void = a.a
{
if await Self.p()
{
return (0, (0, 0, 0 ,0))
}
else
{
return nil
}
}
await task
}
}
}
結(jié)果如下:
$ swiftc --version
Swift version 5.5 (swift-5.5-RELEASE)
Target: x86_64-unknown-linux-gnu
$ swiftc -parse-as-library -O async-return-value-corruption.swift
$ ./async-return-value-corruption
(139787763716080, (0, 0, 0, 0))
(139787629498352, (0, 0, 0, 0))
(139787965042672, (0, 0, 0, 0))
(139787763716080, (0, 0, 0, 0))
(139787629498352, (0, 0, 0, 0))
...
使用 async let 時(shí)出現(xiàn)分段錯(cuò)誤
相對簡單的 async 用法在調(diào)試和發(fā)布版本中都會(huì)遇到分段錯(cuò)誤。雖然作者最初認(rèn)為這僅限于在 main 函數(shù)中使用 async let ,但此后作者也在各種其他上下文中觀察到了這個(gè)問題。
// async-let-segfault.swift
@main
enum Main
{
static
func foo() async -> [Void]
{
try? await Task.sleep(nanoseconds: 1)
return []
}
static
func main() async
{
async let task:Void =
{
() async -> () in
try? await Task.sleep(nanoseconds: 1)
}()
while true
{
let _:[Void] = await Self.foo()
}
}
}
結(jié)果
$ swiftc --version
Swift version 5.5 (swift-5.5-RELEASE)
Target: x86_64-unknown-linux-gnu
$ swiftc -O -parse-as-library async-let-segfault.swift
async-let-segfault.swift:23:15: warning: will never be executed
await task
^
async-let-segfault.swift:19:15: note: condition always evaluates to true
while true
^
$ ./async-let-segfault
Segmentation fault (core dumped)
將枚舉傳遞給 actor-isolated 的方法時(shí)堆棧損壞
actor-isolated 方法接收到的枚舉值與調(diào)用者傳遞的值不同。作者在調(diào)試和發(fā)布版本中都觀察到了這個(gè)問題,但在調(diào)試版本中更為常見和可重現(xiàn)。所有最近的版本,包括 5.5-RELEASE、DEVELOPMENT-SNAPSHOT-2021-09-23-a,都會(huì)受到影響。
// 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))
}
}
}
結(jié)果
$ swiftc --version
Swift version 5.6-dev (LLVM ae102eaadf2d38c, Swift be2d00b32742678)
Target: x86_64-unknown-linux-gnu
$ 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)))
作者遇到了額外的內(nèi)存損壞錯(cuò)誤,包括在 actor-isolated 的屬性(線程 8)上調(diào)用實(shí)例方法時(shí)有些奇怪,但這 3 個(gè)是作者本周遇到的問題,并已將它們歸檔為:
SR-15225?https://bugs.swift.org/browse/SR-15225
SR-15241?https://bugs.swift.org/browse/SR-15241
SR-15240?https://bugs.swift.org/browse/SR-15240
在作者看來,錯(cuò)誤 (1) 是迄今為止最危險(xiǎn)的,因?yàn)樗悄l(fā)生的,并且會(huì)影響 5.5 版本的工具鏈。與 bug (3) 一樣,bug (1) 也代表了一個(gè)潛在的安全漏洞,盡管它可能不容易在自然發(fā)生的代碼中被利用。
作者發(fā)現(xiàn)錯(cuò)誤 (3) 確實(shí)出現(xiàn)在使用 5.5 版本工具鏈構(gòu)建的二進(jìn)制文件中,它們都根據(jù)微小的代碼更改變成段錯(cuò)誤,所以到目前為止只能作為段錯(cuò)誤重現(xiàn)的錯(cuò)誤 (2) 也可能表現(xiàn)為無形漏洞。
作者建議使用并發(fā)特性的開發(fā)人員不應(yīng)該發(fā)布任何用 5.5 工具鏈編譯的東西,直到這些問題得到修復(fù)并發(fā)布補(bǔ)丁。
SIMD 類型 BUG
看起來任務(wù)組和具有廣泛對齊的 SIMD 類型還有另一個(gè)問題。作者已經(jīng)確認(rèn)這個(gè)問題也會(huì)影響 5.5-RELEASE 工具鏈,能夠在調(diào)試和發(fā)布版本中重現(xiàn)它。
@main
enum Main
{
static
func main() async
{
await withTaskGroup(of: SIMD4<Int32>.self)
{
(group:inout TaskGroup) in
group.addTask
{
return SIMD4<Int32>.init(repeating: 0)
}
}
}
}
結(jié)果
$ swiftc --version
Swift version 5.5 (swift-5.5-RELEASE)
Target: x86_64-unknown-linux-gnu
$ swiftc -O -parse-as-library async-stack-corruption-simd.swift
$ ./async-stack-corruption-simd
Segmentation fault (core dumped)
目前這個(gè)帖子在論壇中引起了廣泛的關(guān)注。Swift 5.5 給開發(fā)者帶來了很多新特性,特別是在異步編程方面。但似乎 Swift 還有許多路要走,要想獲得更廣泛的應(yīng)用,必須確保語言自身的穩(wěn)定性,相信核心團(tuán)隊(duì)也意識到這些問題。
