mirror of
https://github.com/vxcontrol/golua.git
synced 2026-06-30 22:17:55 -04:00
Fixed crashing this code on windows due uncatched longjmp from lua_error function
This commit is contained in:
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user