Skip to content

Commit 2b9038b

Browse files
committed
bgpd: Prevent use after free of peer structure
When changing the peers sockunion structure the bgp->peer list was not being updated properly. Since the peer's su is being used for a sorted insert then the change of it requires that the value be pulled out of the bgp->peer list and then put back into as well. Additionally ensure that the hash is always released on peer deletion. Lead to this from this decode in a address sanitizer run. ================================================================= ==30778==ERROR: AddressSanitizer: heap-use-after-free on address 0x62a0000d8440 at pc 0x7f48c9c5c547 bp 0x7ffcba272cb0 sp 0x7ffcba272ca8 READ of size 2 at 0x62a0000d8440 thread T0 #0 0x7f48c9c5c546 in sockunion_same lib/sockunion.c:425 #1 0x55cfefe3000f in peer_hash_same bgpd/bgpd.c:890 #2 0x7f48c9bde039 in hash_release lib/hash.c:209 #3 0x55cfefe3373f in bgp_peer_conf_if_to_su_update bgpd/bgpd.c:1541 #4 0x55cfefd0be7a in bgp_stop bgpd/bgp_fsm.c:1631 #5 0x55cfefe4028f in peer_delete bgpd/bgpd.c:2362 #6 0x55cfefdd5e97 in no_neighbor_interface_config bgpd/bgp_vty.c:4267 #7 0x7f48c9b9d160 in cmd_execute_command_real lib/command.c:949 #8 0x7f48c9ba1112 in cmd_execute_command lib/command.c:1009 #9 0x7f48c9ba1573 in cmd_execute lib/command.c:1162 #10 0x7f48c9c87402 in vty_command lib/vty.c:526 #11 0x7f48c9c87832 in vty_execute lib/vty.c:1291 #12 0x7f48c9c8e741 in vtysh_read lib/vty.c:2130 #13 0x7f48c9c7a66d in thread_call lib/thread.c:1585 #14 0x7f48c9bf64e7 in frr_run lib/libfrr.c:1123 #15 0x55cfefc75a15 in main bgpd/bgp_main.c:540 #16 0x7f48c96b009a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a) #17 0x55cfefc787f9 in _start (/usr/lib/frr/bgpd+0xe27f9) 0x62a0000d8440 is located 576 bytes inside of 23376-byte region [0x62a0000d8200,0x62a0000ddd50) freed by thread T0 here: #0 0x7f48c9eb9fb0 in __interceptor_free (/lib/x86_64-linux-gnu/libasan.so.5+0xe8fb0) #1 0x55cfefe3fe42 in peer_free bgpd/bgpd.c:1113 #2 0x55cfefe3fe42 in peer_unlock_with_caller bgpd/bgpd.c:1144 #3 0x55cfefe4092e in peer_delete bgpd/bgpd.c:2457 #4 0x55cfefdd5e97 in no_neighbor_interface_config bgpd/bgp_vty.c:4267 #5 0x7f48c9b9d160 in cmd_execute_command_real lib/command.c:949 #6 0x7f48c9ba1112 in cmd_execute_command lib/command.c:1009 #7 0x7f48c9ba1573 in cmd_execute lib/command.c:1162 #8 0x7f48c9c87402 in vty_command lib/vty.c:526 #9 0x7f48c9c87832 in vty_execute lib/vty.c:1291 #10 0x7f48c9c8e741 in vtysh_read lib/vty.c:2130 #11 0x7f48c9c7a66d in thread_call lib/thread.c:1585 #12 0x7f48c9bf64e7 in frr_run lib/libfrr.c:1123 #13 0x55cfefc75a15 in main bgpd/bgp_main.c:540 #14 0x7f48c96b009a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a) Signed-off-by: Donald Sharp <[email protected]>
1 parent 69a6233 commit 2b9038b

File tree

1 file changed

+61
-9
lines changed

1 file changed

+61
-9
lines changed

bgpd/bgpd.c

+61-9
Original file line numberDiff line numberDiff line change
@@ -1591,15 +1591,18 @@ void bgp_peer_conf_if_to_su_update(struct peer *peer)
15911591
struct interface *ifp;
15921592
int prev_family;
15931593
int peer_addr_updated = 0;
1594+
struct listnode *node;
1595+
union sockunion old_su;
15941596

1597+
/*
1598+
* This function is only ever needed when FRR an interface
1599+
* based peering, so this simple test will tell us if
1600+
* we are in an interface based configuration or not
1601+
*/
15951602
if (!peer->conf_if)
15961603
return;
15971604

1598-
/*
1599-
* Our peer structure is stored in the bgp->peerhash
1600-
* release it before we modify anything.
1601-
*/
1602-
hash_release(peer->bgp->peerhash, peer);
1605+
old_su = peer->su;
16031606

16041607
prev_family = peer->su.sa.sa_family;
16051608
if ((ifp = if_lookup_by_name(peer->conf_if, peer->bgp->vrf_id))) {
@@ -1642,9 +1645,48 @@ void bgp_peer_conf_if_to_su_update(struct peer *peer)
16421645
}
16431646

16441647
/*
1645-
* Since our su changed we need to del/add peer to the peerhash
1648+
* If they are the same, nothing to do here, move along
16461649
*/
1647-
(void)hash_get(peer->bgp->peerhash, peer, hash_alloc_intern);
1650+
if (!sockunion_same(&old_su, &peer->su)) {
1651+
union sockunion new_su = peer->su;
1652+
struct bgp *bgp = peer->bgp;
1653+
1654+
/*
1655+
* Our peer structure is stored in the bgp->peerhash
1656+
* release it before we modify anything in both the
1657+
* hash and the list. But *only* if the peer
1658+
* is in the bgp->peerhash as that on deletion
1659+
* we call bgp_stop which calls this function :(
1660+
* so on deletion let's remove from the list first
1661+
* and then do the deletion preventing this from
1662+
* being added back on the list below when we
1663+
* fail to remove it up here.
1664+
*/
1665+
1666+
/*
1667+
* listnode_lookup just scans the list
1668+
* for the peer structure so it's safe
1669+
* to use without modifying the su
1670+
*/
1671+
node = listnode_lookup(bgp->peer, peer);
1672+
if (node) {
1673+
/*
1674+
* Let's reset the peer->su release and
1675+
* reset it and put it back. We have to
1676+
* do this because hash_release will
1677+
* scan through looking for a matching
1678+
* su if needed.
1679+
*/
1680+
peer->su = old_su;
1681+
hash_release(peer->bgp->peerhash, peer);
1682+
listnode_delete(peer->bgp->peer, peer);
1683+
1684+
peer->su = new_su;
1685+
(void)hash_get(peer->bgp->peerhash, peer,
1686+
hash_alloc_intern);
1687+
listnode_add_sort(peer->bgp->peer, peer);
1688+
}
1689+
}
16481690
}
16491691

16501692
void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi, safi_t safi)
@@ -1794,6 +1836,7 @@ struct peer *peer_create_accept(struct bgp *bgp)
17941836

17951837
peer = peer_lock(peer); /* bgp peer list reference */
17961838
listnode_add_sort(bgp->peer, peer);
1839+
(void)hash_get(bgp->peerhash, peer, hash_alloc_intern);
17971840

17981841
return peer;
17991842
}
@@ -2486,9 +2529,16 @@ int peer_delete(struct peer *peer)
24862529
/* Delete from all peer list. */
24872530
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
24882531
&& (pn = listnode_lookup(bgp->peer, peer))) {
2489-
peer_unlock(peer); /* bgp peer list reference */
2532+
/*
2533+
* Removing from the list node first because
2534+
* peer_unlock *can* call peer_delete( I know,
2535+
* I know ). So let's remove it and in
2536+
* the su recalculate function we'll ensure
2537+
* it's in there or not.
2538+
*/
24902539
list_delete_node(bgp->peer, pn);
24912540
hash_release(bgp->peerhash, peer);
2541+
peer_unlock(peer); /* bgp peer list reference */
24922542
}
24932543

24942544
/* Buffers. */
@@ -3768,8 +3818,10 @@ int bgp_delete(struct bgp *bgp)
37683818
for (ALL_LIST_ELEMENTS(bgp->group, node, next, group))
37693819
peer_group_delete(group);
37703820

3771-
for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer))
3821+
while (listcount(bgp->peer)) {
3822+
peer = listnode_head(bgp->peer);
37723823
peer_delete(peer);
3824+
}
37733825

37743826
if (bgp->peer_self && !IS_BGP_INSTANCE_HIDDEN(bgp)) {
37753827
peer_delete(bgp->peer_self);

0 commit comments

Comments
 (0)