static char* test_serialize_binary() { size_t size = 512; lsb_output_buffer b; lsb_err_value ret = lsb_init_output_buffer(&b, size); mu_assert(ret == NULL, "received: %s", lsb_err_string(ret)); lsb_serialize_binary(&b, "a\r\n\\\"", 5); mu_assert(b.pos == 9, "received %d", (int)b.pos); mu_assert(memcmp(b.buf, "a\\r\\n\\\\\\\"", 5) == 0, "received %.*s", 9, b.buf); lsb_free_output_buffer(&b); return NULL; }
char* lsb_destroy(lsb_lua_sandbox *lsb) { char *err = NULL; if (!lsb) { return err; } if (preserve_global_data(lsb)) { size_t len = strlen(lsb->error_message); err = malloc(len + 1); if (err != NULL) { strcpy(err, lsb->error_message); } } lsb_terminate(lsb, NULL); lsb_free_output_buffer(&lsb->output); free(lsb->state_file); free(lsb->lua_file); free(lsb); return err; }
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; }
lsb_lua_sandbox* lsb_create(void *parent, const char *lua_file, const char *cfg, lsb_logger logger) { if (!lua_file) { if (logger) logger(__FUNCTION__, 3, "lua_file must be specified"); return NULL; } if (!set_tz()) { if (logger) logger(__FUNCTION__, 3, "fail to set the TZ to UTC"); return NULL; } set_random_seed(); lsb_lua_sandbox *lsb = malloc(sizeof*lsb); if (!lsb) { if (logger) logger(__FUNCTION__, 3, "memory allocation failed"); return NULL; } memset(lsb->usage, 0, sizeof(lsb->usage)); #ifdef LUA_JIT lsb->lua = luaL_newstate(); #else lsb->lua = lua_newstate(memory_manager, lsb); #endif if (!lsb->lua) { if (logger) logger(__FUNCTION__, 3, "lua state creation failed"); free(lsb); return NULL; } // add the config to the lsb_config registry table lua_State *lua_cfg = load_sandbox_config(cfg, logger); if (!lua_cfg) { lua_close(lsb->lua); free(lsb); return NULL; } lua_pushnil(lua_cfg); lua_pushvalue(lua_cfg, LUA_GLOBALSINDEX); copy_table(lsb->lua, lua_cfg, logger); lua_pop(lua_cfg, 2); lua_close(lua_cfg); size_t ml = get_usage_config(lsb->lua, -1, "memory_limit"); size_t il = get_usage_config(lsb->lua, -1, "instruction_limit"); size_t ol = get_usage_config(lsb->lua, -1, "output_limit"); lua_setfield(lsb->lua, LUA_REGISTRYINDEX, LSB_CONFIG_TABLE); lua_pushcclosure(lsb->lua, &read_config, 0); lua_setglobal(lsb->lua, "read_config"); lua_pushlightuserdata(lsb->lua, (void *)lsb); lua_pushcclosure(lsb->lua, &output, 1); lua_setglobal(lsb->lua, "output"); lsb->parent = parent; lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT] = ml; lsb->usage[LSB_UT_INSTRUCTION][LSB_US_LIMIT] = il; lsb->usage[LSB_UT_OUTPUT][LSB_US_LIMIT] = ol; lsb->state = LSB_UNKNOWN; lsb->error_message[0] = 0; lsb->lua_file = malloc(strlen(lua_file) + 1); lsb->state_file = NULL; if (!lsb->lua_file || lsb_init_output_buffer(&lsb->output, ol)) { if (logger) logger(__FUNCTION__, 3, "memory allocation failed failed"); lsb_free_output_buffer(&lsb->output); free(lsb->lua_file); lua_close(lsb->lua); lsb->lua = NULL; free(lsb); return NULL; } strcpy(lsb->lua_file, lua_file); return lsb; }
static hs_output_plugin* create_output_plugin(lsb_message_match_builder *mmb, const hs_config *cfg, hs_sandbox_config *sbc) { char *state_file = NULL; char lua_file[HS_MAX_PATH]; if (!hs_get_fqfn(sbc->dir, sbc->filename, lua_file, sizeof(lua_file))) { hs_log(NULL, g_module, 3, "%s failed to construct the lua_file path", sbc->cfg_name); return NULL; } hs_output_plugin *p = calloc(1, sizeof(hs_output_plugin)); if (!p) { hs_log(NULL, g_module, 2, "%s hs_output_plugin memory allocation failed", sbc->cfg_name); return NULL; } if (pthread_mutex_init(&p->cp_lock, NULL)) { free(p); hs_log(NULL, g_module, 3, "%s pthread_mutex_init failed", sbc->cfg_name); return NULL; } p->list_index = -1; p->sequence_id = 1; p->ticker_interval = sbc->ticker_interval; int stagger = p->ticker_interval > 60 ? 60 : p->ticker_interval; // distribute when the timer_events will fire if (stagger) { p->ticker_expires = time(NULL) + rand() % stagger; } if (sbc->async_buffer_size > 0) { p->async_len = sbc->async_buffer_size; p->async_cp = calloc(p->async_len, sizeof(hs_checkpoint_pair)); if (!p->async_cp) { destroy_output_plugin(p); hs_log(NULL, g_module, 2, "%s async buffer memory allocation failed", sbc->cfg_name); return NULL; } } p->mm = lsb_create_message_matcher(mmb, sbc->message_matcher); if (!p->mm) { hs_log(NULL, g_module, 3, "%s invalid message_matcher: %s", sbc->cfg_name, sbc->message_matcher); destroy_output_plugin(p); return NULL; } size_t len = strlen(sbc->cfg_name) + 1; p->name = malloc(len); if (!p->name) { hs_log(NULL, g_module, 2, "%s name memory allocation failed", sbc->cfg_name); destroy_output_plugin(p); } memcpy(p->name, sbc->cfg_name, len); if (sbc->preserve_data) { size_t len = strlen(cfg->output_path) + strlen(sbc->cfg_name) + 7; state_file = malloc(len); if (!state_file) { hs_log(NULL, g_module, 2, "%s state_file memory allocation failed", sbc->cfg_name); destroy_output_plugin(p); return NULL; } int ret = snprintf(state_file, len, "%s/%s.data", cfg->output_path, sbc->cfg_name); if (ret < 0 || ret > (int)len - 1) { hs_log(NULL, g_module, 3, "%s failed to construct the state_file path", sbc->cfg_name); free(state_file); destroy_output_plugin(p); return NULL; } } lsb_output_buffer ob; if (lsb_init_output_buffer(&ob, 8 * 1024)) { hs_log(NULL, g_module, 3, "%s configuration memory allocation failed", sbc->cfg_name); free(state_file); destroy_output_plugin(p); return NULL; } if (!hs_get_full_config(&ob, 'o', cfg, sbc)) { hs_log(NULL, g_module, 3, "%s hs_get_full_config failed", sbc->cfg_name); lsb_free_output_buffer(&ob); free(state_file); destroy_output_plugin(p); return NULL; } lsb_logger logger = {.context = NULL, .cb = hs_log}; p->hsb = lsb_heka_create_output(p, lua_file, state_file, ob.buf, &logger, update_checkpoint_callback); lsb_free_output_buffer(&ob); free(sbc->cfg_lua); sbc->cfg_lua = NULL; free(state_file); if (!p->hsb) { destroy_output_plugin(p); hs_log(NULL, g_module, 3, "%s lsb_heka_create_output failed", sbc->cfg_name); return NULL; } return p; }