diff options
author | Eldad Zack <eldad@fogrefinery.com> | 2012-05-19 21:59:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-05-20 16:58:39 -0400 |
commit | 9b905fe68433378032b851c4d81a59187689fa52 (patch) | |
tree | 307801197cf9ce8b520a026962bf1e73f0019f9f /net | |
parent | f7142e6c226076fd40c2ebaad9fb0c9a631b790e (diff) |
ipv6/exthdrs: strict Pad1 and PadN check
The following tightens the padding check from commit
c1412fce7eccae62b4de22494f6ab3ff8a90c0c6 :
* Take into account combinations of consecutive Pad1 and PadN.
* Catch the corner case of when only padding is present in the
header, when the extention header length is 0 (i.e., 8 bytes).
In this case, the header would have exactly 6 bytes of padding:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: Next Header : Hdr Ext Len=0 : :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
: Padding (Pad1 or PadN) :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Signed-off-by: Eldad Zack <eldad@fogrefinery.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/exthdrs.c | 15 |
1 files changed, 14 insertions, 1 deletions
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 50ec95f9aeeb..6447dc49429f 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c | |||
@@ -144,6 +144,7 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb) | |||
144 | const unsigned char *nh = skb_network_header(skb); | 144 | const unsigned char *nh = skb_network_header(skb); |
145 | int off = skb_network_header_len(skb); | 145 | int off = skb_network_header_len(skb); |
146 | int len = (skb_transport_header(skb)[1] + 1) << 3; | 146 | int len = (skb_transport_header(skb)[1] + 1) << 3; |
147 | int padlen = 0; | ||
147 | 148 | ||
148 | if (skb_transport_offset(skb) + len > skb_headlen(skb)) | 149 | if (skb_transport_offset(skb) + len > skb_headlen(skb)) |
149 | goto bad; | 150 | goto bad; |
@@ -158,6 +159,9 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb) | |||
158 | switch (nh[off]) { | 159 | switch (nh[off]) { |
159 | case IPV6_TLV_PAD1: | 160 | case IPV6_TLV_PAD1: |
160 | optlen = 1; | 161 | optlen = 1; |
162 | padlen++; | ||
163 | if (padlen > 7) | ||
164 | goto bad; | ||
161 | break; | 165 | break; |
162 | 166 | ||
163 | case IPV6_TLV_PADN: | 167 | case IPV6_TLV_PADN: |
@@ -166,7 +170,8 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb) | |||
166 | * of 8. 7 is therefore the highest valid value. | 170 | * of 8. 7 is therefore the highest valid value. |
167 | * See also RFC 4942, Section 2.1.9.5. | 171 | * See also RFC 4942, Section 2.1.9.5. |
168 | */ | 172 | */ |
169 | if (optlen > 7) | 173 | padlen += optlen; |
174 | if (padlen > 7) | ||
170 | goto bad; | 175 | goto bad; |
171 | /* RFC 4942 recommends receiving hosts to | 176 | /* RFC 4942 recommends receiving hosts to |
172 | * actively check PadN payload to contain | 177 | * actively check PadN payload to contain |
@@ -195,11 +200,19 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb) | |||
195 | if (ip6_tlvopt_unknown(skb, off) == 0) | 200 | if (ip6_tlvopt_unknown(skb, off) == 0) |
196 | return false; | 201 | return false; |
197 | } | 202 | } |
203 | padlen = 0; | ||
198 | break; | 204 | break; |
199 | } | 205 | } |
200 | off += optlen; | 206 | off += optlen; |
201 | len -= optlen; | 207 | len -= optlen; |
202 | } | 208 | } |
209 | /* This case will not be caught by above check since its padding | ||
210 | * length is smaller than 7: | ||
211 | * 1 byte NH + 1 byte Length + 6 bytes Padding | ||
212 | */ | ||
213 | if ((padlen == 6) && ((off - skb_network_header_len(skb)) == 8)) | ||
214 | goto bad; | ||
215 | |||
203 | if (len == 0) | 216 | if (len == 0) |
204 | return true; | 217 | return true; |
205 | bad: | 218 | bad: |