In the ARP callout timer expiration function, the current time_second

is compared against the entry expiration time value (that was set based
on time_second) to check if the current time is larger than the set
expiration time. Due to the +/- timer granularity value, the comparison
returns false, causing the alternative code to be executed. The
alternative code path freed the memory without removing that entry
from the table list, causing a use-after-free bug.

Reviewed by:	discussed with kmacy
MFC after:	immediately
Verified by:	rnoland, yongari
This commit is contained in:
Qing Li 2009-10-20 17:55:42 +00:00
parent e64585bdc2
commit fc02323563
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=198301

View File

@ -175,18 +175,18 @@ arptimer(void *arg)
CURVNET_SET(ifp->if_vnet);
IF_AFDATA_LOCK(ifp);
LLE_WLOCK(lle);
if (((lle->la_flags & LLE_DELETED) ||
(time_second >= lle->la_expire)) &&
(!callout_pending(&lle->la_timer) &&
if ((!callout_pending(&lle->la_timer) &&
callout_active(&lle->la_timer))) {
(void) llentry_free(lle);
ARPSTAT_INC(timeouts);
} else {
/*
* Still valid, just drop our reference
*/
LLE_FREE_LOCKED(lle);
}
#ifdef DIAGNOSTICS
else {
struct sockaddr *l3addr = L3_ADDR(lle);
log(LOG_INFO, "arptimer issue: %p, IPv4 address: \"%s\"\n", lle,
inet_ntoa(((const struct sockaddr_in *)l3addr)->sin_addr));
}
#endif
IF_AFDATA_UNLOCK(ifp);
CURVNET_RESTORE();
}
@ -377,7 +377,7 @@ arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m,
if (renew) {
LLE_ADDREF(la);
la->la_expire = time_second;
la->la_expire = time_second + V_arpt_down;
callout_reset(&la->la_timer, hz * V_arpt_down, arptimer, la);
la->la_asked++;
LLE_WUNLOCK(la);