aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrumeet <yuuta@yuuta.moe>2022-07-28 15:38:34 -0700
committerTrumeet <yuuta@yuuta.moe>2022-07-28 15:38:34 -0700
commit0506ca8bdf8584858c0e462a24712c651a7e4e59 (patch)
treee8fd11d7fe7615536ec3f244bd5f90d39b396450
parent35caf0da7fbf4ece60def1df1d88d34bf16185aa (diff)
downloadacron-0506ca8bdf8584858c0e462a24712c651a7e4e59.tar
acron-0506ca8bdf8584858c0e462a24712c651a7e4e59.tar.gz
acron-0506ca8bdf8584858c0e462a24712c651a7e4e59.tar.bz2
acron-0506ca8bdf8584858c0e462a24712c651a7e4e59.zip
fix(libacron): memory leak when received more than one frames
API:CHANGE
-rw-r--r--client/acronc/handler_socket.c20
-rw-r--r--client/libacron/README.md10
-rw-r--r--client/libacron/net.c23
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;