DF-0754 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | diff --git a/sys/netproto/mpls/mpls_var.h b/sys/netproto/mpls/mpls_var.h --- a/sys/netproto/mpls/mpls_var.h +++ b/sys/netproto/mpls/mpls_var.h @@ -56,8 +56,8 @@ void mpls_init(void); void mpls_hashfn(struct mbuf **, int); void mpls_input(struct mbuf *); -int mpls_output(struct mbuf *, struct rtentry *); -boolean_t mpls_output_process(struct mbuf *, struct rtentry *); +int mpls_output(struct mbuf **, struct rtentry *); +boolean_t mpls_output_process(struct mbuf **, struct rtentry *); #endif /* _KERNEL */ diff --git a/sys/netproto/mpls/mpls_output.c b/sys/netproto/mpls/mpls_output.c --- a/sys/netproto/mpls/mpls_output.c +++ b/sys/netproto/mpls/mpls_output.c @@ -43,17 +43,18 @@ static int mpls_push(struct mbuf **, mpls_label_t, mpls_s_t, mpls_exp_t, mpls_ttl_t); -static int mpls_swap(struct mbuf *, mpls_label_t); -static int mpls_pop(struct mbuf *, mpls_s_t *); +static int mpls_swap(struct mbuf **, mpls_label_t); +static int mpls_pop(struct mbuf **, mpls_s_t *); int -mpls_output(struct mbuf *m, struct rtentry *rt) +mpls_output(struct mbuf **mp, struct rtentry *rt) { struct sockaddr_mpls *smpls = NULL; int error = 0, i; mpls_s_t stackempty; mpls_ttl_t ttl = 255; struct ip *ip; + struct mbuf *m = *mp; M_ASSERTPKTHDR(m); @@ -85,7 +86,7 @@ 0, ttl); if (error) - return (error); + goto out; stackempty = 0; m->m_flags |= M_MPLSLABELED; break; @@ -94,24 +95,28 @@ * Operation is only permmited if label stack * is not empty. */ - if (stackempty) - return (ENOTSUP); + if (stackempty) { + error = ENOTSUP; + goto out; + } KKASSERT(m->m_flags & M_MPLSLABELED); - error = mpls_swap(m, ntohl(smpls->smpls_label)); + error = mpls_swap(&m, ntohl(smpls->smpls_label)); if (error) - return (error); + goto out; break; case MPLSLOP_POP: /* * Operation is only permmited if label stack * is not empty. */ - if (stackempty) - return (ENOTSUP); + if (stackempty) { + error = ENOTSUP; + goto out; + } KKASSERT(m->m_flags & M_MPLSLABELED); - error = mpls_pop(m, &stackempty); + error = mpls_pop(&m, &stackempty); if (error) - return (error); + goto out; /* * If we are popping out the last label then * mark the mbuf as ~M_MPLSLABELED. @@ -121,10 +126,19 @@ break; default: /* Unknown label operation */ - return (ENOTSUP); + error = ENOTSUP; + goto out; } } +out: + /* + * Propagate the (possibly new) mbuf head back to the caller. + * mpls_push/mpls_swap/mpls_pop may reallocate the head mbuf via + * M_PREPEND/m_pullup; without this write-back the caller would + * dereference a stale (possibly freed) pointer. (DF-0753/DF-0754) + */ + *mp = m; return (error); } @@ -132,7 +146,7 @@ * Returns FALSE if no further output processing required. */ boolean_t -mpls_output_process(struct mbuf *m, struct rtentry *rt) +mpls_output_process(struct mbuf **mp, struct rtentry *rt) { int error; @@ -140,9 +154,9 @@ if (!(rt->rt_flags & RTF_MPLSOPS)) return TRUE; - error = mpls_output(m, rt); + error = mpls_output(mp, rt); if (error) { - m_freem(m); + m_freem(*mp); return FALSE; } @@ -169,14 +183,20 @@ } static int -mpls_swap(struct mbuf *m, mpls_label_t label) { +mpls_swap(struct mbuf **mp, mpls_label_t label) { struct mpls *mpls; u_int32_t buf; mpls_ttl_t ttl; + struct mbuf *m = *mp; - if (m->m_len < sizeof(struct mpls) && - (m = m_pullup(m, sizeof(struct mpls))) == NULL) - return (ENOBUFS); + if (m->m_len < sizeof(struct mpls)) { + m = m_pullup(m, sizeof(struct mpls)); + if (m == NULL) { + *mp = NULL; + return (ENOBUFS); + } + *mp = m; + } mpls = mtod(m, struct mpls *); buf = ntohl(mpls->mpls_shim); @@ -194,14 +214,18 @@ } static int -mpls_pop(struct mbuf *m, mpls_s_t *sbit) { +mpls_pop(struct mbuf **mp, mpls_s_t *sbit) { struct mpls *mpls; u_int32_t buf; + struct mbuf *m = *mp; if (m->m_len < sizeof(struct mpls)) { m = m_pullup(m, sizeof(struct mpls)); - if (m == NULL) + if (m == NULL) { + *mp = NULL; return (ENOBUFS); + } + *mp = m; } mpls = mtod(m, struct mpls *); buf = ntohl(mpls->mpls_shim); diff --git a/sys/netproto/mpls/mpls_input.c b/sys/netproto/mpls/mpls_input.c --- a/sys/netproto/mpls/mpls_input.c +++ b/sys/netproto/mpls/mpls_input.c @@ -205,7 +205,7 @@ ifp = cache_rt->ro_rt->rt_ifp; dst = cache_rt->ro_rt->rt_gateway; - error = mpls_output(m, cache_rt->ro_rt); + error = mpls_output(&m, cache_rt->ro_rt); if (error) goto bad; error = (*ifp->if_output)(ifp, m, dst, cache_rt->ro_rt); diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -692,7 +692,7 @@ #endif #ifdef MPLS - if (!mpls_output_process(m, ro->ro_rt)) + if (!mpls_output_process(&m, ro->ro_rt)) goto done; #endif error = ifp->if_output(ifp, m, (struct sockaddr *)dst, @@ -736,7 +736,7 @@ m->m_pkthdr.len); } #ifdef MPLS - if (!mpls_output_process(m, ro->ro_rt)) + if (!mpls_output_process(&m, ro->ro_rt)) continue; #endif error = ifp->if_output(ifp, m, (struct sockaddr *)dst, |