transform (MakeGCStackSlots): do not move the stack chain pop earlier

Previously, the MakeGCStackSlots pass would attempt to pop the stack chain before a tail call.
This resulted in use-after-free bugs when the tail call allocated memory and used a value allocated by its caller.
Instead of trying to move the stack chain pop, remove the tail flag from the call.
This commit is contained in:
Nia Waldvogel
2022-06-03 12:41:36 -04:00
committed by Ayke
parent 906757603d
commit 2c93a4085c
3 changed files with 65 additions and 34 deletions
+18 -2
View File
@@ -5,6 +5,7 @@ target triple = "wasm32-unknown-unknown-wasm"
@runtime.stackChainStart = external global %runtime.stackChainObject*
@someGlobal = global i8 3
@ptrGlobal = global i8** null
declare void @runtime.trackPointer(i8* nocapture readonly)
@@ -20,8 +21,6 @@ define i8* @needsStackSlots() {
; so tracking it is not really necessary.
%ptr = call i8* @runtime.alloc(i32 4, i8* null)
call void @runtime.trackPointer(i8* %ptr)
; Restoring the stack pointer can happen at this position, before the return.
; This avoids issues with tail calls.
call void @someArbitraryFunction()
%val = load i8, i8* @someGlobal
ret i8* %ptr
@@ -103,3 +102,20 @@ define void @testGEPBitcast() {
define void @someArbitraryFunction() {
ret void
}
define void @earlyPopRegression() {
%x.alloc = call i8* @runtime.alloc(i32 4, i8* null)
call void @runtime.trackPointer(i8* %x.alloc)
%x = bitcast i8* %x.alloc to i8**
; At this point the pass used to pop the stack chain, resulting in a potential use-after-free during allocAndSave.
musttail call void @allocAndSave(i8** %x)
ret void
}
define void @allocAndSave(i8** %x) {
%y = call i8* @runtime.alloc(i32 4, i8* null)
call void @runtime.trackPointer(i8* %y)
store i8* %y, i8** %x
store i8** %x, i8*** @ptrGlobal
ret void
}
+37 -2
View File
@@ -5,6 +5,7 @@ target triple = "wasm32-unknown-unknown-wasm"
@runtime.stackChainStart = internal global %runtime.stackChainObject* null
@someGlobal = global i8 3
@ptrGlobal = global i8** null
declare void @runtime.trackPointer(i8* nocapture readonly)
@@ -25,9 +26,9 @@ define i8* @needsStackSlots() {
%ptr = call i8* @runtime.alloc(i32 4, i8* null)
%4 = getelementptr { %runtime.stackChainObject*, i32, i8* }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, i32 0, i32 2
store i8* %ptr, i8** %4, align 4
store %runtime.stackChainObject* %1, %runtime.stackChainObject** @runtime.stackChainStart, align 4
call void @someArbitraryFunction()
%val = load i8, i8* @someGlobal, align 1
store %runtime.stackChainObject* %1, %runtime.stackChainObject** @runtime.stackChainStart, align 4
ret i8* %ptr
}
@@ -75,8 +76,8 @@ define i8* @fibNext(i8* %x, i8* %y) {
%out.alloc = call i8* @runtime.alloc(i32 1, i8* null)
%4 = getelementptr { %runtime.stackChainObject*, i32, i8* }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, i32 0, i32 2
store i8* %out.alloc, i8** %4, align 4
store %runtime.stackChainObject* %1, %runtime.stackChainObject** @runtime.stackChainStart, align 4
store i8 %out.val, i8* %out.alloc, align 1
store %runtime.stackChainObject* %1, %runtime.stackChainObject** @runtime.stackChainStart, align 4
ret i8* %out.alloc
}
@@ -141,3 +142,37 @@ define void @testGEPBitcast() {
define void @someArbitraryFunction() {
ret void
}
define void @earlyPopRegression() {
%gc.stackobject = alloca { %runtime.stackChainObject*, i32, i8* }, align 8
store { %runtime.stackChainObject*, i32, i8* } { %runtime.stackChainObject* null, i32 1, i8* null }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, align 4
%1 = load %runtime.stackChainObject*, %runtime.stackChainObject** @runtime.stackChainStart, align 4
%2 = getelementptr { %runtime.stackChainObject*, i32, i8* }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, i32 0, i32 0
store %runtime.stackChainObject* %1, %runtime.stackChainObject** %2, align 4
%3 = bitcast { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject to %runtime.stackChainObject*
store %runtime.stackChainObject* %3, %runtime.stackChainObject** @runtime.stackChainStart, align 4
%x.alloc = call i8* @runtime.alloc(i32 4, i8* null)
%4 = getelementptr { %runtime.stackChainObject*, i32, i8* }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, i32 0, i32 2
store i8* %x.alloc, i8** %4, align 4
%x = bitcast i8* %x.alloc to i8**
call void @allocAndSave(i8** %x)
store %runtime.stackChainObject* %1, %runtime.stackChainObject** @runtime.stackChainStart, align 4
ret void
}
define void @allocAndSave(i8** %x) {
%gc.stackobject = alloca { %runtime.stackChainObject*, i32, i8* }, align 8
store { %runtime.stackChainObject*, i32, i8* } { %runtime.stackChainObject* null, i32 1, i8* null }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, align 4
%1 = load %runtime.stackChainObject*, %runtime.stackChainObject** @runtime.stackChainStart, align 4
%2 = getelementptr { %runtime.stackChainObject*, i32, i8* }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, i32 0, i32 0
store %runtime.stackChainObject* %1, %runtime.stackChainObject** %2, align 4
%3 = bitcast { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject to %runtime.stackChainObject*
store %runtime.stackChainObject* %3, %runtime.stackChainObject** @runtime.stackChainStart, align 4
%y = call i8* @runtime.alloc(i32 4, i8* null)
%4 = getelementptr { %runtime.stackChainObject*, i32, i8* }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, i32 0, i32 2
store i8* %y, i8** %4, align 4
store i8* %y, i8** %x, align 4
store i8** %x, i8*** @ptrGlobal, align 4
store %runtime.stackChainObject* %1, %runtime.stackChainObject** @runtime.stackChainStart, align 4
ret void
}