diff --git a/src/box/iproto.cc b/src/box/iproto.cc index a1419233b9a0eafef65767d4eadcd3f3be6986a2..f2952812f049f045e332f9b341e1edc8ebe6930c 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -1322,6 +1322,7 @@ iproto_connection_on_input(ev_loop *loop, struct ev_io *watcher, return; } /* Read input. */ + ibuf_reserve(in, ibuf_unused(in)); ssize_t nrd = iostream_read(io, in->wpos, ibuf_unused(in)); if (nrd < 0) { /* Socket is not ready. */ if (nrd == IOSTREAM_ERROR) @@ -1343,7 +1344,7 @@ iproto_connection_on_input(ev_loop *loop, struct ev_io *watcher, IPROTO_RECEIVED, nrd); /* Update the read position and connection state. */ - in->wpos += nrd; + ibuf_alloc(in, nrd); con->parse_size += nrd; /* Enqueue all requests which are fully read up. */ if (iproto_enqueue_batch(con, in) != 0) diff --git a/src/lua/buffer.lua b/src/lua/buffer.lua index c5cefe32b60c029bde0bcf606b2565985bff1f89..9fc5ee8fda2a5a09d9bb9dad0b95bcbdbf5e5b9b 100644 --- a/src/lua/buffer.lua +++ b/src/lua/buffer.lua @@ -1,6 +1,7 @@ -- buffer.lua (internal file) local ffi = require('ffi') +local utils = require('internal.utils') local READAHEAD = 16320 ffi.cdef[[ @@ -120,10 +121,19 @@ local function ibuf_recycle(buf) builtin.ibuf_reinit(buf) end +local function ibuf_poison_unallocated(buf) + utils.poison_memory_region(buf.wpos, buf.epos - buf.wpos); +end + +local function ibuf_unpoison_unallocated(buf) + utils.unpoison_memory_region(buf.wpos, buf.epos - buf.wpos); +end + local function ibuf_reset(buf) checkibuf(buf, 'reset') buf.rpos = buf.buf buf.wpos = buf.buf + ibuf_poison_unallocated(buf) end local function ibuf_reserve_slow(buf, size) @@ -137,6 +147,7 @@ end local function ibuf_reserve(buf, size) checkibuf(buf, 'reserve') if buf.wpos + size <= buf.epos then + ibuf_unpoison_unallocated(buf) return buf.wpos end return ibuf_reserve_slow(buf, size) @@ -146,11 +157,18 @@ local function ibuf_alloc(buf, size) checkibuf(buf, 'alloc') local wpos if buf.wpos + size <= buf.epos then + -- + -- In case of using same buffer we need to unpoison newly + -- allocated memory after previous ibuf_alloc or poison after + -- newly allocated memory after previous ibuf_reserve. + -- + ibuf_unpoison_unallocated(buf) wpos = buf.wpos else wpos = ibuf_reserve_slow(buf, size) end buf.wpos = buf.wpos + size + ibuf_poison_unallocated(buf) return wpos end diff --git a/src/lua/init.c b/src/lua/init.c index 354fa52e0d203f91698ffc72655c6fdf99352b8d..10cbb816e25482284c1c6b7cb5abfaf4ae7ee356 100644 --- a/src/lua/init.c +++ b/src/lua/init.c @@ -279,6 +279,7 @@ static const char *lua_modules[] = { /* Make it first to affect load of all other modules */ "strict", strict_lua, "compat", compat_lua, + "internal.utils", utils_lua, "fun", fun_lua, "debug", debug_lua, "tarantool", init_lua, @@ -303,7 +304,6 @@ static const char *lua_modules[] = { "tap", tap_lua, "help.en_US", help_en_US_lua, "help", help_lua, - "internal.utils", utils_lua, "internal.argparse", argparse_lua, "internal.trigger", trigger_lua, "pwd", pwd_lua, diff --git a/src/lua/utils.lua b/src/lua/utils.lua index 53797e2834577e8f157a0ffc6f2ba0f8816fa389..b69d64ef459549247771420007061094e931f7bd 100644 --- a/src/lua/utils.lua +++ b/src/lua/utils.lua @@ -1,3 +1,6 @@ +local ffi = require('ffi') +local tarantool = require('tarantool') + local utils = {} -- Same as type(), but returns 'number' if 'param' is @@ -111,4 +114,34 @@ function utils.update_param_table(table, defaults) return new_table end +ffi.cdef[[ +void +__asan_poison_memory_region(void const volatile *addr, size_t size); +void +__asan_unpoison_memory_region(void const volatile *addr, size_t size); +int +__asan_address_is_poisoned(void const volatile *addr); +]] + +if tarantool.build.asan then + utils.poison_memory_region = function(start, size) + ffi.C.__asan_poison_memory_region(start, size) + end + utils.unpoison_memory_region = function(start, size) + ffi.C.__asan_unpoison_memory_region(start, size) + end + utils.memory_region_is_poisoned = function(start, size) + for i = 0, size - 1 do + if ffi.C.__asan_address_is_poisoned(start + i) == 0 then + return false + end + end + return true + end +else + utils.poison_memory_region = function() end + utils.unpoison_memory_region = function() end + utils.memory_region_is_poisoned = function() return false end +end + return utils diff --git a/test/app-luatest/buffer_test.lua b/test/app-luatest/buffer_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..46a32e2ff0ffd5b753989c8272f527faeb181ed6 --- /dev/null +++ b/test/app-luatest/buffer_test.lua @@ -0,0 +1,81 @@ +local tarantool = require('tarantool') +local buffer = require('buffer') +local utils = require('internal.utils') +local ffi = require('ffi') +local t = require('luatest') + +local g = t.group() + +g.test_poison = function() + t.skip_if(not tarantool.build.asan, 'only make sense with ASAN') + + local buf = buffer.ibuf() + local is_poisoned = function(buf, ptr) + t.assert(utils.memory_region_is_poisoned(ptr, buf:unused())) + end + + -- Test poison on allocation. -- + local ptr = buf:alloc(99) + t.assert(ptr ~= nil) + ffi.fill(ptr, 99) + is_poisoned(buf, ptr + 99) + + t.assert(buf:unused() > 133) + ptr = buf:alloc(133) + t.assert(ptr ~= nil) + ffi.fill(ptr, 133) + is_poisoned(buf, ptr + 133) + + local size = buf:unused() + 77 + ptr = buf:alloc(size) + t.assert(ptr ~= nil) + ffi.fill(ptr, size) + t.assert_gt(buf:unused(), 0) + is_poisoned(buf, ptr + size) + + -- Test poison after reset. -- + buf:reset() + ptr = buf:alloc(0) + is_poisoned(buf, ptr) + + -- Test poison on reserve. -- + ptr = buf:reserve(777) + t.assert(ptr ~= nil) + t.assert_ge(buf:unused(), 777) + ffi.fill(ptr, buf:unused()) + ptr = buf:alloc(333) + t.assert(ptr ~= nil) + ffi.fill(ptr, 333) + is_poisoned(buf, ptr + 333) + + t.assert_gt(buf:unused(), 888) + ptr = buf:reserve(333) + t.assert(ptr ~= nil) + t.assert_ge(buf:unused(), 333) + ffi.fill(ptr, buf:unused()) + ptr = buf:alloc(888) + t.assert(ptr ~= nil) + ffi.fill(ptr, 888) + is_poisoned(buf, ptr + 888) + + size = buf:unused() + 133; + ptr = buf:reserve(size) + t.assert(ptr ~= nil) + t.assert_ge(buf:unused(), size) + ffi.fill(ptr, buf:unused()) + t.assert_gt(buf:unused(), 0) + ptr = buf:alloc(size) + t.assert(ptr ~= nil) + t.assert_gt(buf:unused(), 0) + is_poisoned(buf, ptr + size) + + size = buf:unused(buf) + 221; + buf:read(337) + ptr = buf:reserve(size) + t.assert(ptr ~= nil) + t.assert_ge(buf:unused(), size) + ffi.fill(ptr, buf:unused()) + ptr = buf:alloc(size) + t.assert_gt(buf:unused(), 0) + is_poisoned(buf, ptr + size) +end diff --git a/test/box-tap/merger.test.lua b/test/box-tap/merger.test.lua index 2d313daa3a51a59a290b173dcbeff638d4eed37e..f22aaa5c5b85ffa8f0216c4620accbc369e2b6ae 100755 --- a/test/box-tap/merger.test.lua +++ b/test/box-tap/merger.test.lua @@ -44,10 +44,10 @@ local function truncated_msgpack_buffer(data, trunc) local len = data:len() local buf = buffer.ibuf() -- Ensure we have enough buffer to write len + trunc bytes. - buf:reserve(len + trunc) - local p = buf:alloc(len) + local p = buf:reserve(len + trunc) -- Ensure len bytes follows with trunc zero bytes. ffi.copy(p, data .. string.rep('\0', trunc), len + trunc) + buf:alloc(len) return buf end