Skip to content

Commit 13fa964

Browse files
committed
zebra: fix table heap-after-free crash
Fix a heap-after-free that causes zebra to crash even without address-sanitizer. To reproduce: > echo "100 my_table" | tee -a /etc/iproute2/rt_tables > ip route add blackhole default table 100 > ip route show table 100 > ip l add red type vrf table 100 > ip l del red > ip route del blackhole default table 100 Zebra manages routing tables for all existing Linux RT tables, regardless of whether they are assigned to a VRF interface. When a table is not assigned to any VRF, zebra arbitrarily assigns it to the default VRF, even though this is not strictly accurate (the code expects this behavior). When an RT table is created after a VRF, zebra correctly assigns the table to the VRF. However, if a VRF interface is assigned to an existing RT table, zebra does not update the table owner, which remains as the default VRF. As a result, existing routing entries remain under the default VRF, while new entries are correctly assigned to the VRF. The VRF mismatch is unexpected in the code and creates crashes and memory related issues. Furthermore, Linux does not automatically delete RT tables when they are unassigned from a VRF. It is incorrect to delete these tables from zebra. Instead, at VRF disabling, do not release the table but reassign it to the default VRF. At VRF enabling, change the table owner back to the appropriate VRF. > ==2866266==ERROR: AddressSanitizer: heap-use-after-free on address 0x606000154f54 at pc 0x7fa32474b83f bp 0x7ffe94f67d90 sp 0x7ffe94f67d88 > READ of size 1 at 0x606000154f54 thread T0 > #0 0x7fa32474b83e in rn_hash_node_const_find lib/table.c:28 > #1 0x7fa32474bab1 in rn_hash_node_find lib/table.c:28 > #2 0x7fa32474d783 in route_node_get lib/table.c:283 > #3 0x7fa3247328dd in srcdest_rnode_get lib/srcdest_table.c:231 > FRRouting#4 0x55b0e4fa8da4 in rib_find_rn_from_ctx zebra/zebra_rib.c:1957 > FRRouting#5 0x55b0e4fa8e31 in rib_process_result zebra/zebra_rib.c:1988 > FRRouting#6 0x55b0e4fb9d64 in rib_process_dplane_results zebra/zebra_rib.c:4894 > FRRouting#7 0x7fa32476689c in event_call lib/event.c:1996 > FRRouting#8 0x7fa32463b7b2 in frr_run lib/libfrr.c:1232 > FRRouting#9 0x55b0e4e6c32a in main zebra/main.c:526 > FRRouting#10 0x7fa32424fd09 in __libc_start_main ../csu/libc-start.c:308 > FRRouting#11 0x55b0e4e2d649 in _start (/usr/lib/frr/zebra+0x1a1649) > > 0x606000154f54 is located 20 bytes inside of 56-byte region [0x606000154f40,0x606000154f78) > freed by thread T0 here: > #0 0x7fa324ca9b6f in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:123 > #1 0x7fa324668d8f in qfree lib/memory.c:130 > #2 0x7fa32474c421 in route_table_free lib/table.c:126 > #3 0x7fa32474bf96 in route_table_finish lib/table.c:46 > FRRouting#4 0x55b0e4fbca3a in zebra_router_free_table zebra/zebra_router.c:191 > FRRouting#5 0x55b0e4fbccea in zebra_router_release_table zebra/zebra_router.c:214 > FRRouting#6 0x55b0e4fd428e in zebra_vrf_disable zebra/zebra_vrf.c:219 > FRRouting#7 0x7fa32476fabf in vrf_disable lib/vrf.c:326 > FRRouting#8 0x7fa32476f5d4 in vrf_delete lib/vrf.c:231 > FRRouting#9 0x55b0e4e4ad36 in interface_vrf_change zebra/interface.c:1478 > FRRouting#10 0x55b0e4e4d5d2 in zebra_if_dplane_ifp_handling zebra/interface.c:1949 > FRRouting#11 0x55b0e4e4fb89 in zebra_if_dplane_result zebra/interface.c:2268 > FRRouting#12 0x55b0e4fb9f26 in rib_process_dplane_results zebra/zebra_rib.c:4954 > FRRouting#13 0x7fa32476689c in event_call lib/event.c:1996 > FRRouting#14 0x7fa32463b7b2 in frr_run lib/libfrr.c:1232 > FRRouting#15 0x55b0e4e6c32a in main zebra/main.c:526 > FRRouting#16 0x7fa32424fd09 in __libc_start_main ../csu/libc-start.c:308 > > previously allocated by thread T0 here: > #0 0x7fa324caa037 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 > #1 0x7fa324668c4d in qcalloc lib/memory.c:105 > #2 0x7fa32474bf33 in route_table_init_with_delegate lib/table.c:38 > #3 0x7fa32474e73c in route_table_init lib/table.c:512 > FRRouting#4 0x55b0e4fbc353 in zebra_router_get_table zebra/zebra_router.c:137 > FRRouting#5 0x55b0e4fd4da0 in zebra_vrf_table_create zebra/zebra_vrf.c:358 > FRRouting#6 0x55b0e4fd3d30 in zebra_vrf_enable zebra/zebra_vrf.c:140 > FRRouting#7 0x7fa32476f9b2 in vrf_enable lib/vrf.c:286 > FRRouting#8 0x55b0e4e4af76 in interface_vrf_change zebra/interface.c:1533 > FRRouting#9 0x55b0e4e4d612 in zebra_if_dplane_ifp_handling zebra/interface.c:1968 > FRRouting#10 0x55b0e4e4fb89 in zebra_if_dplane_result zebra/interface.c:2268 > FRRouting#11 0x55b0e4fb9f26 in rib_process_dplane_results zebra/zebra_rib.c:4954 > FRRouting#12 0x7fa32476689c in event_call lib/event.c:1996 > FRRouting#13 0x7fa32463b7b2 in frr_run lib/libfrr.c:1232 > FRRouting#14 0x55b0e4e6c32a in main zebra/main.c:526 > FRRouting#15 0x7fa32424fd09 in __libc_start_main ../csu/libc-start.c:308 Fixes: d8612e6 ("zebra: Track tables allocated by vrf and cleanup") Signed-off-by: Louis Scalbert <[email protected]>
1 parent 172a2aa commit 13fa964

File tree

3 files changed

+142
-3
lines changed

3 files changed

+142
-3
lines changed

zebra/zebra_nhg.c

+63
Original file line numberDiff line numberDiff line change
@@ -2923,6 +2923,69 @@ static uint32_t proto_nhg_nexthop_active_update(struct nexthop_group *nhg)
29232923
return curr_active;
29242924
}
29252925

2926+
void nexthop_vrf_update(struct route_node *rn, struct route_entry *re,
2927+
vrf_id_t vrf_id)
2928+
{
2929+
struct nhg_hash_entry *curr_nhe, *new_nhe;
2930+
afi_t rt_afi = family2afi(rn->p.family);
2931+
struct nexthop *nexthop;
2932+
2933+
re->vrf_id = vrf_id;
2934+
2935+
/* Make a local copy of the existing nhe, so we don't work on/modify
2936+
* the shared nhe.
2937+
*/
2938+
curr_nhe = zebra_nhe_copy(re->nhe, re->nhe->id);
2939+
2940+
if (IS_ZEBRA_DEBUG_NHG_DETAIL)
2941+
zlog_debug("%s: re %p nhe %p (%pNG), curr_nhe %p", __func__, re,
2942+
re->nhe, re->nhe, curr_nhe);
2943+
2944+
/* Clear the existing id, if any: this will avoid any confusion
2945+
* if the id exists, and will also force the creation
2946+
* of a new nhe reflecting the changes we may make in this local copy.
2947+
*/
2948+
curr_nhe->id = 0;
2949+
2950+
curr_nhe->vrf_id = vrf_id;
2951+
for (ALL_NEXTHOPS(curr_nhe->nhg, nexthop)) {
2952+
if (!nexthop->ifindex)
2953+
/* change VRF ID of nexthop without interfaces
2954+
* (eg. blackhole)
2955+
*/
2956+
nexthop->vrf_id = vrf_id;
2957+
}
2958+
2959+
if (zebra_nhg_get_backup_nhg(curr_nhe)) {
2960+
for (ALL_NEXTHOPS(curr_nhe->backup_info->nhe->nhg, nexthop)) {
2961+
if (!nexthop->ifindex)
2962+
/* change VRF ID of nexthop without interfaces
2963+
* (eg. blackhole)
2964+
*/
2965+
nexthop->vrf_id = vrf_id;
2966+
}
2967+
}
2968+
2969+
/*
2970+
* Ref or create an nhe that matches the current state of the
2971+
* nexthop(s).
2972+
*/
2973+
new_nhe = zebra_nhg_rib_find_nhe(curr_nhe, rt_afi);
2974+
2975+
if (IS_ZEBRA_DEBUG_NHG_DETAIL)
2976+
zlog_debug("%s: re %p CHANGED: nhe %p (%pNG) => new_nhe %p (%pNG)",
2977+
__func__, re, re->nhe, re->nhe, new_nhe, new_nhe);
2978+
2979+
route_entry_update_nhe(re, new_nhe);
2980+
2981+
/*
2982+
* Do not need the old / copied nhe anymore since it
2983+
* was either copied over into a new nhe or not
2984+
* used at all.
2985+
*/
2986+
zebra_nhg_free(curr_nhe);
2987+
}
2988+
29262989
/*
29272990
* This function takes the start of two comparable nexthops from two different
29282991
* nexthop groups and walks them to see if they can be considered the same

zebra/zebra_nhg.h

+2
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ extern void zebra_nhg_mark_keep(void);
401401

402402
/* Nexthop resolution processing */
403403
struct route_entry; /* Forward ref to avoid circular includes */
404+
extern void nexthop_vrf_update(struct route_node *rn, struct route_entry *re,
405+
vrf_id_t vrf_id);
404406
extern int nexthop_active_update(struct route_node *rn, struct route_entry *re,
405407
struct route_entry *old_re);
406408

zebra/zebra_vrf.c

+77-3
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,46 @@ static int zebra_vrf_enable(struct vrf *vrf)
154154
return 0;
155155
}
156156

157+
/* update the VRF ID of a routing table and their routing entries */
158+
static void zebra_vrf_disable_update_vrfid(struct zebra_vrf *zvrf, afi_t afi,
159+
safi_t safi)
160+
{
161+
struct rib_table_info *info;
162+
struct route_entry *re;
163+
struct route_node *rn;
164+
bool empty_table = true;
165+
166+
/* Assign the table to the default VRF.
167+
* Although the table is not technically owned by the default VRF,
168+
* the code assumes that unassigned routing tables are
169+
* associated with the default VRF.
170+
*/
171+
info = route_table_get_info(zvrf->table[afi][safi]);
172+
info->zvrf = vrf_info_lookup(VRF_DEFAULT);
173+
174+
rn = route_top(zvrf->table[afi][safi]);
175+
if (rn)
176+
empty_table = false;
177+
while (rn) {
178+
if (!rn->info) {
179+
rn = route_next(rn);
180+
continue;
181+
}
182+
183+
/* Assign the route entries to the default VRF,
184+
* even though they are not actually owned by it.
185+
*/
186+
RNODE_FOREACH_RE (rn, re)
187+
nexthop_vrf_update(rn, re, VRF_DEFAULT);
188+
189+
rn = route_next(rn);
190+
}
191+
192+
if (empty_table)
193+
zebra_router_release_table(zvrf, zvrf->table_id, afi, safi);
194+
zvrf->table[afi][safi] = NULL;
195+
}
196+
157197
/* Callback upon disabling a VRF. */
158198
static int zebra_vrf_disable(struct vrf *vrf)
159199
{
@@ -216,9 +256,15 @@ static int zebra_vrf_disable(struct vrf *vrf)
216256
* we no-longer need this pointer.
217257
*/
218258
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
219-
zebra_router_release_table(zvrf, zvrf->table_id, afi,
220-
safi);
221-
zvrf->table[afi][safi] = NULL;
259+
if (!zvrf->table[afi][safi] ||
260+
vrf->vrf_id == VRF_DEFAULT) {
261+
zebra_router_release_table(zvrf, zvrf->table_id,
262+
afi, safi);
263+
zvrf->table[afi][safi] = NULL;
264+
continue;
265+
}
266+
267+
zebra_vrf_disable_update_vrfid(zvrf, afi, safi);
222268
}
223269
}
224270

@@ -349,14 +395,42 @@ static void zebra_rnhtable_node_cleanup(struct route_table *table,
349395
static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi,
350396
safi_t safi)
351397
{
398+
vrf_id_t vrf_id = zvrf->vrf->vrf_id;
399+
struct rib_table_info *info;
400+
struct route_entry *re;
352401
struct route_node *rn;
353402
struct prefix p;
354403

355404
assert(!zvrf->table[afi][safi]);
356405

406+
/* Attempt to retrieve the Linux routing table using zvrf->table_id.
407+
* If the table was created before the VRF, it will already exist.
408+
* Otherwise, create a new table.
409+
*/
357410
zvrf->table[afi][safi] =
358411
zebra_router_get_table(zvrf, zvrf->table_id, afi, safi);
359412

413+
/* If the table existed before the VRF was created, info->zvrf was
414+
* referring to the default VRF.
415+
* Assign the table to the new VRF.
416+
* Note: FRR does not allow multiple VRF interfaces to be created with the
417+
* same table ID.
418+
*/
419+
info = route_table_get_info(zvrf->table[afi][safi]);
420+
info->zvrf = zvrf;
421+
422+
/* If the table existed before the VRF was created, their routing entries
423+
* was owned by the default VRF.
424+
* Re-assign all the routing entries to the new VRF.
425+
*/
426+
for (rn = route_top(zvrf->table[afi][safi]); rn; rn = route_next(rn)) {
427+
if (!rn->info)
428+
continue;
429+
430+
RNODE_FOREACH_RE (rn, re)
431+
nexthop_vrf_update(rn, re, vrf_id);
432+
}
433+
360434
memset(&p, 0, sizeof(p));
361435
p.family = afi2family(afi);
362436

0 commit comments

Comments
 (0)