From 6318efa7987b4f0531a9401614b0c717e81b75b9 Mon Sep 17 00:00:00 2001 From: "Paul J. Davis" Date: Tue, 30 Sep 2014 17:03:37 -0500 Subject: [PATCH] Fix memory leak when encoding bare bignums This fixes a leak when encoding a bare bignum. Technically it would be possible to hit this memory leak randomly with bignums in objects but the chances are highly unlikely. Thanks to @miriampena for the issue. Fixes #69 --- c_src/encoder.c | 23 +++++++++++++--------- test/jiffy_14_bignum_memory_leak.erl | 29 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 test/jiffy_14_bignum_memory_leak.erl diff --git a/c_src/encoder.c b/c_src/encoder.c index ac4935d..471cfcc 100644 --- a/c_src/encoder.c +++ b/c_src/encoder.c @@ -205,17 +205,22 @@ enc_unknown(Encoder* e, ERL_NIF_TERM value) // a bignum might produce when encoded. e->iosize += e->i + 16; - // Reinitialize our binary for the next buffer. - e->curr = bin; - if(!enif_alloc_binary(BIN_INC_SIZE, e->curr)) { - return 0; - } + // Reinitialize our binary for the next buffer if we + // used any data in the buffer. If we haven't used any + // bytes in the buffer then we can safely reuse it + // for anything following the unknown value. + if(e->i > 0) { + e->curr = bin; + if(!enif_alloc_binary(BIN_INC_SIZE, e->curr)) { + return 0; + } - memset(e->curr->data, 0, e->curr->size); + memset(e->curr->data, 0, e->curr->size); - e->p = (char*) e->curr->data; - e->u = (unsigned char*) e->curr->data; - e->i = 0; + e->p = (char*) e->curr->data; + e->u = (unsigned char*) e->curr->data; + e->i = 0; + } return 1; } diff --git a/test/jiffy_14_bignum_memory_leak.erl b/test/jiffy_14_bignum_memory_leak.erl new file mode 100644 index 0000000..df3f4df --- /dev/null +++ b/test/jiffy_14_bignum_memory_leak.erl @@ -0,0 +1,29 @@ +% This file is part of Jiffy released under the MIT license. +% See the LICENSE file for more information. + +-module(jiffy_14_bignum_memory_leak). + +-include_lib("eunit/include/eunit.hrl"). + + +bignum_encoding_leak_test_() -> + run_gc(), + Before = erlang:memory(binary), + encode_bignums(1000000), + run_gc(), + After = erlang:memory(binary), + ?_assert(After - Before < 100000). + + +run_gc() -> + [erlang:garbage_collect(Pid) || Pid <- erlang:processes()]. + + +encode_bignums(N) -> + {_, Ref} = spawn_monitor(fun() -> + [jiffy:encode(1072502107250210725021072502) || _ <- lists:seq(1, N)] + end), + receive + {'DOWN', Ref, process, _, _} -> + ok + end.