CVE-2018-10756: Use After Free (CWE-416)
value: 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.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 7.8
Published: 2020-05-15T16:15Z
Repository: https://github.com/transmission/transmission
Language: C
Files changed: libtransmission/variant.c
Commit date: 2020-04-29 17:29:38 +0300
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>

Diff: diff --git a/libtransmission/variant.c b/libtransmission/variant.c
index df577f2f0..8a14001c2 100644
--- a/libtransmission/variant.c
+++ b/libtransmission/variant.c
@@ -756,11 +756,11 @@ 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;
 };
 
 static void nodeConstruct(struct SaveNode* node, tr_variant const* v, bool sort_dicts)
@@ -781,34 +781,40 @@ static void nodeConstruct(struct SaveNode* node, tr_variant const* v, bool sort_
             tmp[i].keystr = tr_quark_get_string(tmp[i].val->key, NULL);
         }
 
         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);
     }
 }
 
 /**
  * This function's previous recursive implementation was

Vulnerable code:     tr_variant sorted;
        tr_variantInitDict(&node->sorted, n);
            node->sorted.val.l.vals[i] = *tmp[i].val;
        node->sorted.val.l.count = n;
        node->v = &node->sorted;
        node->v = v;
    if (node->v == &node->sorted)
        tr_free(node->sorted.val.l.vals);
Fixed code:     tr_variant* sorted;
        node->sorted = tr_new(tr_variant, 1);
        tr_variantInitDict(node->sorted, n);
            node->sorted->val.l.vals[i] = *tmp[i].val;
        node->sorted->val.l.count = n;
        v = node->sorted;
        node->sorted = NULL;

    node->v = v;
    TR_ASSERT(node != NULL);

    if (node->sorted != NULL)
        tr_free(node->sorted->val.l.vals);
        tr_free(node->sorted);