lsb_err_value lsb_serialize_binary(lsb_output_buffer *ob, const void *src, size_t len) { lsb_err_value ret = NULL; const char *uc = (const char *)src; for (unsigned i = 0; !ret && i < len; ++i) { switch (uc[i]) { case '\n': ret = lsb_outputs(ob, "\\n", 2); break; case '\r': ret = lsb_outputs(ob, "\\r", 2); break; case '"': ret = lsb_outputs(ob, "\\\"", 2); break; case '\\': ret = lsb_outputs(ob, "\\\\", 2); break; default: ret = lsb_outputc(ob, uc[i]); break; } } return ret; }
lsb_err_value lsb_serialize_double(lsb_output_buffer *ob, double d) { if (isnan(d)) { return lsb_outputs(ob, "0/0", 3); } if (d == INFINITY) { return lsb_outputs(ob, "1/0", 3); } if (d == -INFINITY) { return lsb_outputs(ob, "-1/0", 4); } return lsb_outputfd(ob, d); }
lsb_err_value lsb_outputd(lsb_output_buffer *b, double d) { if (!b) return LSB_ERR_UTIL_NULL; if (isnan(d)) { return lsb_outputs(b, "nan", 3); } if (d == INFINITY) { return lsb_outputs(b, "inf", 3); } if (d == -INFINITY) { return lsb_outputs(b, "-inf", 4); } return lsb_outputfd(b, d); }
static int serialize_bloom_filter(lua_State *lua) { lsb_output_buffer* ob = lua_touserdata(lua, -1); const char *key = lua_touserdata(lua, -2); bloom_filter* bf = lua_touserdata(lua, -3); if (!(ob && key && bf)) { return 1; } if (lsb_outputf(ob, "if %s == nil then %s = bloom_filter.new(%u, %g) end\n", key, key, (unsigned)bf->items, bf->probability)) { return 1; } if (lsb_outputf(ob, "%s:fromstring(%u, \"", key, (unsigned)bf->cnt)) { return 1; } if (lsb_serialize_binary(ob, bf->data, bf->bytes)) return 1; if (lsb_outputs(ob, "\")\n", 3)) { return 1; } return 0; }
static int output_hyperloglog(lua_State *lua) { lsb_output_buffer *ob = lua_touserdata(lua, -1); hyperloglog *hll = lua_touserdata(lua, -2); if (!(ob && hll)) return 1; if (lsb_outputs(ob, (const char *)hll, sizeof(hyperloglog) - 1)) return 1; return 0; }
static int serialize_hyperloglog(lua_State *lua) { lsb_output_buffer *ob = lua_touserdata(lua, -1); const char *key = lua_touserdata(lua, -2); hyperloglog *hll = lua_touserdata(lua, -3); if (!(ob && key && hll)) return 1; if (lsb_outputf(ob, "if %s == nil then %s = hyperloglog.new() end\n", key, key)) { return 1; } if (lsb_outputf(ob, "%s:fromstring(\"", key)) return 1; if (lsb_serialize_binary(ob, hll, sizeof(hyperloglog) - 1)) return 1; if (lsb_outputs(ob, "\")\n", 3)) return 1; return 0; }
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; }