From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id DE56C2353D for ; Mon, 25 Jun 2018 10:00:49 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 4QLacHcJDeVm for ; Mon, 25 Jun 2018 10:00:49 -0400 (EDT) Received: from smtp52.i.mail.ru (smtp52.i.mail.ru [94.100.177.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 9BD79228DA for ; Mon, 25 Jun 2018 10:00:49 -0400 (EDT) From: Vladislav Shpilevoy Subject: [tarantool-patches] [PATCH v2 1/1] iproto: protect from false-correct size in msg header Date: Mon, 25 Jun 2018 17:00:46 +0300 Message-Id: In-Reply-To: <20180625135537.GA25322@chai> References: <20180625135537.GA25322@chai> Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org Cc: kostja@tarantool.org Consider this packet: msgpack = require('msgpack') data = msgpack.encode(18400000000000000000)..'aaaaaaa' Tarantool interprets 18400000000000000000 as size of a coming iproto request, and tries with no any checks to allocate buffer of such size. It calculates needed capacity like this: capacity = start_value; while (capacity < size) capacity *= 2; Here it is possible that on i-th iteration 'capacity' < 'size', but 'capacity * 2' overflows 64 bits and becomes < 'size' again, so this loop never ends and occupies 100% CPU. Strictly speaking overflow has undefined behavior. On the original system it led to nullifying 'capacity'. Such size is improbable as a real packet gabarits, but can appear as a result of parsing of some invalid packet, first bytes of which accidentally appears to be valid MessagePack uint. This is how the bug emerged on the real system. Lets restrict the maximal packet size to 2GB. Closes #3464 --- Issue: https://github.com/tarantool/tarantool/issues/3464 Branch: https://github.com/tarantool/tarantool/tree/gerold103/gh-3464-iproto-100-cpu Changelog. - v2: move packet size check in enqueue_batch() function. src/box/iproto.cc | 19 +++++++++++++++---- test/box/net.box.result | 22 ++++++++++++++++++++++ test/box/net.box.test.lua | 10 ++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/box/iproto.cc b/src/box/iproto.cc index 0b92c316e..d69958962 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -64,7 +64,10 @@ #include "cfg.h" /* The number of iproto messages in flight */ -enum { IPROTO_MSG_MAX = 768 }; +enum { + IPROTO_MSG_MAX = 768, + IPROTO_PACKET_SIZE_MAX = 2UL * 1024 * 1024 * 1024, +}; /** * Network readahead. A signed integer to avoid @@ -581,18 +584,26 @@ iproto_enqueue_batch(struct iproto_connection *con, struct ibuf *in) { int n_requests = 0; bool stop_input = false; + const char *errmsg; while (con->parse_size && stop_input == false) { const char *reqstart = in->wpos - con->parse_size; const char *pos = reqstart; /* Read request length. */ if (mp_typeof(*pos) != MP_UINT) { + errmsg = "packet length"; +err_msgpack: cpipe_flush_input(&tx_pipe); - tnt_raise(ClientError, ER_INVALID_MSGPACK, - "packet length"); + tnt_raise(ClientError, ER_INVALID_MSGPACK, errmsg); } if (mp_check_uint(pos, in->wpos) >= 0) break; - uint32_t len = mp_decode_uint(&pos); + uint64_t len = mp_decode_uint(&pos); + if (len > IPROTO_PACKET_SIZE_MAX) { + errmsg = tt_sprintf("too big packet size in the "\ + "header: %llu", + (unsigned long long) len); + goto err_msgpack; + } const char *reqend = pos + len; if (reqend > in->wpos) break; diff --git a/test/box/net.box.result b/test/box/net.box.result index aa418b87d..7180f7f66 100644 --- a/test/box/net.box.result +++ b/test/box/net.box.result @@ -2360,6 +2360,28 @@ box.internal.collation.drop('test') space:drop() --- ... +-- +-- gh-3464: iproto hangs in 100% CPU when too big packet size +-- is received due to size_t overflow. +-- +c = net:connect(box.cfg.listen) +--- +... +data = msgpack.encode(18400000000000000000)..'aaaaaaa' +--- +... +c._transport.perform_request(nil, nil, 'inject', nil, data) +--- +- 77 +- Peer closed +... +c:close() +--- +... +test_run:grep_log('default', 'too big packet size in the header') ~= nil +--- +- true +... box.schema.user.revoke('guest', 'read,write,execute', 'universe') --- ... diff --git a/test/box/net.box.test.lua b/test/box/net.box.test.lua index 4cadd3b29..1e716ca89 100644 --- a/test/box/net.box.test.lua +++ b/test/box/net.box.test.lua @@ -961,4 +961,14 @@ c:close() box.internal.collation.drop('test') space:drop() +-- +-- gh-3464: iproto hangs in 100% CPU when too big packet size +-- is received due to size_t overflow. +-- +c = net:connect(box.cfg.listen) +data = msgpack.encode(18400000000000000000)..'aaaaaaa' +c._transport.perform_request(nil, nil, 'inject', nil, data) +c:close() +test_run:grep_log('default', 'too big packet size in the header') ~= nil + box.schema.user.revoke('guest', 'read,write,execute', 'universe') -- 2.15.1 (Apple Git-101)