From 0506ca8bdf8584858c0e462a24712c651a7e4e59 Mon Sep 17 00:00:00 2001 From: Trumeet Date: Thu, 28 Jul 2022 15:38:34 -0700 Subject: fix(libacron): memory leak when received more than one frames API:CHANGE --- client/acronc/handler_socket.c | 20 ++++++++++++++++++++ client/libacron/README.md | 10 ++++++++++ client/libacron/net.c | 23 +++++++++++++++++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/client/acronc/handler_socket.c b/client/acronc/handler_socket.c index efed127..2dfda5e 100644 --- a/client/acronc/handler_socket.c +++ b/client/acronc/handler_socket.c @@ -156,6 +156,26 @@ static void on_read(uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) { /* acronc error. Socket is working. */ ex(false); } + + LOGD("Clearing backlog."); + while (true) { + if ((r = ac_receive(ac_conn, NULL, 0, &obj))) { + LOGEV("Cannot clear backlog (%d).", r); + /* libac error. Socket is working. */ + free(buf->base); + ex(false); + return; + } + if (!obj) { + break; + } + if (cb_recv(obj)) { + /* acronc error. Socket is working. */ + ex(false); + free(buf->base); + return; + } + } } free(buf->base); diff --git a/client/libacron/README.md b/client/libacron/README.md index 5fba5c6..14d37ad 100644 --- a/client/libacron/README.md +++ b/client/libacron/README.md @@ -214,6 +214,16 @@ while (!(r = ac_receive(connection, buffer, bytes, &obj))) { } ``` +> **Note**: +> +> In some cases, the buffer may contain more than one valid WebSocket frames. In such cases, +> `ac_receive` will backlog the additional parsed messages, and they will return the second time +> calling `ac_receive` with buffer = bytes = 0. +> +> This will happen only `ac_receive` returns zero with a non-NULL obj output. If that is true, +> the client must additionally call `ac_receive` infinite times with buffer = bytes = 0, until either +> `ac_receive` returns an error or obj output is NULL. + The program can make requests using `ac_request()`: ```c diff --git a/client/libacron/net.c b/client/libacron/net.c index b393475..55b10ce 100644 --- a/client/libacron/net.c +++ b/client/libacron/net.c @@ -62,6 +62,17 @@ static bool on_message_handler(struct wic_inst *inst, } /* TODO: Only parse when fin = true? */ struct ac_result *res = &conn->result; + if (res->has_result) { + /* This flag will be cleared upon ac_receive using memset(). + * A positive flag indicates that on_message_handler was called + * more than once before wic_parse returns. This could happen due + * to more than one WebSocket frames were returned by a single read. + * To prevent the new message from being overwritten (and thus causing + * memory leaks), we return false here, making wic backlog the new message + * until the next wic_parse. */ + LOGD("Two or more frames arrived before wic_parse returns. Keeping the message."); + return false; + } int r; if ((r = deserialize(data, size, &res->obj))) { if (r == AC_E_SERIALIZER_CONTINUE) { @@ -202,12 +213,20 @@ int ac_receive(void *connection, /* In case the deserializer does not run at all. */ memset(&conn->result, 0, sizeof(struct ac_result)); - for (pos = 0U; pos < len; pos += retval) { - retval = wic_parse(inst, &ptr[pos], len - pos); + if (!len) { + retval = wic_parse(inst, NULL, 0); if (wic_get_state(inst) == WIC_STATE_CLOSED) { LOGE("Connection closed."); return AC_E_NET; } + } else { + for (pos = 0U; pos < len; pos += retval) { + retval = wic_parse(inst, &ptr[pos], len - pos); + if (wic_get_state(inst) == WIC_STATE_CLOSED) { + LOGE("Connection closed."); + return AC_E_NET; + } + } } struct ac_result *res = &conn->result; -- cgit v1.2.3