ba3a7a2
From e69b535f92eafb599329bf725d9b4c6fd5d7fded Mon Sep 17 00:00:00 2001
ba3a7a2
From: Paul Jakma <paul@jakma.org>
ba3a7a2
Date: Sat, 6 Jan 2018 19:52:10 +0000
ba3a7a2
Subject: [PATCH] bgpd/security: Fix double free of unknown attribute
ba3a7a2
ba3a7a2
Security issue: Quagga-2018-1114
ba3a7a2
See: https://www.quagga.net/security/Quagga-2018-1114.txt
ba3a7a2
ba3a7a2
It is possible for bgpd to double-free an unknown attribute. This can happen
ba3a7a2
via bgp_update_receive receiving an UPDATE with an invalid unknown attribute.
ba3a7a2
bgp_update_receive then will call bgp_attr_unintern_sub and bgp_attr_flush,
ba3a7a2
and the latter may try free an already freed unknown attr.
ba3a7a2
ba3a7a2
* bgpd/bgp_attr.c: (transit_unintern) Take a pointer to the caller's storage
ba3a7a2
  for the (struct transit *), so that transit_unintern can NULL out the
ba3a7a2
  caller's reference if the (struct transit) is freed.
ba3a7a2
  (cluster_unintern) By inspection, appears to have a similar issue.
ba3a7a2
  (bgp_attr_unintern_sub) adjust for above.
ba3a7a2
---
ba3a7a2
 bgpd/bgp_attr.c | 33 +++++++++++++++++++--------------
ba3a7a2
 bgpd/bgp_attr.h |  4 ++--
ba3a7a2
 2 files changed, 21 insertions(+), 16 deletions(-)
ba3a7a2
ba3a7a2
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
ba3a7a2
index 9564637e..0c2806b5 100644
ba3a7a2
--- a/bgpd/bgp_attr.c
ba3a7a2
+++ b/bgpd/bgp_attr.c
ba3a7a2
@@ -199,15 +199,17 @@ cluster_intern (struct cluster_list *cluster)
ba3a7a2
 }
ba3a7a2
 
ba3a7a2
 void
ba3a7a2
-cluster_unintern (struct cluster_list *cluster)
ba3a7a2
+cluster_unintern (struct cluster_list **cluster)
ba3a7a2
 {
ba3a7a2
-  if (cluster->refcnt)
ba3a7a2
-    cluster->refcnt--;
ba3a7a2
+  struct cluster_list *c = *cluster;
ba3a7a2
+  if (c->refcnt)
ba3a7a2
+    c->refcnt--;
ba3a7a2
 
ba3a7a2
-  if (cluster->refcnt == 0)
ba3a7a2
+  if (c->refcnt == 0)
ba3a7a2
     {
ba3a7a2
-      hash_release (cluster_hash, cluster);
ba3a7a2
-      cluster_free (cluster);
ba3a7a2
+      hash_release (cluster_hash, c);
ba3a7a2
+      cluster_free (c);
ba3a7a2
+      *cluster = NULL;
ba3a7a2
     }
ba3a7a2
 }
ba3a7a2
 
ba3a7a2
@@ -357,15 +359,18 @@ transit_intern (struct transit *transit)
ba3a7a2
 }
ba3a7a2
 
ba3a7a2
 void
ba3a7a2
-transit_unintern (struct transit *transit)
ba3a7a2
+transit_unintern (struct transit **transit)
ba3a7a2
 {
ba3a7a2
-  if (transit->refcnt)
ba3a7a2
-    transit->refcnt--;
ba3a7a2
+  struct transit *t = *transit;
ba3a7a2
+  
ba3a7a2
+  if (t->refcnt)
ba3a7a2
+    t->refcnt--;
ba3a7a2
 
ba3a7a2
-  if (transit->refcnt == 0)
ba3a7a2
+  if (t->refcnt == 0)
ba3a7a2
     {
ba3a7a2
-      hash_release (transit_hash, transit);
ba3a7a2
-      transit_free (transit);
ba3a7a2
+      hash_release (transit_hash, t);
ba3a7a2
+      transit_free (t);
ba3a7a2
+      *transit = NULL;
ba3a7a2
     }
ba3a7a2
 }
ba3a7a2
 
ba3a7a2
@@ -820,11 +825,11 @@ bgp_attr_unintern_sub (struct attr *attr)
ba3a7a2
       UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
ba3a7a2
       
ba3a7a2
       if (attr->extra->cluster)
ba3a7a2
-        cluster_unintern (attr->extra->cluster);
ba3a7a2
+        cluster_unintern (&attr->extra->cluster);
ba3a7a2
       UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST));
ba3a7a2
       
ba3a7a2
       if (attr->extra->transit)
ba3a7a2
-        transit_unintern (attr->extra->transit);
ba3a7a2
+        transit_unintern (&attr->extra->transit);
ba3a7a2
     }
ba3a7a2
 }
ba3a7a2
 
ba3a7a2
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
ba3a7a2
index 9ff074b2..052acc7d 100644
ba3a7a2
--- a/bgpd/bgp_attr.h
ba3a7a2
+++ b/bgpd/bgp_attr.h
ba3a7a2
@@ -187,10 +187,10 @@ extern unsigned long int attr_unknown_count (void);
ba3a7a2
 
ba3a7a2
 /* Cluster list prototypes. */
ba3a7a2
 extern int cluster_loop_check (struct cluster_list *, struct in_addr);
ba3a7a2
-extern void cluster_unintern (struct cluster_list *);
ba3a7a2
+extern void cluster_unintern (struct cluster_list **);
ba3a7a2
 
ba3a7a2
 /* Transit attribute prototypes. */
ba3a7a2
-void transit_unintern (struct transit *);
ba3a7a2
+void transit_unintern (struct transit **);
ba3a7a2
 
ba3a7a2
 /* Below exported for unit-test purposes only */
ba3a7a2
 struct bgp_attr_parser_args {
ba3a7a2
-- 
ba3a7a2
2.14.3
ba3a7a2