From: Thierry Fournier Date: Fri, 19 Feb 2016 11:09:29 +0000 (+0100) Subject: BUG/MAJOR: lua: segfault using Concat object X-Git-Tag: v1.7-dev2~86 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=49d4842e9898296f95de3dd3b26d1b68d6fa0d72;p=thirdparty%2Fhaproxy.git BUG/MAJOR: lua: segfault using Concat object Concat object is based on "luaL_Buffer". The luaL_Buffer documentation says: During its normal operation, a string buffer uses a variable number of stack slots. So, while using a buffer, you cannot assume that you know where the top of the stack is. You can use the stack between successive calls to buffer operations as long as that use is balanced; that is, when you call a buffer operation, the stack is at the same level it was immediately after the previous buffer operation. (The only exception to this rule is luaL_addvalue.) After calling luaL_pushresult the stack is back to its level when the buffer was initialized, plus the final string on its top. So, the stack cannot be manipulated between the first call at the function "luaL_buffinit()" and the last call to the function "luaL_pushresult()" because we cannot known the stack status. In other way, the memory used by these functions seems to be collected by GC, so if the GC is triggered during the usage of the Concat object, it can be used some released memory. This patch rewrite the Concat class without the "luaL_Buffer" system. It uses "userdata()" forr the memory allocation of the buffer strings. --- diff --git a/include/types/hlua.h b/include/types/hlua.h index a923624652..34bd648a92 100644 --- a/include/types/hlua.h +++ b/include/types/hlua.h @@ -32,6 +32,8 @@ struct stream; #define HLUA_F_AS_STRING 0x01 #define HLUA_F_MAY_USE_HTTP 0x02 +#define HLUA_CONCAT_BLOCSZ 2048 + enum hlua_exec { HLUA_E_OK = 0, HLUA_E_AGAIN, /* LUA yield, must resume the stack execution later, when @@ -136,6 +138,11 @@ struct hlua_socket { luaL_Buffer b; /* buffer used to prepare strings. */ }; +struct hlua_concat { + int size; + int len; +}; + #else /* USE_LUA */ /* Empty struct for compilation compatibility */ diff --git a/src/hlua_fcn.c b/src/hlua_fcn.c index 85451b08f2..fdcac05777 100644 --- a/src/hlua_fcn.c +++ b/src/hlua_fcn.c @@ -9,6 +9,8 @@ #include +#include + /* Contains the class reference of the concat object. */ static int class_concat_ref; @@ -121,14 +123,16 @@ static void hlua_array_add_fcn(lua_State *L, const char *name, lua_rawset(L, -3); } -static luaL_Buffer *hlua_check_concat(lua_State *L, int ud) +static struct hlua_concat *hlua_check_concat(lua_State *L, int ud) { - return (luaL_Buffer *)(hlua_checkudata(L, ud, class_concat_ref)); + return (struct hlua_concat *)(hlua_checkudata(L, ud, class_concat_ref)); } static int hlua_concat_add(lua_State *L) { - luaL_Buffer *b; + struct hlua_concat *b; + char *buffer; + char *new; const char *str; size_t l; @@ -138,34 +142,68 @@ static int hlua_concat_add(lua_State *L) /* Second arg must be a string. */ str = luaL_checklstring(L, 2, &l); - luaL_addlstring(b, str, l); + /* Get the buffer. */ + lua_rawgeti(L, 1, 1); + buffer = lua_touserdata(L, -1); + lua_pop(L, 1); + + /* Update the buffer size if it s required. The old buffer + * is crushed by the new in the object array, so it will + * be deleted by the GC. + * Note that in the first loop, the "new" variable is only + * used as a flag. + */ + new = NULL; + while (b->size - b->len < l) { + b->size += HLUA_CONCAT_BLOCSZ; + new = buffer; + } + if (new) { + new = lua_newuserdata(L, b->size); + memcpy(new, buffer, b->len); + lua_rawseti(L, 1, 1); + buffer = new; + } + + /* Copy string, and update metadata. */ + memcpy(buffer + b->len, str, l); + b->len += l; return 0; } static int hlua_concat_dump(lua_State *L) { - luaL_Buffer *b; + struct hlua_concat *b; + char *buffer; /* First arg must be a concat object. */ b = hlua_check_concat(L, 1); + /* Get the buffer. */ + lua_rawgeti(L, 1, 1); + buffer = lua_touserdata(L, -1); + lua_pop(L, 1); + /* Push the soncatenated strng in the stack. */ - luaL_pushresult(b); + lua_pushlstring(L, buffer, b->len); return 1; } int hlua_concat_new(lua_State *L) { - luaL_Buffer *b; + struct hlua_concat *b; lua_newtable(L); - b = lua_newuserdata(L, sizeof(luaL_Buffer)); + b = (struct hlua_concat *)lua_newuserdata(L, sizeof(*b)); + b->size = HLUA_CONCAT_BLOCSZ; + b->len = 0; lua_rawseti(L, -2, 0); + lua_newuserdata(L, HLUA_CONCAT_BLOCSZ); + lua_rawseti(L, -2, 1); lua_rawgeti(L, LUA_REGISTRYINDEX, class_concat_ref); lua_setmetatable(L, -2); - luaL_buffinit(L, b); return 1; }