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;
 }

Reply via email to