From: Zhou Nan <zhouna...@huawei.com>

When we set ipv6 addr, we need to recalculate checksum of L4 header.
In our testcase, after send ipv6 fragment package, KASAN detect "use after 
free" when calling function update_ipv6_checksum, and crash occurred after a 
while.
If ipv6 package is fragment, and it is not first seg, we should not recalculate 
checksum of L4 header since this kind of package has no
L4 header.
To prevent crash, we set "recalc_csum" "false" when calling function 
"set_ipv6_addr".
We also find that function skb_ensure_writable (make sure L4 header is 
writable) is helpful before calling inet_proto_csum_replace16 to recalculate 
checksum.

Fixes: ada5efce102d6191e5c66fc385ba52a2d340ef50
       ("datapath: Fix IPv6 later frags parsing")

Signed-off-by: Zhou Nan <zhouna...@huawei.com>
---
 datapath/actions.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/datapath/actions.c b/datapath/actions.c index fbf4457..52cf03e 
100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -456,12 +456,21 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 
l4_proto,
                                 __be32 addr[4], const __be32 new_addr[4])  {
        int transport_len = skb->len - skb_transport_offset(skb);
+       int err;
 
        if (l4_proto == NEXTHDR_TCP) {
+               err = skb_ensure_writable(skb, skb_transport_offset(skb) +
+                               sizeof(struct tcphdr));
+               if (unlikely(err))
+                       return;
                if (likely(transport_len >= sizeof(struct tcphdr)))
                        inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb,
                                                  addr, new_addr, true);
        } else if (l4_proto == NEXTHDR_UDP) {
+               err = skb_ensure_writable(skb, skb_transport_offset(skb) +
+                               sizeof(struct udphdr));
+               if (unlikely(err))
+                       return;
                if (likely(transport_len >= sizeof(struct udphdr))) {
                        struct udphdr *uh = udp_hdr(skb);
 
@@ -473,6 +482,10 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 
l4_proto,
                        }
                }
        } else if (l4_proto == NEXTHDR_ICMP) {
+               err = skb_ensure_writable(skb, skb_transport_offset(skb) +
+                               sizeof(struct icmp6hdr));
+               if (unlikely(err))
+                       return;
                if (likely(transport_len >= sizeof(struct icmp6hdr)))
                        inet_proto_csum_replace16(&icmp6_hdr(skb)->icmp6_cksum,
                                                  skb, addr, new_addr, true);
@@ -589,12 +602,15 @@ static int set_ipv6(struct sk_buff *skb, struct 
sw_flow_key *flow_key,
        if (is_ipv6_mask_nonzero(mask->ipv6_src)) {
                __be32 *saddr = (__be32 *)&nh->saddr;
                __be32 masked[4];
+               bool recalc_csum = true;
 
                mask_ipv6_addr(saddr, key->ipv6_src, mask->ipv6_src, masked);
 
                if (unlikely(memcmp(saddr, masked, sizeof(masked)))) {
+                       if (flow_key->ip.frag == OVS_FRAG_TYPE_LATER)
+                               recalc_csum = false;
                        set_ipv6_addr(skb, flow_key->ip.proto, saddr, masked,
-                                     true);
+                                     recalc_csum);
                        memcpy(&flow_key->ipv6.addr.src, masked,
                               sizeof(flow_key->ipv6.addr.src));
                }
@@ -614,6 +630,8 @@ static int set_ipv6(struct sk_buff *skb, struct sw_flow_key 
*flow_key,
                                                             NEXTHDR_ROUTING,
                                                             NULL, &flags)
                                               != NEXTHDR_ROUTING);
+                       if (flow_key->ip.frag == OVS_FRAG_TYPE_LATER)
+                               recalc_csum = false;
 
                        set_ipv6_addr(skb, flow_key->ip.proto, daddr, masked,
                                      recalc_csum);
--
2.27.0

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to