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

netfilter: ip6t_rt: fix rt0_hdr parsing in rt_mt6

In rt_mt6(), when it's a nonlinear skb, the 1st skb_header_pointer()
only copies sizeof(struct ipv6_rt_hdr) to _route that rh points to.
The access by ((const struct rt0_hdr *)rh)->reserved will overflow
the buffer. So this access should be moved below the 2nd call to
skb_header_pointer().

Besides, after the 2nd skb_header_pointer(), its return value should
also be checked, othersize, *rp may cause null-pointer-ref.

v1->v2:
- clean up some old debugging log.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Xin Long and committed by
Pablo Neira Ayuso
a482c5e0 465f15a6

+6 -42
+6 -42
net/ipv6/netfilter/ip6t_rt.c
··· 25 25 static inline bool 26 26 segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) 27 27 { 28 - bool r; 29 - pr_debug("segsleft_match:%c 0x%x <= 0x%x <= 0x%x\n", 30 - invert ? '!' : ' ', min, id, max); 31 - r = (id >= min && id <= max) ^ invert; 32 - pr_debug(" result %s\n", r ? "PASS" : "FAILED"); 33 - return r; 28 + return (id >= min && id <= max) ^ invert; 34 29 } 35 30 36 31 static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par) ··· 60 65 return false; 61 66 } 62 67 63 - pr_debug("IPv6 RT LEN %u %u ", hdrlen, rh->hdrlen); 64 - pr_debug("TYPE %04X ", rh->type); 65 - pr_debug("SGS_LEFT %u %02X\n", rh->segments_left, rh->segments_left); 66 - 67 - pr_debug("IPv6 RT segsleft %02X ", 68 - segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1], 69 - rh->segments_left, 70 - !!(rtinfo->invflags & IP6T_RT_INV_SGS))); 71 - pr_debug("type %02X %02X %02X ", 72 - rtinfo->rt_type, rh->type, 73 - (!(rtinfo->flags & IP6T_RT_TYP) || 74 - ((rtinfo->rt_type == rh->type) ^ 75 - !!(rtinfo->invflags & IP6T_RT_INV_TYP)))); 76 - pr_debug("len %02X %04X %02X ", 77 - rtinfo->hdrlen, hdrlen, 78 - !(rtinfo->flags & IP6T_RT_LEN) || 79 - ((rtinfo->hdrlen == hdrlen) ^ 80 - !!(rtinfo->invflags & IP6T_RT_INV_LEN))); 81 - pr_debug("res %02X %02X %02X ", 82 - rtinfo->flags & IP6T_RT_RES, 83 - ((const struct rt0_hdr *)rh)->reserved, 84 - !((rtinfo->flags & IP6T_RT_RES) && 85 - (((const struct rt0_hdr *)rh)->reserved))); 86 - 87 68 ret = (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1], 88 69 rh->segments_left, 89 70 !!(rtinfo->invflags & IP6T_RT_INV_SGS))) && ··· 78 107 reserved), 79 108 sizeof(_reserved), 80 109 &_reserved); 110 + if (!rp) { 111 + par->hotdrop = true; 112 + return false; 113 + } 81 114 82 115 ret = (*rp == 0); 83 116 } 84 117 85 - pr_debug("#%d ", rtinfo->addrnr); 86 118 if (!(rtinfo->flags & IP6T_RT_FST)) { 87 119 return ret; 88 120 } else if (rtinfo->flags & IP6T_RT_FST_NSTRICT) { 89 - pr_debug("Not strict "); 90 121 if (rtinfo->addrnr > (unsigned int)((hdrlen - 8) / 16)) { 91 - pr_debug("There isn't enough space\n"); 92 122 return false; 93 123 } else { 94 124 unsigned int i = 0; 95 125 96 - pr_debug("#%d ", rtinfo->addrnr); 97 126 for (temp = 0; 98 127 temp < (unsigned int)((hdrlen - 8) / 16); 99 128 temp++) { ··· 109 138 return false; 110 139 } 111 140 112 - if (ipv6_addr_equal(ap, &rtinfo->addrs[i])) { 113 - pr_debug("i=%d temp=%d;\n", i, temp); 141 + if (ipv6_addr_equal(ap, &rtinfo->addrs[i])) 114 142 i++; 115 - } 116 143 if (i == rtinfo->addrnr) 117 144 break; 118 145 } 119 - pr_debug("i=%d #%d\n", i, rtinfo->addrnr); 120 146 if (i == rtinfo->addrnr) 121 147 return ret; 122 148 else 123 149 return false; 124 150 } 125 151 } else { 126 - pr_debug("Strict "); 127 152 if (rtinfo->addrnr > (unsigned int)((hdrlen - 8) / 16)) { 128 - pr_debug("There isn't enough space\n"); 129 153 return false; 130 154 } else { 131 - pr_debug("#%d ", rtinfo->addrnr); 132 155 for (temp = 0; temp < rtinfo->addrnr; temp++) { 133 156 ap = skb_header_pointer(skb, 134 157 ptr ··· 138 173 if (!ipv6_addr_equal(ap, &rtinfo->addrs[temp])) 139 174 break; 140 175 } 141 - pr_debug("temp=%d #%d\n", temp, rtinfo->addrnr); 142 176 if (temp == rtinfo->addrnr && 143 177 temp == (unsigned int)((hdrlen - 8) / 16)) 144 178 return ret;