Vulnerability: Use-after-free in libtransmission/variant.c in Transmission before 3.00 allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted torrent file.
Vulnerability ID: CVE-2018-10756
CWE: CWE-416
Severity score: 6.4
Project: https://github.com/transmission/transmission
Published: 2020-05-15
File: libtransmission/variant.c
Commit message: CVE-2018-10756: Fix heap-use-after-free in tr_variantWalk

In libtransmission/variant.c, function tr_variantWalk, when the variant
stack is reallocated, a pointer to the previously allocated memory
region is kept. This address is later accessed (heap use-after-free)
while walking back down the stack, causing the application to crash.
The application can be any application which uses libtransmission, such
as transmission-daemon, transmission-gtk, transmission-show, etc.

Reported-by: Tom Richards <tom@tomrichards.net>
Commit author: Mike Gelfand

Vulnerable version (before fix):
 static int compareKeyIndex(void const* va, void const* vb) struct SaveNode {     tr_variant const* v;     tr_variant sorted;     size_t childIndex;     bool isVisited; }; static void nodeConstruct(struct SaveNode* node, tr_variant const* v, bool sort_          qsort(tmp, n, sizeof(struct KeyIndex), compareKeyIndex);          tr_variantInitDict(&node->sorted, n);          for (size_t i = 0; i < n; ++i)         {             node->sorted.val.l.vals[i] = *tmp[i].val;         }          node->sorted.val.l.count = n;          tr_free(tmp);          node->v = &node->sorted;     }     else     {         node->v = v;     } }  static void nodeDestruct(struct SaveNode* node) {     if (node->v == &node->sorted)     {         tr_free(node->sorted.val.l.vals);     } } 

Fixed version (after fix):
 static int compareKeyIndex(void const* va, void const* vb) struct SaveNode {     tr_variant const* v;     tr_variant* sorted;     size_t childIndex;     bool isVisited; }; static void nodeConstruct(struct SaveNode* node, tr_variant const* v, bool sort_          qsort(tmp, n, sizeof(struct KeyIndex), compareKeyIndex);          node->sorted = tr_new(tr_variant, 1);         tr_variantInitDict(node->sorted, n);          for (size_t i = 0; i < n; ++i)         {             node->sorted->val.l.vals[i] = *tmp[i].val;         }          node->sorted->val.l.count = n;          tr_free(tmp);          v = node->sorted;     }     else     {         node->sorted = NULL;     }      node->v = v; }  static void nodeDestruct(struct SaveNode* node) {     TR_ASSERT(node != NULL);      if (node->sorted != NULL)     {         tr_free(node->sorted->val.l.vals);         tr_free(node->sorted);     } } 

Patch:
patch: @@ -758,7 +758,7 @@ static int compareKeyIndex(void const* va, void const* vb)
 struct SaveNode
 {
     tr_variant const* v;
-    tr_variant sorted;
+    tr_variant* sorted;
     size_t childIndex;
     bool isVisited;
 };
@@ -783,30 +783,36 @@ static void nodeConstruct(struct SaveNode* node, tr_variant const* v, bool sort_
 
         qsort(tmp, n, sizeof(struct KeyIndex), compareKeyIndex);
 
-        tr_variantInitDict(&node->sorted, n);
+        node->sorted = tr_new(tr_variant, 1);
+        tr_variantInitDict(node->sorted, n);
 
         for (size_t i = 0; i < n; ++i)
         {
-            node->sorted.val.l.vals[i] = *tmp[i].val;
+            node->sorted->val.l.vals[i] = *tmp[i].val;
         }
 
-        node->sorted.val.l.count = n;
+        node->sorted->val.l.count = n;
 
         tr_free(tmp);
 
-        node->v = &node->sorted;
+        v = node->sorted;
     }
     else
     {
-        node->v = v;
+        node->sorted = NULL;
     }
+
+    node->v = v;
 }
 
 static void nodeDestruct(struct SaveNode* node)
 {
-    if (node->v == &node->sorted)
+    TR_ASSERT(node != NULL);
+
+    if (node->sorted != NULL)
     {
-        tr_free(node->sorted.val.l.vals);
+        tr_free(node->sorted->val.l.vals);
+        tr_free(node->sorted);
     }
 }
path: libtransmission/variant.c