Index: sys/netinet6/ip6_output.c =================================================================== --- sys/netinet6/ip6_output.c +++ sys/netinet6/ip6_output.c @@ -2301,25 +2301,39 @@ return (ip6_setpktopt(optname, buf, len, opt, cred, 1, 0, uproto)); } +#define GET_PKTOPT_RLOCK_INP_RECHECK(in6p) do { \ + INP_RLOCK(in6p); \ + if (in6p->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { \ + INP_RUNLOCK(in6p); \ + free(optdata, M_TEMP); \ + return (ECONNRESET); \ + } \ + pktopt = in6p->in6p_outputopts; \ + prev_optdatalen = optdatalen; \ +} while(0) + +#define GET_PKTOPT_LEN(field, lenexpr) do { \ + if (pktopt && pktopt->field) \ + optdatalen = ulmin(lenexpr, sopt->sopt_valsize); \ + else \ + optdatalen = 0; \ +} while(0) + #define GET_PKTOPT_VAR(field, lenexpr) do { \ - if (pktopt && pktopt->field) { \ + GET_PKTOPT_LEN(field, lenexpr); \ + while (optdatalen) { \ INP_RUNLOCK(in6p); \ - optdata = malloc(sopt->sopt_valsize, M_TEMP, M_WAITOK); \ + optdata = malloc(optdatalen, M_TEMP, M_WAITOK); \ malloc_optdata = true; \ - INP_RLOCK(in6p); \ - if (in6p->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { \ - INP_RUNLOCK(in6p); \ - free(optdata, M_TEMP); \ - return (ECONNRESET); \ - } \ - pktopt = in6p->in6p_outputopts; \ - if (pktopt && pktopt->field) { \ - optdatalen = min(lenexpr, sopt->sopt_valsize); \ - bcopy(&pktopt->field, optdata, optdatalen); \ - } else { \ + GET_PKTOPT_RLOCK_INP_RECHECK(in6p); \ + GET_PKTOPT_LEN(field, lenexpr); \ + if (optdatalen != prev_optdatalen) { \ free(optdata, M_TEMP); \ optdata = NULL; \ malloc_optdata = false; \ + } else { \ + bcopy(&pktopt->field, optdata, optdatalen); \ + break; \ } \ } \ } while(0) @@ -2336,6 +2350,7 @@ void *optdata = NULL; bool malloc_optdata = false; int optdatalen = 0; + int prev_optdatalen = 0; int error = 0; struct in6_pktinfo null_pktinfo; int deftclass = 0, on;