When using flowtable llentrys can outlive the interface with which they're associated

at which the lle_tbl pointer points to freed memory and the llt_free pointer is no longer
valid.

Move the free pointer in to the llentry itself and update the initalization sites.

MFC after:	2 weeks
This commit is contained in:
kmacy 2012-02-23 18:21:37 +00:00
parent ffbbf1cb2c
commit a99e9d281d
3 changed files with 36 additions and 39 deletions

View File

@ -106,7 +106,6 @@ struct llentry {
("negative refcnt %d", (lle)->lle_refcnt)); \
(lle)->lle_refcnt++; \
} while (0)
#define LLE_REMREF(lle) do { \
LLE_WLOCK_ASSERT(lle); \
KASSERT((lle)->lle_refcnt > 1, \
@ -116,7 +115,7 @@ struct llentry {
#define LLE_FREE_LOCKED(lle) do { \
if ((lle)->lle_refcnt <= 1) \
(lle)->lle_tbl->llt_free((lle)->lle_tbl, (lle));\
(lle)->lle_free((lle)->lle_tbl, (lle));\
else { \
(lle)->lle_refcnt--; \
LLE_WUNLOCK(lle); \
@ -152,7 +151,6 @@ struct lltable {
int llt_af;
struct ifnet *llt_ifp;
void (*llt_free)(struct lltable *, struct llentry *);
void (*llt_prefix_free)(struct lltable *,
const struct sockaddr *prefix,
const struct sockaddr *mask,

View File

@ -1260,6 +1260,20 @@ struct in_llentry {
struct sockaddr_in l3_addr4;
};
/*
* Deletes an address from the address table.
* This function is called by the timer functions
* such as arptimer() and nd6_llinfo_timer(), and
* the caller does the locking.
*/
static void
in_lltable_free(struct lltable *llt, struct llentry *lle)
{
LLE_WUNLOCK(lle);
LLE_LOCK_DESTROY(lle);
free(lle, M_LLTABLE);
}
static struct llentry *
in_lltable_new(const struct sockaddr *l3addr, u_int flags)
{
@ -1277,25 +1291,11 @@ in_lltable_new(const struct sockaddr *l3addr, u_int flags)
lle->base.la_expire = time_uptime; /* mark expired */
lle->l3_addr4 = *(const struct sockaddr_in *)l3addr;
lle->base.lle_refcnt = 1;
lle->base.lle_free = in_lltable_free;
LLE_LOCK_INIT(&lle->base);
return &lle->base;
}
/*
* Deletes an address from the address table.
* This function is called by the timer functions
* such as arptimer() and nd6_llinfo_timer(), and
* the caller does the locking.
*/
static void
in_lltable_free(struct lltable *llt, struct llentry *lle)
{
LLE_WUNLOCK(lle);
LLE_LOCK_DESTROY(lle);
free(lle, M_LLTABLE);
}
#define IN_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \
(((ntohl((d)->sin_addr.s_addr) ^ (a)->sin_addr.s_addr) & (m)->sin_addr.s_addr)) == 0 )
@ -1577,7 +1577,6 @@ in_domifattach(struct ifnet *ifp)
llt = lltable_init(ifp, AF_INET);
if (llt != NULL) {
llt->llt_free = in_lltable_free;
llt->llt_prefix_free = in_lltable_prefix_free;
llt->llt_lookup = in_lltable_lookup;
llt->llt_dump = in_lltable_dump;

View File

@ -2439,25 +2439,6 @@ struct in6_llentry {
struct sockaddr_in6 l3_addr6;
};
static struct llentry *
in6_lltable_new(const struct sockaddr *l3addr, u_int flags)
{
struct in6_llentry *lle;
lle = malloc(sizeof(struct in6_llentry), M_LLTABLE,
M_DONTWAIT | M_ZERO);
if (lle == NULL) /* NB: caller generates msg */
return NULL;
lle->l3_addr6 = *(const struct sockaddr_in6 *)l3addr;
lle->base.lle_refcnt = 1;
LLE_LOCK_INIT(&lle->base);
callout_init_rw(&lle->base.ln_timer_ch, &lle->base.lle_lock,
CALLOUT_RETURNUNLOCKED);
return &lle->base;
}
/*
* Deletes an address from the address table.
* This function is called by the timer functions
@ -2472,6 +2453,26 @@ in6_lltable_free(struct lltable *llt, struct llentry *lle)
free(lle, M_LLTABLE);
}
static struct llentry *
in6_lltable_new(const struct sockaddr *l3addr, u_int flags)
{
struct in6_llentry *lle;
lle = malloc(sizeof(struct in6_llentry), M_LLTABLE,
M_DONTWAIT | M_ZERO);
if (lle == NULL) /* NB: caller generates msg */
return NULL;
lle->l3_addr6 = *(const struct sockaddr_in6 *)l3addr;
lle->base.lle_refcnt = 1;
lle->base.lle_free = in6_lltable_free;
LLE_LOCK_INIT(&lle->base);
callout_init_rw(&lle->base.ln_timer_ch, &lle->base.lle_lock,
CALLOUT_RETURNUNLOCKED);
return &lle->base;
}
static void
in6_lltable_prefix_free(struct lltable *llt,
const struct sockaddr *prefix,
@ -2713,7 +2714,6 @@ in6_domifattach(struct ifnet *ifp)
ext->scope6_id = scope6_ifattach(ifp);
ext->lltable = lltable_init(ifp, AF_INET6);
if (ext->lltable != NULL) {
ext->lltable->llt_free = in6_lltable_free;
ext->lltable->llt_prefix_free = in6_lltable_prefix_free;
ext->lltable->llt_lookup = in6_lltable_lookup;
ext->lltable->llt_dump = in6_lltable_dump;