CVE-2021-36080: Double Free (CWE-415)
value: GNU LibreDWG 0.12.3.4163 through 0.12.3.4191 has a double-free in bit_chain_free (called from dwg_encode_MTEXT and dwg_encode_add_object).
CWE description: The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
CVSS v3 base score: 8.8
Published: 2021-07-01T03:15Z
Repository: https://github.com/LibreDWG/libredwg
Language: C
Files changed: src/encode.c
Commit date: 2021-03-08 09:08:17 +0100
Commit message: encode: avoid hdl_dat double-free

In case of an handle overflow, such as num_reactors.
Fixes oss-fuzz issue 31724.

Diff: diff --git a/src/encode.c b/src/encode.c
index c555c8aae..4af17c622 100644
--- a/src/encode.c
+++ b/src/encode.c
@@ -729,10 +729,11 @@ static bool env_var_checked_p;
         ENCODE_COMMON_HANDLES                                                 \
         obj_flush_hdlstream (obj, dat, hdl_dat); /* common */                 \
         obj_flush_hdlstream (obj, dat, &dat1); /* special accumulated */      \
         bit_chain_free (&dat1);                                               \
         bit_chain_free (&dat2);                                               \
+        *hdl_dat = *dat;                                                      \
         hdl_dat = dat;                                                        \
       }                                                                       \
     else                                                                      \
       {                                                                       \
         if (hdl_dat != dat)                                                   \
@@ -828,42 +829,47 @@ EXPORT long dwg_add_##token (Dwg_Data * dwg)     \
                                  Dwg_Object *restrict obj)                    \
   {                                                                           \
     int error;                                                                \
     Bit_Chain _hdl_dat = { 0 };                                               \
     Bit_Chain *hdl_dat = &_hdl_dat; /* a new copy */                          \
-    Bit_Chain *str_dat = dat; /* a ref */                                     \
+    Bit_Chain *str_dat = dat;       /* a ref */                               \
     LOG_INFO ("Encode entity " #token "\n");                                  \
     bit_chain_init_dat (hdl_dat, 128, dat);                                   \
     error = dwg_encode_entity (obj, dat, hdl_dat, str_dat);                   \
     if (error)                                                                \
       {                                                                       \
-        if (hdl_dat != dat)                                                   \
+        LOG_HANDLE ("Early DWG_ENTITY exit\n");                               \
+        if (hdl_dat != dat && hdl_dat->chain != dat->chain)                   \
           bit_chain_free (hdl_dat);                                           \
         return error;                                                         \
       }                                                                       \
     error = dwg_encode_##token##_private (dat, hdl_dat, str_dat, obj);        \
-    if (error & DWG_ERR_VALUEOUTOFBOUNDS && hdl_dat != dat)                   \
-      bit_chain_free (hdl_dat);                                               \
+    if (error & DWG_ERR_VALUEOUTOFBOUNDS && hdl_dat != dat                    \
+        && hdl_dat->chain != dat->chain)                                      \
+      {                                                                       \
+        LOG_HANDLE ("VALUEOUTOFBOUNDS bypassed DWG_ENTITY_END\n");            \
+        /*bit_chain_free (hdl_dat);*/                                         \
+      }                                                                       \
     return error;                                                             \
   }                                                                           \
   static int dwg_encode_##token##_private (                                   \
-        Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat,               \
-        Dwg_Object *restrict obj)                                             \
+      Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat,                 \
+      Dwg_Object *restrict obj)                                               \
   {                                                                           \
     int error = 0;                                                            \
     BITCODE_BL vcount, rcount3, rcount4;                                      \
     Dwg_Data *dwg = obj->parent;                                              \
     Dwg_Object_Entity *_ent = obj->tio.entity;                                \
-    Dwg_Entity_##token *_obj = _ent->tio.token;                               \
+    Dwg_Entity_##token *_obj = _ent->tio.token;
 
 #define DWG_ENTITY_END                                                        \
     if (hdl_dat->byte > dat->byte)                                            \
       {                                                                       \
         dat->byte = hdl_dat->byte;                                            \
         dat->bit = hdl_dat->bit;                                              \
       }                                                                       \
-    if (hdl_dat != dat)                                                       \
+    if (hdl_dat != dat && hdl_dat->chain != dat->chain)                       \
       bit_chain_free (hdl_dat);                                               \
     return error;                                                             \
   }
 
 /** Returns -1 if not added, else returns the new objid.
@@ -889,11 +895,12 @@ EXPORT long dwg_add_##token (Dwg_Data * dwg)     \
         if (hdl_dat != dat)                                                   \
           bit_chain_free (hdl_dat);                                           \
         return error;                                                         \
       }                                                                       \
     error = dwg_encode_##token##_private (dat, hdl_dat, str_dat, obj);        \
-    if (error & DWG_ERR_VALUEOUTOFBOUNDS && hdl_dat != dat)                   \
+    if (error & DWG_ERR_VALUEOUTOFBOUNDS && hdl_dat != dat                    \
+        && hdl_dat->chain != dat->chain)                                      \
       bit_chain_free (hdl_dat);                                               \
     return error;                                                             \
   }                                                                           \
   static int dwg_encode_##token##_private (                                   \
         Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat,               \
@@ -913,11 +920,11 @@ EXPORT long dwg_add_##token (Dwg_Data * dwg)     \
     if (hdl_dat->byte > dat->byte)                                            \
       {                                                                       \
         dat->byte = hdl_dat->byte;                                            \
         dat->bit = hdl_dat->bit;                                              \
       }                                                                       \
-    if (hdl_dat != dat)                                                       \
+    if (hdl_dat != dat && hdl_dat->chain != dat->chain)                       \
       bit_chain_free (hdl_dat);                                               \
     return error;                                                             \
   }
 
 #define ENT_REACTORS(code)                                                    \

Vulnerable code:     Bit_Chain *str_dat = dat; /* a ref */                                     \
        if (hdl_dat != dat)                                                   \
    if (error & DWG_ERR_VALUEOUTOFBOUNDS && hdl_dat != dat)                   \
      bit_chain_free (hdl_dat);                                               \
        Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat,               \
        Dwg_Object *restrict obj)                                             \
    Dwg_Entity_##token *_obj = _ent->tio.token;                               \
    if (hdl_dat != dat)                                                       \
    if (error & DWG_ERR_VALUEOUTOFBOUNDS && hdl_dat != dat)                   \
    if (hdl_dat != dat)                                                       \
Fixed code:         *hdl_dat = *dat;                                                      \
    Bit_Chain *str_dat = dat;       /* a ref */                               \
        LOG_HANDLE ("Early DWG_ENTITY exit\n");                               \
        if (hdl_dat != dat && hdl_dat->chain != dat->chain)                   \
    if (error & DWG_ERR_VALUEOUTOFBOUNDS && hdl_dat != dat                    \
        && hdl_dat->chain != dat->chain)                                      \
      {                                                                       \
        LOG_HANDLE ("VALUEOUTOFBOUNDS bypassed DWG_ENTITY_END\n");            \
        /*bit_chain_free (hdl_dat);*/                                         \
      }                                                                       \
      Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat,                 \
      Dwg_Object *restrict obj)                                               \
    Dwg_Entity_##token *_obj = _ent->tio.token;
    if (hdl_dat != dat && hdl_dat->chain != dat->chain)                       \
    if (error & DWG_ERR_VALUEOUTOFBOUNDS && hdl_dat != dat                    \
        && hdl_dat->chain != dat->chain)                                      \
    if (hdl_dat != dat && hdl_dat->chain != dat->chain)                       \