This is an automated email from the ASF dual-hosted git repository. nickva pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/couchdb-jiffy.git
commit 1f412fc1c9f4b1fdc61d5dbd0ed962c842c565d5 Author: Nick Vatamaniuc <[email protected]> AuthorDate: Fri Jun 12 18:00:05 2026 -0400 Fix unitialized var access on allocation failure Fix two places where we didn't handle allocation failures. While `enif_alloc_resource()` blows up on allocation failure, `enif_alloc()` and `enif_alloc_binary()` don't. During encoder and decoder resource creation if small (a few KB) mallocs fail we could end up calling the resource destructor with some uninitalized flags like `have_buffer`. To prevent this class of errors in the future opt to memset the encoder and decoder structure so new fields in the future are by default 0-initialized. Thanks to @qzhuyan (William) in https://github.com/davisp/jiffy/pull/296 for pointing this issue out and providing a fix. --- c_src/decoder.c | 28 ++++++++++++++-------------- c_src/encoder.c | 16 ++++------------ 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/c_src/decoder.c b/c_src/decoder.c index 05524f9..b6b57a3 100644 --- a/c_src/decoder.c +++ b/c_src/decoder.c @@ -49,8 +49,8 @@ typedef struct { int st_top; } Decoder; -// Returns an allocated resource or crashes the VM. No need to -// check return pointer for NULL +// Returns an allocated resource or NULL if the term stack +// cannot be allocated static Decoder* dec_new(ErlNifEnv* env) { @@ -60,23 +60,21 @@ dec_new(ErlNifEnv* env) assert(d != NULL); int i; - d->atoms = st; + // Zero everything and only set non-0 fields + memset(d, 0, sizeof(*d)); + d->atoms = st; d->bytes_per_red = DEFAULT_BYTES_PER_REDUCTION; - d->is_partial = 0; - d->return_maps = 0; - d->return_trailer = 0; - d->dedupe_keys = 0; - d->copy_strings = 0; d->null_term = d->atoms->atom_null; - - d->p = NULL; d->len = -1; - d->i = 0; d->st_data = (char*) enif_alloc(STACK_SIZE_INC); + if(d->st_data == NULL) { + enif_release_resource(d); + return NULL; + } + d->st_size = STACK_SIZE_INC; - d->st_top = 0; for(i = 0; i < d->st_size; i++) { d->st_data[i] = st_invalid; @@ -468,9 +466,11 @@ decode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_badarg(env); } - // If allocation fails VM will crash. d = dec_new(env); - assert(d != NULL); + if(d == NULL) { + return enif_make_tuple2(env, st->atom_error, + make_atom(env, "internal_error")); + } tmp_argv[0] = argv[0]; tmp_argv[1] = enif_make_resource(env, d); diff --git a/c_src/encoder.c b/c_src/encoder.c index 79ea956..413ce79 100644 --- a/c_src/encoder.c +++ b/c_src/encoder.c @@ -162,29 +162,21 @@ enc_new(ErlNifEnv* env) jiffy_st* st = (jiffy_st*) enif_priv_data(env); Encoder* e = enif_alloc_resource(st->res_enc, sizeof(Encoder)); + // Zero everything so enc_destroy() never sees an uninitialized + // have_buffer if we bail out before the encoder is fully built + memset(e, 0, sizeof(*e)); + e->atoms = st; e->bytes_per_red = DEFAULT_BYTES_PER_REDUCTION; - e->uescape = 0; - e->pretty = 0; - e->use_nil = 0; - e->escape_forward_slashes = 0; - e->shiftcnt = 0; - e->count = 0; - - e->iosize = 0; e->iolist = enif_make_list(env, 0); - e->partial_output = 0; - if(!enif_alloc_binary(BIN_INC_SIZE, &e->buffer)) { enif_release_resource(e); return NULL; } e->have_buffer = 1; - e->p = e->buffer.data; - e->i = 0; return e; }
