Fixed crashing this code on windows due uncatched longjmp from lua_error function

This commit is contained in:
Dmitry Ng
2021-09-25 21:12:47 +03:00
parent 520dea769d
commit b6d84181f4
8 changed files with 204 additions and 83 deletions
-4
View File
@@ -30,7 +30,6 @@ func AllocatorF(ptr unsafe.Pointer, osize uint, nsize uint) unsafe.Pointer {
ptr = unsafe.Pointer(&(slice[0]))
refHolder[ptr] = slice
}
//fmt.Println("in allocf");
return ptr
}
@@ -40,9 +39,6 @@ func A2(ptr unsafe.Pointer, osize uint, nsize uint) unsafe.Pointer {
}
func main() {
//refHolder = make([][]byte,0,500);
L := lua.NewStateAlloc(AllocatorF)
defer L.Close()
L.OpenLibs()
+1 -1
View File
@@ -29,7 +29,7 @@ func main() {
//force a panic
test := func(L1 *lua.State) int {
L1.RaiseError("panic check")
L1.LuaError("panic check")
return 0
}
L.PushGoFunction(test)
+1
View File
@@ -16,6 +16,7 @@ func main() {
L.GetGlobal("print")
L.PushString("Hello World!")
L.CheckType(2, lua.LUA_TSTRING)
L.Call(1, 0)
L.Register("adder", adder)
+46 -48
View File
@@ -9,8 +9,6 @@
#define MT_GOFUNCTION "GoLua.GoFunction"
#define MT_GOINTERFACE "GoLua.GoInterface"
#define GOLUA_DEFAULT_MSGHANDLER "golua_default_msghandler"
// golua registry key, main states only, non non-main coroutines.
// The address of this constant is used as a unique
// lightuserdata key.
@@ -43,7 +41,7 @@ long long int wrapAtoll(const char *nptr)
}
/* taken from lua5.2 source */
void *testudata(lua_State *L, int ud, const char *tname)
void *clua_testudata(lua_State *L, int ud, const char *tname)
{
void *p = lua_touserdata(L, ud);
if (p != NULL)
@@ -62,25 +60,25 @@ void *testudata(lua_State *L, int ud, const char *tname)
int clua_isgofunction(lua_State *L, int n)
{
return testudata(L, n, MT_GOFUNCTION) != NULL;
return clua_testudata(L, n, MT_GOFUNCTION) != NULL;
}
int clua_isgostruct(lua_State *L, int n)
{
return testudata(L, n, MT_GOINTERFACE) != NULL;
return clua_testudata(L, n, MT_GOINTERFACE) != NULL;
}
unsigned int* clua_checkgosomething(lua_State* L, int index, const char *desired_metatable)
{
if (desired_metatable != NULL)
{
return testudata(L, index, desired_metatable);
return clua_testudata(L, index, desired_metatable);
}
else
{
unsigned int *sid = testudata(L, index, MT_GOFUNCTION);
unsigned int *sid = clua_testudata(L, index, MT_GOFUNCTION);
if (sid != NULL) return sid;
return testudata(L, index, MT_GOINTERFACE);
return clua_testudata(L, index, MT_GOINTERFACE);
}
}
@@ -234,6 +232,46 @@ int panic_msghandler(lua_State *L)
return 1;
}
int callback_panicf(lua_State* L)
{
lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
lua_gettable(L, LUA_REGISTRYINDEX);
unsigned int fid = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_State* main_thread = clua_get_main_thread(L);
size_t main_index = clua_getgostate(main_thread);
return golua_callpanicfunction(L, main_index, fid);
}
//TODO: currently setting garbage when panicf set to null
GoValue clua_atpanic(lua_State* L, unsigned int panicf_id)
{
//get old panicfid
unsigned int old_id;
lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
lua_gettable(L, LUA_REGISTRYINDEX);
if(lua_isnil(L, -1) == 0)
old_id = lua_tointeger(L, -1);
lua_pop(L, 1);
//set registry key for function id of go panic function
lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
//push id value
lua_pushinteger(L, panicf_id);
//set into registry table
lua_settable(L, LUA_REGISTRYINDEX);
//now set the panic function
lua_CFunction pf = lua_atpanic(L, &callback_panicf);
//make a GoInterface with a wrapped C panicf or the original go panicf
if(pf == &callback_panicf)
return (GoValue){1, &old_id};
else
return (GoValue){2, pf};
}
void create_uniq_array(lua_State* L) {
// stack: ...
@@ -610,46 +648,6 @@ void clua_initstate(lua_State* L)
lua_pop(L, 1);
}
int callback_panicf(lua_State* L)
{
lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
lua_gettable(L, LUA_REGISTRYINDEX);
unsigned int fid = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_State* main_thread = clua_get_main_thread(L);
size_t main_index = clua_getgostate(main_thread);
return golua_callpanicfunction(L, main_index, fid);
}
//TODO: currently setting garbage when panicf set to null
GoValue clua_atpanic(lua_State* L, unsigned int panicf_id)
{
//get old panicfid
unsigned int old_id;
lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
lua_gettable(L, LUA_REGISTRYINDEX);
if(lua_isnil(L, -1) == 0)
old_id = lua_tointeger(L, -1);
lua_pop(L, 1);
//set registry key for function id of go panic function
lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
//push id value
lua_pushinteger(L, panicf_id);
//set into registry table
lua_settable(L, LUA_REGISTRYINDEX);
//now set the panic function
lua_CFunction pf = lua_atpanic(L, &callback_panicf);
//make a GoInterface with a wrapped C panicf or the original go panicf
if(pf == &callback_panicf)
return (GoValue){1, &old_id};
else
return (GoValue){2, pf};
}
int clua_callluacfunc(lua_State* L, lua_CFunction f)
{
return f(L);
+1
View File
@@ -49,6 +49,7 @@ void clua_openos(lua_State* L);
uint32_t clua_luajit_ctypeid(lua_State *L, int idx);
void clua_luajit_push_cdata_int64(lua_State *L, int64_t n);
void clua_luajit_push_cdata_uint64(lua_State *L, uint64_t u);
void *clua_testudata(lua_State *L, int ud, const char *tname);
int clua_isgofunction(lua_State *L, int n);
int clua_isgostruct(lua_State *L, int n);
+50 -19
View File
@@ -9,6 +9,7 @@ package lua
import "C"
import (
"fmt"
"os"
"unsafe"
)
@@ -39,11 +40,7 @@ func XMove(from *State, to *State, n int) {
func (L *State) CheckStackArg(narg int) {
if int(C.lua_gettop(L.s)) < abs(narg) {
format := "stack dosen't contains element"
CFormat := C.CString(format)
defer C.free(unsafe.Pointer(CFormat))
C.lua_pushlstring(L.s, CFormat, C.size_t(len(format)))
C.lua_error(L.s)
L.RaiseError("stack dosen't contains element")
}
}
@@ -59,7 +56,7 @@ func (L *State) Argcheck(cond bool, narg int, extramsg string) {
}
}
// luaL_argerror
// luaL_argerror is dangerous on windows system
func (L *State) ArgError(narg int, extramsg string) int {
Cextramsg := C.CString(extramsg)
defer C.free(unsafe.Pointer(Cextramsg))
@@ -68,6 +65,14 @@ func (L *State) ArgError(narg int, extramsg string) int {
return int(C.luaL_argerror(L.s, C.int(narg), Cextramsg))
}
// luaL_argerror is dangerous on windows system
func (L *State) LuaError(msg string) int {
defer L.r.Unlock()
L.r.Lock()
L.PushString(msg)
return int(C.lua_error(L.s))
}
// luaL_callmeta
func (L *State) CallMeta(obj int, e string) int {
Ce := C.CString(e)
@@ -77,37 +82,44 @@ func (L *State) CallMeta(obj int, e string) int {
return int(C.luaL_callmeta(L.s, C.int(obj), Ce))
}
// luaL_checkany
// luaL_checkany isn't work on windows due lua_error call
func (L *State) CheckAny(narg int) {
defer L.r.Unlock()
L.r.Lock()
L.CheckStackArg(narg)
C.luaL_checkany(L.s, C.int(narg))
}
// luaL_checkinteger
// luaL_checkinteger isn't work on windows due lua_error call
func (L *State) CheckInteger(narg int) int {
defer L.r.Unlock()
L.r.Lock()
L.CheckStackArg(narg)
return int(C.luaL_checkinteger(L.s, C.int(narg)))
if !L.IsNumber(narg) {
L.raiseArgumentError(narg, LUA_TNUMBER)
}
return L.ToInteger(narg)
}
// luaL_checknumber
// luaL_checknumber isn't work on windows due lua_error call
func (L *State) CheckNumber(narg int) float64 {
defer L.r.Unlock()
L.r.Lock()
L.CheckStackArg(narg)
return float64(C.luaL_checknumber(L.s, C.int(narg)))
if !L.IsNumber(narg) {
L.raiseArgumentError(narg, LUA_TNUMBER)
}
return L.ToNumber(narg)
}
// luaL_checkstring
// luaL_checkstring isn't work on windows due lua_error call
func (L *State) CheckString(narg int) string {
var length C.size_t
defer L.r.Unlock()
L.r.Lock()
L.CheckStackArg(narg)
return C.GoString(C.luaL_checklstring(L.s, C.int(narg), &length))
if !L.IsString(narg) {
L.raiseArgumentError(narg, LUA_TSTRING)
}
return L.ToString(narg)
}
// luaL_checkoption
@@ -118,20 +130,28 @@ func (L *State) CheckOption(narg int, def string, lst []string) int {
return 0
}
// luaL_checktype
// luaL_checktype isn't work on windows due lua_error call
func (L *State) CheckType(narg int, t LuaValType) {
defer L.r.Unlock()
L.r.Lock()
C.luaL_checktype(L.s, C.int(narg), C.int(t))
L.CheckStackArg(narg)
vt := C.lua_type(L.s, C.int(narg))
if LuaValType(vt) != t {
L.raiseArgumentError(narg, t)
}
}
// luaL_checkudata
// luaL_checkudata isn't work on windows due lua_error call
func (L *State) CheckUdata(narg int, tname string) unsafe.Pointer {
Ctname := C.CString(tname)
defer C.free(unsafe.Pointer(Ctname))
defer L.r.Unlock()
L.r.Lock()
return unsafe.Pointer(C.luaL_checkudata(L.s, C.int(narg), Ctname))
L.CheckStackArg(narg)
if !L.IsUserdata(narg) {
L.raiseArgumentError(narg, LUA_TUSERDATA)
}
return unsafe.Pointer(C.clua_testudata(L.s, C.int(narg), Ctname))
}
// Executes file, returns nil for no errors or the lua error string on failure
@@ -561,3 +581,14 @@ func (L *State) OpenOS() {
L.r.Lock()
C.clua_openos(L.s)
}
func (L *State) raiseArgumentError(narg int, t LuaValType) {
tn := C.GoString(C.lua_typename(L.s, C.int(t)))
vt := C.lua_type(L.s, C.int(narg))
vtn := C.GoString(C.lua_typename(L.s, vt))
index := narg
if index < 0 {
index = L.GetTop() + narg
}
L.RaiseError(fmt.Sprintf("bad argument #%d (%s expected, got %s)", index, tn, vtn))
}
+5 -3
View File
@@ -120,10 +120,12 @@ func (L *State) SetExecutionLimit(instrNumber int) {
}, instrNumber)
}
// lua_error
// correctly catching lua_error can't support on windows so there using go panic method
func (L *State) RaiseError(msg string) {
L.PushString((&LuaError{}).New(L, 0, msg).String())
C.lua_error(L.s)
le := (&LuaError{}).New(L, LUA_ERRRUN, msg)
L.PushString(le.String())
panic(le)
// C.lua_error(L.s)
}
func (L *State) NewError(msg string) *LuaError {
+100 -8
View File
@@ -457,8 +457,8 @@ func TestCustomDebugHook(t *testing.T) {
t.Fatalf("Script should have raised an error")
} else {
le := err.(*LuaError)
if le.Code != 0 {
t.Fatalf("Wrong kind of lua error: %v (%d %d)\n", le, le.Code, 0)
if le.Code != LUA_ERRRUN {
t.Fatalf("Wrong kind of lua error: %v (%d %d)\n", le, le.Code, LUA_ERRRUN)
}
if le.Msg != "stop" {
t.Fatalf("Error should be coming from the hook: %v", le)
@@ -487,6 +487,13 @@ func TestRunLuaCallback(t *testing.T) {
}
L.Register("reg_cb", regCb)
code := `r = 0
reg_cb(
function(x)
r = r + x
return r
end
)`
// This code demonstrates checking that a value on the stack is a go function
L.CheckStack(1)
@@ -497,7 +504,7 @@ func TestRunLuaCallback(t *testing.T) {
L.Pop(1)
// We call example_function from inside Lua VM
if err := L.DoString("r = 0; reg_cb(function(x) r = r + x; return r; end)"); err != nil {
if err := L.DoString(code); err != nil {
t.Fatalf("Error executing main function: %v", err)
}
@@ -534,7 +541,7 @@ func TestRunLuaCallback(t *testing.T) {
L.Pop(-1)
}
func TestRunLuaCallbackFail(t *testing.T) {
func TestRunLuaCallbackFailFromLuaCode(t *testing.T) {
L := NewState()
L.OpenLibs()
defer L.Close()
@@ -554,6 +561,14 @@ func TestRunLuaCallbackFail(t *testing.T) {
}
L.Register("reg_cb", regCb)
code := `reg_cb(
function(x)
for t=1,x do
print(t)
end
return x
end
)`
// This code demonstrates checking that a value on the stack is a go function
L.CheckStack(1)
@@ -564,7 +579,7 @@ func TestRunLuaCallbackFail(t *testing.T) {
L.Pop(1)
// We call example_function from inside Lua VM
if err := L.DoString("reg_cb(function(x) for t=1,x do print(t); end; return x; end)"); err != nil {
if err := L.DoString(code); err != nil {
t.Fatalf("Error executing main function: %v", err)
}
@@ -598,6 +613,83 @@ func TestRunLuaCallbackFail(t *testing.T) {
L.Unref(LUA_REGISTRYINDEX, refTr)
}
func TestRunLuaCallbackFailFromGoCode(t *testing.T) {
L := NewState()
L.OpenLibs()
defer L.Close()
var stCb *State
var refCb, refTr int
regCb := func(L *State) int {
if !L.IsFunction(-1) {
t.Fatalf("Recived callback is not a lua function")
}
stCb = L.NewThread()
refTr = L.Ref(LUA_REGISTRYINDEX)
refCb = L.Ref(LUA_REGISTRYINDEX)
return 0
}
test := func(L *State) int {
L.CheckNumber(-1)
return 0
}
L.Register("reg_cb", regCb)
code := `reg_cb(
function(x)
test(x)
return x
end
)`
// This code demonstrates checking that a value on the stack is a go function
L.CheckStack(1)
L.GetGlobal("reg_cb")
if !L.IsGoFunction(-1) {
t.Fatalf("IsGoFunction failed to recognize a Go function object")
}
L.Pop(1)
// We call example_function from inside Lua VM
if err := L.DoString(code); err != nil {
t.Fatalf("Error executing main function: %v", err)
}
if stCb == nil {
t.Fatalf("Lua callback is not set")
}
stCb.Register("test", test)
for i := 0; i < 10; i++ {
stCb.RawGeti(LUA_REGISTRYINDEX, refCb)
if i%2 == 0 {
stCb.PushNumber(3)
} else {
stCb.PushString("abc")
}
if err := stCb.Call(1, 1); err == nil && i%2 == 1 {
t.Fatal("Call did not return an error")
} else if err != nil {
le := err.(*LuaError)
if le.Code != LUA_ERRRUN {
t.Fatalf("Wrong kind of lua error: %v (%d %d)\n", le, le.Code, LUA_ERRRUN)
}
// TODO: it's a big problem that previously calls are keeping on the stack
if len(le.LuaST)%2 != 0 {
t.Fatalf("Wrong amount of lines in stacktrace: %v (%d)\n", le, len(le.LuaST))
}
}
stCb.Pop(-1)
}
L.Unref(LUA_REGISTRYINDEX, refCb)
L.Unref(LUA_REGISTRYINDEX, refTr)
}
func TestCoroutineRunning(t *testing.T) {
L := NewState()
L.OpenLibs()
@@ -685,7 +777,7 @@ func TestLuaRegsitryIsPerState(t *testing.T) {
if obsVal != val {
panic("expected obsVal to match val")
}
//fmt.Printf("good: retreived val from L registry\n")
// good: retreived val from L registry
L.Pop(1)
// now query the L2 registry
@@ -695,7 +787,7 @@ func TestLuaRegsitryIsPerState(t *testing.T) {
fmt.Printf("bad, expected nil, got: '%s'\n", L2.LuaStackPosToString(-1))
panic("expected nil back when querying L2 registry for key")
}
//fmt.Printf("good: did not retreived val from L2 registry under key\n")
// good: did not retreived val from L2 registry under key
// now check that a new coroutine in L sees the same registry.
L3 := L.NewThread()
@@ -708,7 +800,7 @@ func TestLuaRegsitryIsPerState(t *testing.T) {
if obsVal3 != val {
panic("expected obsVal3 to match val")
}
//fmt.Printf("good: retreived val from L3 registry\n")
// good: retreived val from L3 registry
}
func assert(t *testing.T, b bool, msg string) {