Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 50 additions & 6 deletions src/flb_lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,24 @@ static int flb_lua_is_valid_key(lua_State *l, int index)
return FLB_TRUE;
}

int flb_lua_pushmpack(lua_State *l, mpack_reader_t *reader)
/* nesting limit for MessagePack values pushed to Lua, as for JSON packing */
#define FLB_LUA_MPACK_MAX_DEPTH 512

static int lua_pushmpack_value(lua_State *l, mpack_reader_t *reader, int depth)
{
int ret = 0;
mpack_tag_t tag;
uint32_t length;
uint32_t i;
int index;
const char *data;
struct flb_lua_metadata meta;

tag = mpack_read_tag(reader);
if (mpack_reader_error(reader) != mpack_ok) {
return -1;
}

switch (mpack_tag_type(&tag)) {
case mpack_type_nil:
lua_getglobal(l, FLB_LUA_VAR_FLB_NULL);
Expand All @@ -135,40 +143,52 @@ int flb_lua_pushmpack(lua_State *l, mpack_reader_t *reader)
case mpack_type_str:
case mpack_type_bin:
case mpack_type_ext:
/* the reader checks the length against the remaining data */
length = mpack_tag_bytes(&tag);
lua_pushlstring(l, reader->data, length);
reader->data += length;
data = mpack_read_bytes_inplace(reader, length);
if (mpack_reader_error(reader) != mpack_ok) {
return -1;
}
lua_pushlstring(l, data, length);
mpack_done_type(reader, mpack_tag_type(&tag));
break;
case mpack_type_array:
if (depth >= FLB_LUA_MPACK_MAX_DEPTH || !lua_checkstack(l, 3)) {
return -1;
}
flb_lua_metadata_init(&meta);
meta.data_type = FLB_LUA_L2C_TYPE_ARRAY;

length = mpack_tag_array_count(&tag);
lua_createtable(l, length, 0);
index = lua_gettop(l); /* save index of created table */
for (i = 0; i < length; i++) {
ret = flb_lua_pushmpack(l, reader);
ret = lua_pushmpack_value(l, reader, depth + 1);
if (ret) {
return ret;
}
lua_rawseti(l, -2, i+1);
}
mpack_done_array(reader);
flb_lua_setmetatable(l, &meta, index);

break;
case mpack_type_map:
if (depth >= FLB_LUA_MPACK_MAX_DEPTH || !lua_checkstack(l, 4)) {
return -1;
}
flb_lua_metadata_init(&meta);
meta.data_type = FLB_LUA_L2C_TYPE_MAP;

length = mpack_tag_map_count(&tag);
lua_createtable(l, length, 0);
index = lua_gettop(l); /* save index of created table */
for (i = 0; i < length; i++) {
ret = flb_lua_pushmpack(l, reader);
ret = lua_pushmpack_value(l, reader, depth + 1);
if (ret) {
return ret;
}
ret = flb_lua_pushmpack(l, reader);
ret = lua_pushmpack_value(l, reader, depth + 1);
if (ret) {
return ret;
}
Expand All @@ -179,15 +199,39 @@ int flb_lua_pushmpack(lua_State *l, mpack_reader_t *reader)
}
lua_settable(l, -3);
}
mpack_done_map(reader);
flb_lua_setmetatable(l, &meta, index);

break;
default:
return -1;
}

if (mpack_reader_error(reader) != mpack_ok) {
return -1;
}

return 0;
}

/*
* Push the next MessagePack value of the reader. On failure, such as truncated
* or too deeply nested data, nothing is pushed and -1 is returned.
*/
int flb_lua_pushmpack(lua_State *l, mpack_reader_t *reader)
{
int ret;
int top;

top = lua_gettop(l);
ret = lua_pushmpack_value(l, reader, 0);
if (ret != 0) {
lua_settop(l, top);
}

return ret;
}

void flb_lua_pushmsgpack(lua_State *l, msgpack_object *o)
{
int i;
Expand Down
61 changes: 61 additions & 0 deletions tests/internal/lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,66 @@ static void test_pushmpack()
lua_close(l);
}

/* malformed or too deeply nested input is rejected without pushing values */
static void test_pushmpack_invalid()
{
int i;
int top;
char *nested;
size_t size;
mpack_reader_t reader;
lua_State *l = lua_setup(NULL);
/* str8 declaring 200 bytes with 3 present */
const char truncated_str[] = "\xd9\xc8" "abc";
/* array of 3 with a single element */
const char truncated_array[] = "\x93\x01";
/* bin8 declaring 10 bytes inside a map value */
const char truncated_bin[] = "\x81\xa1k\xc4\x0a" "xy";

top = lua_gettop(l);

mpack_reader_init_data(&reader, truncated_str, sizeof(truncated_str) - 1);
TEST_CHECK(flb_lua_pushmpack(l, &reader) == -1);
TEST_CHECK(lua_gettop(l) == top);

mpack_reader_init_data(&reader, truncated_array, sizeof(truncated_array) - 1);
TEST_CHECK(flb_lua_pushmpack(l, &reader) == -1);
TEST_CHECK(lua_gettop(l) == top);

mpack_reader_init_data(&reader, truncated_bin, sizeof(truncated_bin) - 1);
TEST_CHECK(flb_lua_pushmpack(l, &reader) == -1);
TEST_CHECK(lua_gettop(l) == top);

/* nesting: 512 nested arrays are accepted, 513 are rejected */
size = 514;
nested = flb_malloc(size);
TEST_ASSERT(nested != NULL);

memset(nested, 0x91, 512);
nested[512] = 0x01;
mpack_reader_init_data(&reader, nested, 513);
TEST_CHECK(flb_lua_pushmpack(l, &reader) == 0);
TEST_CHECK(lua_gettop(l) == top + 1);
lua_settop(l, top);

memset(nested, 0x91, 513);
nested[513] = 0x01;
mpack_reader_init_data(&reader, nested, 514);
TEST_CHECK(flb_lua_pushmpack(l, &reader) == -1);
TEST_CHECK(lua_gettop(l) == top);

/* a valid value still decodes after rejected ones */
for (i = 0; i < 2; i++) {
mpack_reader_init_data(&reader, "\x92\x01\xa2ok", 5);
TEST_CHECK(flb_lua_pushmpack(l, &reader) == 0);
check_equals(l, "{ [1] = 1 [2] = ok }");
lua_settop(l, top);
}

flb_free(nested);
lua_close(l);
}

static void test_tomsgpack()
{
const char expected[] = "[{\"key\"=>\"value\"}, \"msgpack-str\", 4]";
Expand Down Expand Up @@ -316,6 +376,7 @@ TEST_LIST = {
{ "lua_pushtimetable" , test_pushtimetable},
{ "lua_pushmsgpack" , test_pushmsgpack },
{ "lua_pushmpack" , test_pushmpack },
{ "lua_pushmpack_invalid" , test_pushmpack_invalid },
{ "lua_tomsgpack" , test_tomsgpack },
{ "lua_tompack" , test_tompack },
{ "lua_arraylength" , test_lua_arraylength },
Expand Down
Loading