Fix possible use after free in encap[46]_input().

There is small window, when encap_detach() can free matched entry
directly after we release encapmtx. Instead of use pointer to the
matched entry, save pointers to needed variables from this entry
and use them after release mutex.

Pass argument stored in the encaptab entry to encap_fillarg(), instead
of pointer to matched entry. Also do not allocate new mbuf tag, when
argument that we plan to save in this tag is NULL.

Also make encaptab variable static.

Obtained from:	Yandex LLC
Sponsored by:	Yandex LLC
This commit is contained in:
Andrey V. Elsukov 2015-06-18 18:28:38 +00:00
parent 960dff03c3
commit efb5228ce8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=284566

View File

@ -95,14 +95,14 @@ static MALLOC_DEFINE(M_NETADDR, "encap_export_host", "Export host address struct
static void encap_add(struct encaptab *);
static int mask_match(const struct encaptab *, const struct sockaddr *,
const struct sockaddr *);
static void encap_fillarg(struct mbuf *, const struct encaptab *);
static void encap_fillarg(struct mbuf *, void *);
/*
* All global variables in ip_encap.c are locked using encapmtx.
*/
static struct mtx encapmtx;
MTX_SYSINIT(encapmtx, &encapmtx, "encapmtx", MTX_DEF);
LIST_HEAD(, encaptab) encaptab = LIST_HEAD_INITIALIZER(encaptab);
static LIST_HEAD(, encaptab) encaptab = LIST_HEAD_INITIALIZER(encaptab);
/*
* We currently keey encap_init() for source code compatibility reasons --
@ -122,12 +122,12 @@ encap4_input(struct mbuf **mp, int *offp, int proto)
struct sockaddr_in s, d;
const struct protosw *psw;
struct encaptab *ep, *match;
void *arg;
int matchprio, off, prio;
m = *mp;
off = *offp;
ip = mtod(m, struct ip *);
*mp = NULL;
bzero(&s, sizeof(s));
s.sin_family = AF_INET;
@ -138,6 +138,8 @@ encap4_input(struct mbuf **mp, int *offp, int proto)
d.sin_len = sizeof(struct sockaddr_in);
d.sin_addr = ip->ip_dst;
arg = NULL;
psw = NULL;
match = NULL;
matchprio = 0;
mtx_lock(&encapmtx);
@ -182,14 +184,16 @@ encap4_input(struct mbuf **mp, int *offp, int proto)
match = ep;
}
}
if (match != NULL) {
psw = match->psw;
arg = match->arg;
}
mtx_unlock(&encapmtx);
if (match) {
if (match != NULL) {
/* found a match, "match" has the best one */
psw = match->psw;
if (psw && psw->pr_input) {
encap_fillarg(m, match);
*mp = m;
if (psw != NULL && psw->pr_input != NULL) {
encap_fillarg(m, arg);
(*psw->pr_input)(mp, offp, proto);
} else
m_freem(m);
@ -197,7 +201,6 @@ encap4_input(struct mbuf **mp, int *offp, int proto)
}
/* last resort: inject to raw socket */
*mp = m;
return (rip_input(mp, offp, proto));
}
#endif
@ -211,6 +214,7 @@ encap6_input(struct mbuf **mp, int *offp, int proto)
struct sockaddr_in6 s, d;
const struct protosw *psw;
struct encaptab *ep, *match;
void *arg;
int prio, matchprio;
ip6 = mtod(m, struct ip6_hdr *);
@ -224,6 +228,8 @@ encap6_input(struct mbuf **mp, int *offp, int proto)
d.sin6_len = sizeof(struct sockaddr_in6);
d.sin6_addr = ip6->ip6_dst;
arg = NULL;
psw = NULL;
match = NULL;
matchprio = 0;
mtx_lock(&encapmtx);
@ -251,17 +257,20 @@ encap6_input(struct mbuf **mp, int *offp, int proto)
match = ep;
}
}
if (match != NULL) {
psw = match->psw;
arg = match->arg;
}
mtx_unlock(&encapmtx);
if (match) {
if (match != NULL) {
/* found a match */
psw = match->psw;
if (psw && psw->pr_input) {
encap_fillarg(m, match);
if (psw != NULL && psw->pr_input != NULL) {
encap_fillarg(m, arg);
return (*psw->pr_input)(mp, offp, proto);
} else {
m_freem(m);
return IPPROTO_DONE;
return (IPPROTO_DONE);
}
}
@ -440,14 +449,16 @@ mask_match(const struct encaptab *ep, const struct sockaddr *sp,
}
static void
encap_fillarg(struct mbuf *m, const struct encaptab *ep)
encap_fillarg(struct mbuf *m, void *arg)
{
struct m_tag *tag;
tag = m_tag_get(PACKET_TAG_ENCAP, sizeof (void*), M_NOWAIT);
if (tag) {
*(void**)(tag+1) = ep->arg;
m_tag_prepend(m, tag);
if (arg != NULL) {
tag = m_tag_get(PACKET_TAG_ENCAP, sizeof(void *), M_NOWAIT);
if (tag != NULL) {
*(void**)(tag+1) = arg;
m_tag_prepend(m, tag);
}
}
}