pimd: Prevent use after free from pim_mlag_up_peer_deref

There exists a chain of events where calling pim_mlag_up_peer_deref
can free the up pointer.  Prevent a use after free by returning
the up pointer as needed and checking to make sure we are
ok.

Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
This commit is contained in:
Donald Sharp 2020-03-05 09:26:11 -05:00
parent 3c685e64ff
commit b7e40944a2

View File

@ -210,17 +210,20 @@ static void pim_mlag_up_peer_add(struct mlag_mroute_add *msg)
* - if a local entry continues to exisy and has a MLAG OIF DF election * - if a local entry continues to exisy and has a MLAG OIF DF election
* is re-run (at the end of which the local entry will be the DF). * is re-run (at the end of which the local entry will be the DF).
*/ */
static void pim_mlag_up_peer_deref(struct pim_instance *pim, static struct pim_upstream *pim_mlag_up_peer_deref(struct pim_instance *pim,
struct pim_upstream *up) struct pim_upstream *up)
{ {
if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags)) if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags))
return; return up;
PIM_UPSTREAM_FLAG_UNSET_MLAG_PEER(up->flags); PIM_UPSTREAM_FLAG_UNSET_MLAG_PEER(up->flags);
up = pim_upstream_del(pim, up, __func__); up = pim_upstream_del(pim, up, __func__);
if (up) if (up)
pim_mlag_up_df_role_elect(pim, up); pim_mlag_up_df_role_elect(pim, up);
return up;
} }
static void pim_mlag_up_peer_del(struct mlag_mroute_del *msg) static void pim_mlag_up_peer_del(struct mlag_mroute_del *msg)
{ {
struct pim_upstream *up; struct pim_upstream *up;
@ -256,7 +259,7 @@ static void pim_mlag_up_peer_del(struct mlag_mroute_del *msg)
return; return;
} }
pim_mlag_up_peer_deref(pim, up); (void)pim_mlag_up_peer_deref(pim, up);
} }
/* When we lose connection to the local MLAG daemon we can drop all peer /* When we lose connection to the local MLAG daemon we can drop all peer
@ -300,12 +303,13 @@ static void pim_mlag_up_peer_del_all(void)
up = listnode_head(temp); up = listnode_head(temp);
listnode_delete(temp, up); listnode_delete(temp, up);
pim_mlag_up_peer_deref(pim, up); up = pim_mlag_up_peer_deref(pim, up);
/* /*
* This is the deletion of the reference added * This is the deletion of the reference added
* above * above
*/ */
pim_upstream_del(pim, up, __func__); if (up)
pim_upstream_del(pim, up, __func__);
} }
} }