1 From e69b535f92eafb599329bf725d9b4c6fd5d7fded Mon Sep 17 00:00:00 2001
2 From: Paul Jakma <paul@jakma.org>
3 Date: Sat, 6 Jan 2018 19:52:10 +0000
4 Subject: [PATCH] bgpd/security: Fix double free of unknown attribute
6 Security issue: Quagga-2018-1114
7 See: https://www.quagga.net/security/Quagga-2018-1114.txt
9 It is possible for bgpd to double-free an unknown attribute. This can happen
10 via bgp_update_receive receiving an UPDATE with an invalid unknown attribute.
11 bgp_update_receive then will call bgp_attr_unintern_sub and bgp_attr_flush,
12 and the latter may try free an already freed unknown attr.
14 * bgpd/bgp_attr.c: (transit_unintern) Take a pointer to the caller's storage
15 for the (struct transit *), so that transit_unintern can NULL out the
16 caller's reference if the (struct transit) is freed.
17 (cluster_unintern) By inspection, appears to have a similar issue.
18 (bgp_attr_unintern_sub) adjust for above.
20 Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
22 bgpd/bgp_attr.c | 33 +++++++++++++++++++--------------
23 bgpd/bgp_attr.h | 4 ++--
24 2 files changed, 21 insertions(+), 16 deletions(-)
26 diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
27 index 9564637e..0c2806b5 100644
30 @@ -199,15 +199,17 @@ cluster_intern (struct cluster_list *cluster)
34 -cluster_unintern (struct cluster_list *cluster)
35 +cluster_unintern (struct cluster_list **cluster)
37 - if (cluster->refcnt)
39 + struct cluster_list *c = *cluster;
43 - if (cluster->refcnt == 0)
46 - hash_release (cluster_hash, cluster);
47 - cluster_free (cluster);
48 + hash_release (cluster_hash, c);
54 @@ -357,15 +359,18 @@ transit_intern (struct transit *transit)
58 -transit_unintern (struct transit *transit)
59 +transit_unintern (struct transit **transit)
61 - if (transit->refcnt)
63 + struct transit *t = *transit;
68 - if (transit->refcnt == 0)
71 - hash_release (transit_hash, transit);
72 - transit_free (transit);
73 + hash_release (transit_hash, t);
79 @@ -820,11 +825,11 @@ bgp_attr_unintern_sub (struct attr *attr)
80 UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
82 if (attr->extra->cluster)
83 - cluster_unintern (attr->extra->cluster);
84 + cluster_unintern (&attr->extra->cluster);
85 UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST));
87 if (attr->extra->transit)
88 - transit_unintern (attr->extra->transit);
89 + transit_unintern (&attr->extra->transit);
93 diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
94 index 9ff074b2..052acc7d 100644
97 @@ -187,10 +187,10 @@ extern unsigned long int attr_unknown_count (void);
99 /* Cluster list prototypes. */
100 extern int cluster_loop_check (struct cluster_list *, struct in_addr);
101 -extern void cluster_unintern (struct cluster_list *);
102 +extern void cluster_unintern (struct cluster_list **);
104 /* Transit attribute prototypes. */
105 -void transit_unintern (struct transit *);
106 +void transit_unintern (struct transit **);
108 /* Below exported for unit-test purposes only */
109 struct bgp_attr_parser_args {