#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define CMACH_EXECLIMIT_REGKEY "cmach_execlimit"
typedef struct {
lua_State *L;
size_t mem_used;
size_t mem_limit;
void *execlimit_error;
} cmach_lua_t;
static void *cmach_lua_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
cmach_lua_t *S = ud;
void *new;
if (nsize == 0) {
S->mem_used -= osize;
free(ptr);
return NULL;
} else {
if (!ptr) osize = 0;
if (S->mem_limit && S->mem_used - osize + nsize > S->mem_limit) return NULL;
new = realloc(ptr, nsize);
if (!new) return NULL;
S->mem_used -= osize;
S->mem_used += nsize;
return new;
}
}
static int cmach_lua_panic(lua_State *L) {
const char *msg = lua_tostring(L, -1);
if (lua_checkstack(L, 1)) {
msg = luaL_tolstring(L, -1, NULL);
} else {
msg = lua_tostring(L, -1);
if (!msg) msg = "error not a string";
}
fprintf(stderr, "Lua panic: %s\n", msg);
return 0;
}
int cmach_lua_execlimit_tostring(lua_State *L) {
lua_pushliteral(L, "execution limit reached");
return 1;
}
static int cmach_lua_setup(lua_State *L) {
lua_createtable(L, 0, 0);
lua_createtable(L, 0, 1);
lua_pushcfunction(L, cmach_lua_execlimit_tostring);
lua_setfield(L, -2, "__tostring");
lua_setmetatable(L, -2);
lua_pushlightuserdata(L, (void *)lua_topointer(L, -1));
lua_insert(L, -2);
lua_setfield(L, LUA_REGISTRYINDEX, CMACH_EXECLIMIT_REGKEY);
return 1;
}
int cmach_lua_errmsgh(lua_State *L) {
lua_Debug ar = { 0, };
lua_createtable(L, 4, 0);
lua_pushvalue(L, 1);
lua_rawseti(L, -2, 1);
luaL_traceback(L, L, NULL, 2);
lua_rawseti(L, -2, 2);
if (lua_getstack(L, 2, &ar)) {
lua_getinfo(L, "Sl", &ar);
lua_pushstring(L, ar.short_src);
lua_rawseti(L, -2, 3);
lua_pushinteger(L, ar.currentline);
lua_rawseti(L, -2, 4);
}
return 1;
}
static void cmach_lua_exhausted(lua_State *L, lua_Debug *ar) {
(void)ar;
lua_getfield(L, LUA_REGISTRYINDEX, CMACH_EXECLIMIT_REGKEY);
lua_error(L);
}
void cmach_lua_execlimit(cmach_lua_t *S, int count) {
lua_sethook(S->L, cmach_lua_exhausted, LUA_MASKCOUNT, count);
}
void cmach_lua_free(cmach_lua_t *S) {
lua_close(S->L);
free(S);
}
cmach_lua_t *cmach_lua_new() {
cmach_lua_t *S;
lua_State *L;
int status;
S = calloc(1, sizeof(cmach_lua_t));
if (!S) return NULL;
L = lua_newstate(cmach_lua_alloc, S);
if (!L) {
free(S);
return NULL;
}
S->L = L;
lua_atpanic(L, cmach_lua_panic);
lua_pushcfunction(L, cmach_lua_setup);
status = lua_pcall(L, 0, 1, 0);
if (status != LUA_OK) {
if (status != LUA_ERRMEM) lua_error(L);
cmach_lua_free(S);
return NULL;
}
S->execlimit_error = lua_touserdata(L, -1);
lua_pop(L, 1);
return S;
}
static void cmach_lua_unsetglobal(lua_State *L, char *name) {
lua_pushnil(L);
lua_setglobal(L, name);
}
static void cmach_lua_seal_base_mayfail(lua_State *L) {
cmach_lua_unsetglobal(L, "dofile");
cmach_lua_unsetglobal(L, "load");
cmach_lua_unsetglobal(L, "loadfile");
cmach_lua_unsetglobal(L, "pcall");
cmach_lua_unsetglobal(L, "print");
cmach_lua_unsetglobal(L, "require");
cmach_lua_unsetglobal(L, "xpcall");
}
static int cmach_lua_openlibs_sealed_mayfail(lua_State *L) {
luaL_requiref(L, "_G", luaopen_base, 1);
luaL_requiref(L, "table", luaopen_table, 1);
luaL_requiref(L, "string", luaopen_string, 1);
luaL_requiref(L, "math", luaopen_math, 1);
luaL_requiref(L, "utf8", luaopen_utf8, 1);
cmach_lua_seal_base_mayfail(L);
return 0;
}
int cmach_lua_openlibs_sealed(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_openlibs_sealed_mayfail);
result = lua_pcall(L, 0, 0, -2);
if (result == LUA_OK) {
lua_pop(L, 1);
} else {
lua_remove(L, -2);
}
return result;
}
static int cmach_lua_openlibs_mayfail(lua_State *L) {
luaL_openlibs(L);
return 0;
}
int cmach_lua_openlibs(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_openlibs_mayfail);
result = lua_pcall(L, 0, 0, -2);
if (result == LUA_OK) {
lua_pop(L, 1);
} else {
lua_remove(L, -2);
}
return result;
}
static int cmach_lua_seal_mayfail(lua_State *L) {
cmach_lua_unsetglobal(L, "coroutine");
cmach_lua_unsetglobal(L, "debug");
cmach_lua_unsetglobal(L, "io");
cmach_lua_unsetglobal(L, "os");
cmach_lua_unsetglobal(L, "package");
cmach_lua_seal_base_mayfail(L);
return 0;
}
int cmach_lua_seal(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_seal_mayfail);
result = lua_pcall(L, 0, 0, -2);
if (result == LUA_OK) {
lua_pop(L, 1);
} else {
lua_remove(L, -2);
}
return result;
}
static int cmach_lua_touserstr_mayfail(lua_State *L) {
const char *str;
size_t len;
char *buf;
str = luaL_tolstring(L, 1, &len);
#if LUA_VERSION_NUM < 504
buf = lua_newuserdata(L, len);
#else
buf = lua_newuserdatauv(L, len, 0);
#endif
memcpy(buf, str, len);
return 1;
}
int cmach_lua_touserstr(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_touserstr_mayfail);
lua_pushvalue(L, -3);
result = lua_pcall(L, 1, 1, -3);
lua_remove(L, -2); lua_remove(L, -2); return result;
}
static int cmach_lua_usertostr_mayfail(lua_State *L) {
lua_pushlstring(L, lua_touserdata(L, 1), lua_tointeger(L, 2));
return 1;
}
int cmach_lua_pushlstring(lua_State *L, const char *str, lua_Integer len) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_usertostr_mayfail);
lua_pushlightuserdata(L, (void *)str);
lua_pushinteger(L, len);
result = lua_pcall(L, 2, 1, -4);
lua_remove(L, -2); return result;
}
static int cmach_lua_ref_mayfail(lua_State *L) {
lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX));
return 1;
}
int cmach_lua_ref(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_ref_mayfail);
lua_pushvalue(L, -3);
result = lua_pcall(L, 1, 1, -3);
lua_remove(L, -2); lua_remove(L, -2); return result;
}
int cmach_lua_is_execlimit(cmach_lua_t *S) {
if (lua_topointer(S->L, -1) == S->execlimit_error) {
return 1;
} else {
return 0;
}
}
typedef int (*cmach_callback_fptr)(void *context, int nargs);
typedef void (*cmach_callback_release_fptr)(void *context);
int cmach_call_callback(lua_State *L) {
cmach_callback_fptr callback = lua_touserdata(L, lua_upvalueindex(2));
int result = callback(lua_touserdata(L, lua_upvalueindex(1)), lua_gettop(L));
if (result < 0) lua_error(L);
return result;
}
static int cmach_lua_closure_gc(lua_State *L) {
void *context;
cmach_callback_release_fptr release;
lua_rawgeti(L, 1, 1);
context = lua_touserdata(L, -1);
lua_rawgeti(L, 1, 2);
release = lua_touserdata(L, -1);
release(context);
return 0;
}
static int cmach_lua_pushclosure_mayfail(lua_State *L) {
lua_createtable(L, 2, 0);
lua_pushvalue(L, 1);
lua_rawseti(L, -2, 1);
lua_pushvalue(L, 3);
lua_rawseti(L, -2, 2);
lua_remove(L, -2);
lua_createtable(L, 0, 1);
lua_pushcfunction(L, cmach_lua_closure_gc);
lua_setfield(L, -2, "__gc");
lua_setmetatable(L, -2);
lua_pushcclosure(L, cmach_call_callback, 3);
return 1;
}
int cmach_lua_pushclosure(
lua_State *L,
void *context,
cmach_callback_fptr callback,
cmach_callback_release_fptr release
) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_pushclosure_mayfail);
lua_pushlightuserdata(L, context);
lua_pushlightuserdata(L, callback);
lua_pushlightuserdata(L, release);
result = lua_pcall(L, 3, 1, -5);
lua_remove(L, -2); return result;
}
static int cmach_lua_newtable_mayfail(lua_State *L) {
lua_newtable(L);
return 1;
}
int cmach_lua_newtable(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_newtable_mayfail);
result = lua_pcall(L, 0, 1, -2);
lua_remove(L, -2); return result;
}
static int cmach_lua_len_mayfail(lua_State *L) {
lua_pushinteger(L, luaL_len(L, 1));
return 1;
}
int cmach_lua_len(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_len_mayfail);
lua_pushvalue(L, -3);
result = lua_pcall(L, 1, 1, -3);
lua_remove(L, -2); lua_remove(L, -2); return result;
}
static int cmach_lua_gettable_mayfail(lua_State *L) {
lua_gettable(L, 1);
return 1;
}
int cmach_lua_gettable(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_gettable_mayfail);
lua_pushvalue(L, -4);
lua_pushvalue(L, -4);
result = lua_pcall(L, 2, 1, -4);
lua_remove(L, -2); lua_remove(L, -2); lua_remove(L, -2); return result;
}
static int cmach_lua_settable_mayfail(lua_State *L) {
lua_settable(L, 1);
return 1;
}
int cmach_lua_settable(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_settable_mayfail);
lua_pushvalue(L, -5);
lua_pushvalue(L, -5);
lua_pushvalue(L, -5);
result = lua_pcall(L, 3, 0, -5);
lua_pop(L, 4); return result;
}
static int cmach_lua_setglobal_mayfail(lua_State *L) {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
lua_insert(L, 1);
lua_rawset(L, 1);
return 0;
}
int cmach_lua_setglobal(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_setglobal_mayfail);
lua_pushvalue(L, -4);
lua_pushvalue(L, -4);
result = lua_pcall(L, 2, 0, -4);
lua_pop(L, 3); return result;
}
static int cmach_lua_getglobal_mayfail(lua_State *L) {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
lua_insert(L, 1);
lua_rawget(L, 1);
return 1;
}
int cmach_lua_getglobal(lua_State *L) {
int result;
lua_pushcfunction(L, cmach_lua_errmsgh);
lua_pushcfunction(L, cmach_lua_getglobal_mayfail);
lua_pushvalue(L, -3);
result = lua_pcall(L, 1, 1, -3);
lua_replace(L, -3); lua_pop(L, 1); return result;
}