Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ipv6: Cap TLV scan in ip6_tnl_parse_tlv_enc_lim

Commit 47d3d7ac656a ("ipv6: Implement limits on Hop-by-Hop and
Destination options") added net.ipv6.max_{hbh,dst}_opts_{cnt,len}
and applied them in ip6_parse_tlv(), the generic TLV walker
invoked from ipv6_destopt_rcv() and ipv6_parse_hopopts().

ip6_tnl_parse_tlv_enc_lim() does not go through ip6_parse_tlv();
it has its own hand-rolled TLV scanner inside its NEXTHDR_DEST
branch which looks for IPV6_TLV_TNL_ENCAP_LIMIT. That inner
loop is bounded only by optlen, which can be up to 2048 bytes.
Stuffing the Destination Options header with 2046 Pad1 (type=0)
entries advances the scanner a single byte at a time, yielding
~2000 TLV iterations per extension header.

Reusing max_dst_opts_cnt to bound the TLV iterations, matching
the semantics from 47d3d7ac656a, would require duplicating
ip6_parse_tlv() to also validate Pad1/PadN payload. It would
also mandate enforcing max_dst_opts_len, since otherwise an
attacker shifts the axis to few options with a giant PadN and
recovers the original DoS. Allowing up to 8 options before the
tunnel encapsulation limit TLV is liberal enough; in practice
encap limit is the first TLV. Thus, go with a hard-coded limit
IP6_TUNNEL_MAX_DEST_TLVS (8).

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Justin Iurman <justin.iurman@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Daniel Borkmann and committed by
Jakub Kicinski
076b8cad d293ca71

+6
+6
net/ipv6/ip6_tunnel.c
··· 62 62 MODULE_ALIAS_RTNL_LINK("ip6tnl"); 63 63 MODULE_ALIAS_NETDEV("ip6tnl0"); 64 64 65 + #define IP6_TUNNEL_MAX_DEST_TLVS 8 66 + 65 67 #define IP6_TUNNEL_HASH_SIZE_SHIFT 5 66 68 #define IP6_TUNNEL_HASH_SIZE (1 << IP6_TUNNEL_HASH_SIZE_SHIFT) 67 69 ··· 427 425 break; 428 426 } 429 427 if (nexthdr == NEXTHDR_DEST) { 428 + int tlv_cnt = 0; 430 429 u16 i = 2; 431 430 432 431 while (1) { 433 432 struct ipv6_tlv_tnl_enc_lim *tel; 433 + 434 + if (unlikely(tlv_cnt++ >= IP6_TUNNEL_MAX_DEST_TLVS)) 435 + break; 434 436 435 437 /* No more room for encapsulation limit */ 436 438 if (i + sizeof(*tel) > optlen)