DF-0753 / 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 | diff --git a/sys/netproto/mpls/mpls_var.h b/sys/netproto/mpls/mpls_var.h index 23595b2689975475cb8cdcbc4ca81bf4e697dd76..4ec2141f7dfdc6b4f26f98c6afd9f5c8a344c8f4 100644 @@ -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 index f3198b7f41132f316347f53506036838a767a1c7..6754bd5e41492aaef04c1b0e8858a80e14786e5a 100644 @@ -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 index 445aee9edc7f40b4967ebb77169255fa3e226a3b..1f5c3c9d371207b414a880367d5995fb8cf53491 100644 @@ -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 index 241cc4163cb315413b0cb7e99dabcd8c68aa49cc..495cbb5997b596c2ecc6be7b4d7551cf82dfe509 100644 @@ -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, |