int serialize_table(lua_sandbox* lsb, serialization_data* data, size_t parent) { int result = 0; lua_checkstack(lsb->m_lua, 2); lua_pushnil(lsb->m_lua); while (result == 0 && lua_next(lsb->m_lua, -2) != 0) { result = serialize_kvp(lsb, data, parent); lua_pop(lsb->m_lua, 1); // Remove the value leaving the key on top for // the next interation. } return result; }
int preserve_global_data(lua_sandbox* lsb, const char* data_file) { static const char* G = "_G"; lua_getglobal(lsb->m_lua, G); if (!lua_istable(lsb->m_lua, -1)) { snprintf(lsb->m_error_message, ERROR_SIZE, "preserve_global_data cannot access the global table"); return 1; } FILE* fh = fopen(data_file, "wb"); if (fh == NULL) { snprintf(lsb->m_error_message, ERROR_SIZE, "preserve_global_data could not open: %s", data_file); return 1; } int result = 0; serialization_data data; data.m_fh = fh; data.m_keys.m_size = OUTPUT_SIZE; data.m_keys.m_pos = 0; data.m_keys.m_data = malloc(data.m_keys.m_size); data.m_tables.m_size = 64; data.m_tables.m_pos = 0; data.m_tables.m_array = malloc(data.m_tables.m_size * sizeof(table_ref)); if (data.m_tables.m_array == NULL || data.m_keys.m_data == NULL) { snprintf(lsb->m_error_message, ERROR_SIZE, "preserve_global_data out of memory"); result = 1; } else { dynamic_snprintf(&data.m_keys, "%s", G); data.m_keys.m_pos += 1; data.m_globals = lua_topointer(lsb->m_lua, -1); lua_checkstack(lsb->m_lua, 2); lua_pushnil(lsb->m_lua); while (result == 0 && lua_next(lsb->m_lua, -2) != 0) { result = serialize_kvp(lsb, &data, 0); lua_pop(lsb->m_lua, 1); } lua_pop(lsb->m_lua, lua_gettop(lsb->m_lua)); // Wipe the entire Lua stack. Since incremental cleanup on failure // was added the stack should only contain table _G. } free(data.m_tables.m_array); free(data.m_keys.m_data); fclose(fh); if (result != 0) { remove(data_file); } return result; }
static lsb_err_value serialize_table(lsb_lua_sandbox *lsb, serialization_data *data, size_t parent) { lsb_err_value ret = NULL; lua_checkstack(lsb->lua, 2); lua_pushnil(lsb->lua); while (!ret && lua_next(lsb->lua, -2) != 0) { ret = serialize_kvp(lsb, data, parent); lua_pop(lsb->lua, 1); // Remove the value leaving the key on top for // the next interation. } return ret; }
lsb_err_value preserve_global_data(lsb_lua_sandbox *lsb) { if (!lsb->lua || !lsb->state_file) { return NULL; } lua_sethook(lsb->lua, NULL, 0, 0); // make sure the string library is loaded before we start lua_getglobal(lsb->lua, LUA_STRLIBNAME); if (!lua_istable(lsb->lua, -1)) { lua_getglobal(lsb->lua, "require"); if (!lua_iscfunction(lsb->lua, -1)) { snprintf(lsb->error_message, LSB_ERROR_SIZE, "preserve_global_data 'require' function not found"); return LSB_ERR_LUA; } lua_pushstring(lsb->lua, LUA_STRLIBNAME); if (lua_pcall(lsb->lua, 1, 1, 0)) { int len = snprintf(lsb->error_message, LSB_ERROR_SIZE, "preserve_global_data failed loading 'string'"); if (len >= LSB_ERROR_SIZE || len < 0) { lsb->error_message[LSB_ERROR_SIZE - 1] = 0; } return LSB_ERR_LUA; } } lua_pop(lsb->lua, 1); lua_pushvalue(lsb->lua, LUA_GLOBALSINDEX); FILE *fh = fopen(lsb->state_file, "wb" CLOSE_ON_EXEC); if (fh == NULL) { int len = snprintf(lsb->error_message, LSB_ERROR_SIZE, "preserve_global_data could not open: %s", lsb->state_file); if (len >= LSB_ERROR_SIZE || len < 0) { lsb->error_message[LSB_ERROR_SIZE - 1] = 0; } return LSB_ERR_LUA;; } lsb_err_value ret = NULL; serialization_data data; data.fh = fh; // Clear the sandbox limits during preservation. #ifdef LUA_JIT lua_gc(lsb->lua, LUA_GCSETMEMLIMIT, 0); #else // size_t limit = lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT]; lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT] = 0; #endif // size_t cur_output_size = lsb->output.size; // size_t max_output_size = lsb->output.maxsize; lsb->output.maxsize = 0; // end clear data.tables.size = 64; data.tables.pos = 0; data.tables.array = malloc(data.tables.size * sizeof(table_ref)); if (data.tables.array == NULL || lsb_init_output_buffer(&data.keys, 0)) { snprintf(lsb->error_message, LSB_ERROR_SIZE, "preserve_global_data out of memory"); ret = LSB_ERR_UTIL_OOM; } else { fprintf(data.fh, "if %s and %s ~= %d then return end\n", preservation_version, preservation_version, get_preservation_version(lsb->lua)); ret = lsb_outputs(&data.keys, "_G", 2); if (!ret) { data.keys.pos += 1; // preserve the NUL in this use case data.globals = lua_topointer(lsb->lua, -1); lua_checkstack(lsb->lua, 2); lua_pushnil(lsb->lua); while (!ret && lua_next(lsb->lua, -2) != 0) { ret = serialize_kvp(lsb, &data, 0); lua_pop(lsb->lua, 1); } } lua_pop(lsb->lua, lua_gettop(lsb->lua)); // Wipe the entire Lua stack. Since incremental cleanup on failure // was added the stack should only contain table _G. } free(data.tables.array); lsb_free_output_buffer(&data.keys); fclose(fh); if (ret) remove(lsb->state_file); // Uncomment if we start preserving state when not destroying the sandbox // Note: serialization uses the output buffer, inprogress output can be // destroyed if the user was collecting output between calls. /* // Restore the sandbox limits after preservation #ifdef LUA_JIT lua_gc(lsb->lua, LUA_GCSETMEMLIMIT, lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT]); #else lua_gc(lsb->lua, LUA_GCCOLLECT, 0); lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT] = limit; lsb->usage[LSB_UT_MEMORY][LSB_US_MAXIMUM] = lsb->usage[LSB_UT_MEMORY][LSB_US_CURRENT]; #endif lsb->output.maxsize = max_output_size; lsb_clear_output_buffer(lsb->output); if (lsb->output.size > cur_output_size) { void* ptr = realloc(lsb->output.data, cur_output_size); if (!ptr) return 1; lsb->output.data = ptr; lsb->output.size = cur_output_size; } // end restore */ return ret; }