With this patch the internal use of the skb_trimmed is reduced to the ICMPv6/IGMP checksum verification. And for the length checks the newly introduced helper functions are used instead of calculating and checking with skb->len directly. These changes should hopefully make it easier to verify that length checks are performed properly.
Unified · Split
| @@ -1493,22 +1493,22 @@ static int ip_mc_check_igmp_reportv3(struct sk_buff *skb) · 1492 unmodified lines | |||
| 1493 | 1493 | ||
| 1494 | len += sizeof(struct igmpv3_report); | 1494 | len += sizeof(struct igmpv3_report); |
| 1495 | 1495 | ||
| 1496 | return pskb_may_pull(skb, len) ? 0 : -EINVAL; | 1496 | return ip_mc_may_pull(skb, len) ? 0 : -EINVAL; |
| 1497 | } | 1497 | } |
| 1498 | 1498 | ||
| 1499 | static int ip_mc_check_igmp_query(struct sk_buff *skb) | 1499 | static int ip_mc_check_igmp_query(struct sk_buff *skb) |
| 1500 | { | 1500 | { |
| 1501 | unsigned int len = skb_transport_offset(skb); | 1501 | unsigned int transport_len = ip_transport_len(skb); |
| 1502 | 1502 | unsigned int len; | |
| 1503 | len += sizeof(struct igmphdr); | ||
| 1504 | if (skb->len < len) | ||
| 1505 | return -EINVAL; | ||
| 1506 | 1503 | ||
| 1507 | /* IGMPv{1,2}? */ | 1504 | /* IGMPv{1,2}? */ |
| 1508 | if (skb->len != len) { | 1505 | if (transport_len != sizeof(struct igmphdr)) { |
| 1509 | /* or IGMPv3? */ | 1506 | /* or IGMPv3? */ |
| 1510 | len += sizeof(struct igmpv3_query) - sizeof(struct igmphdr); | 1507 | if (transport_len < sizeof(struct igmpv3_query)) |
| 1511 | if (skb->len < len || !pskb_may_pull(skb, len)) | 1508 | return -EINVAL; |
| 1509 | |||
| 1510 | len = skb_transport_offset(skb) + sizeof(struct igmpv3_query); | ||
| 1511 | if (!ip_mc_may_pull(skb, len)) | ||
| 1512 | return -EINVAL; | 1512 | return -EINVAL; |
| 1513 | } | 1513 | } |
| 1514 | 1514 | ||
| @@ -1544,35 +1544,24 @@ static inline __sum16 ip_mc_validate_checksum(struct sk_buff *skb) · 29 unmodified lines | |||
| 1544 | return skb_checksum_simple_validate(skb); | 1544 | return skb_checksum_simple_validate(skb); |
| 1545 | } | 1545 | } |
| 1546 | 1546 | ||
| 1547 | static int __ip_mc_check_igmp(struct sk_buff *skb) | 1547 | static int ip_mc_check_igmp_csum(struct sk_buff *skb) |
| 1548 | |||
| 1549 | { | 1548 | { |
| 1550 | struct sk_buff *skb_chk; | ||
| 1551 | unsigned int transport_len; | ||
| 1552 | unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); | 1549 | unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); |
| 1553 | int ret = -EINVAL; | 1550 | unsigned int transport_len = ip_transport_len(skb); |
| 1551 | struct sk_buff *skb_chk; | ||
| 1554 | 1552 | ||
| 1555 | transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); | 1553 | if (!ip_mc_may_pull(skb, len)) |
| 1554 | return -EINVAL; | ||
| 1556 | 1555 | ||
| 1557 | skb_chk = skb_checksum_trimmed(skb, transport_len, | 1556 | skb_chk = skb_checksum_trimmed(skb, transport_len, |
| 1558 | ip_mc_validate_checksum); | 1557 | ip_mc_validate_checksum); |
| 1559 | if (!skb_chk) | 1558 | if (!skb_chk) |
| 1560 | goto err; | 1559 | return -EINVAL; |
| 1561 | |||
| 1562 | if (!pskb_may_pull(skb_chk, len)) | ||
| 1563 | goto err; | ||
| 1564 | |||
| 1565 | ret = ip_mc_check_igmp_msg(skb_chk); | ||
| 1566 | if (ret) | ||
| 1567 | goto err; | ||
| 1568 | |||
| 1569 | ret = 0; | ||
| 1570 | 1560 | ||
| 1571 | err: | 1561 | if (skb_chk != skb) |
| 1572 | if (skb_chk && skb_chk != skb) | ||
| 1573 | kfree_skb(skb_chk); | 1562 | kfree_skb(skb_chk); |
| 1574 | 1563 | ||
| 1575 | return ret; | 1564 | return 0; |
| 1576 | } | 1565 | } |
| 1577 | 1566 | ||
| 1578 | /** | 1567 | /** |
| @@ -1600,7 +1589,11 @@ int ip_mc_check_igmp(struct sk_buff *skb) · 21 unmodified lines | |||
| 1600 | if (ip_hdr(skb)->protocol != IPPROTO_IGMP) | 1589 | if (ip_hdr(skb)->protocol != IPPROTO_IGMP) |
| 1601 | return -ENOMSG; | 1590 | return -ENOMSG; |
| 1602 | 1591 | ||
| 1603 | return __ip_mc_check_igmp(skb); | 1592 | ret = ip_mc_check_igmp_csum(skb); |
| 1593 | if (ret < 0) | ||
| 1594 | return ret; | ||
| 1595 | |||
| 1596 | return ip_mc_check_igmp_msg(skb); | ||
| 1604 | } | 1597 | } |
| 1605 | EXPORT_SYMBOL(ip_mc_check_igmp); | 1598 | EXPORT_SYMBOL(ip_mc_check_igmp); |
| 1606 | 1599 | ||
| @@ -77,27 +77,27 @@ static int ipv6_mc_check_mld_reportv2(struct sk_buff *skb) · 76 unmodified lines | |||
| 77 | 77 | ||
| 78 | len += sizeof(struct mld2_report); | 78 | len += sizeof(struct mld2_report); |
| 79 | 79 | ||
| 80 | return pskb_may_pull(skb, len) ? 0 : -EINVAL; | 80 | return ipv6_mc_may_pull(skb, len) ? 0 : -EINVAL; |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | static int ipv6_mc_check_mld_query(struct sk_buff *skb) | 83 | static int ipv6_mc_check_mld_query(struct sk_buff *skb) |
| 84 | { | 84 | { |
| 85 | unsigned int transport_len = ipv6_transport_len(skb); | ||
| 85 | struct mld_msg *mld; | 86 | struct mld_msg *mld; |
| 86 | unsigned int len = skb_transport_offset(skb); | 87 | unsigned int len; |
| 87 | 88 | ||
| 88 | /* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */ | 89 | /* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */ |
| 89 | if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) | 90 | if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) |
| 90 | return -EINVAL; | 91 | return -EINVAL; |
| 91 | 92 | ||
| 92 | len += sizeof(struct mld_msg); | ||
| 93 | if (skb->len < len) | ||
| 94 | return -EINVAL; | ||
| 95 | |||
| 96 | /* MLDv1? */ | 93 | /* MLDv1? */ |
| 97 | if (skb->len != len) { | 94 | if (transport_len != sizeof(struct mld_msg)) { |
| 98 | /* or MLDv2? */ | 95 | /* or MLDv2? */ |
| 99 | len += sizeof(struct mld2_query) - sizeof(struct mld_msg); | 96 | if (transport_len < sizeof(struct mld2_query)) |
| 100 | if (skb->len < len || !pskb_may_pull(skb, len)) | 97 | return -EINVAL; |
| 98 | |||
| 99 | len = skb_transport_offset(skb) + sizeof(struct mld2_query); | ||
| 100 | if (!ipv6_mc_may_pull(skb, len)) | ||
| 101 | return -EINVAL; | 101 | return -EINVAL; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| @@ -115,7 +115,13 @@ static int ipv6_mc_check_mld_query(struct sk_buff *skb) · 11 unmodified lines | |||
| 115 | 115 | ||
| 116 | static int ipv6_mc_check_mld_msg(struct sk_buff *skb) | 116 | static int ipv6_mc_check_mld_msg(struct sk_buff *skb) |
| 117 | { | 117 | { |
| 118 | struct mld_msg *mld = (struct mld_msg *)skb_transport_header(skb); | 118 | unsigned int len = skb_transport_offset(skb) + sizeof(struct mld_msg); |
| 119 | struct mld_msg *mld; | ||
| 120 | |||
| 121 | if (!ipv6_mc_may_pull(skb, len)) | ||
| 122 | return -EINVAL; | ||
| 123 | |||
| 124 | mld = (struct mld_msg *)skb_transport_header(skb); | ||
| 119 | 125 | ||
| 120 | switch (mld->mld_type) { | 126 | switch (mld->mld_type) { |
| 121 | case ICMPV6_MGM_REDUCTION: | 127 | case ICMPV6_MGM_REDUCTION: |
| @@ -136,36 +142,24 @@ static inline __sum16 ipv6_mc_validate_checksum(struct sk_buff *skb) · 14 unmodified lines | |||
| 136 | return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo); | 142 | return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo); |
| 137 | } | 143 | } |
| 138 | 144 | ||
| 139 | static int __ipv6_mc_check_mld(struct sk_buff *skb) | 145 | static int ipv6_mc_check_icmpv6(struct sk_buff *skb) |
| 140 | |||
| 141 | { | 146 | { |
| 142 | struct sk_buff *skb_chk = NULL; | 147 | unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr); |
| 143 | unsigned int transport_len; | 148 | unsigned int transport_len = ipv6_transport_len(skb); |
| 144 | unsigned int len = skb_transport_offset(skb) + sizeof(struct mld_msg); | 149 | struct sk_buff *skb_chk; |
| 145 | int ret = -EINVAL; | ||
| 146 | 150 | ||
| 147 | transport_len = ntohs(ipv6_hdr(skb)->payload_len); | 151 | if (!ipv6_mc_may_pull(skb, len)) |
| 148 | transport_len -= skb_transport_offset(skb) - sizeof(struct ipv6hdr); | 152 | return -EINVAL; |
| 149 | 153 | ||
| 150 | skb_chk = skb_checksum_trimmed(skb, transport_len, | 154 | skb_chk = skb_checksum_trimmed(skb, transport_len, |
| 151 | ipv6_mc_validate_checksum); | 155 | ipv6_mc_validate_checksum); |
| 152 | if (!skb_chk) | 156 | if (!skb_chk) |
| 153 | goto err; | 157 | return -EINVAL; |
| 154 | |||
| 155 | if (!pskb_may_pull(skb_chk, len)) | ||
| 156 | goto err; | ||
| 157 | |||
| 158 | ret = ipv6_mc_check_mld_msg(skb_chk); | ||
| 159 | if (ret) | ||
| 160 | goto err; | ||
| 161 | |||
| 162 | ret = 0; | ||
| 163 | 158 | ||
| 164 | err: | 159 | if (skb_chk != skb) |
| 165 | if (skb_chk && skb_chk != skb) | ||
| 166 | kfree_skb(skb_chk); | 160 | kfree_skb(skb_chk); |
| 167 | 161 | ||
| 168 | return ret; | 162 | return 0; |
| 169 | } | 163 | } |
| 170 | 164 | ||
| 171 | /** | 165 | /** |
| @@ -195,6 +189,10 @@ int ipv6_mc_check_mld(struct sk_buff *skb) · 23 unmodified lines | |||
| 195 | if (ret < 0) | 189 | if (ret < 0) |
| 196 | return ret; | 190 | return ret; |
| 197 | 191 | ||
| 198 | return __ipv6_mc_check_mld(skb); | 192 | ret = ipv6_mc_check_icmpv6(skb); |
| 193 | if (ret < 0) | ||
| 194 | return ret; | ||
| 195 | |||
| 196 | return ipv6_mc_check_mld_msg(skb); | ||
| 199 | } | 197 | } |
| 200 | EXPORT_SYMBOL(ipv6_mc_check_mld); | 198 | EXPORT_SYMBOL(ipv6_mc_check_mld); |