diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index dfaa7b3e9ae9..82c159e0532a 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -237,6 +237,10 @@ #define KASAN_ABI_VERSION 3 #endif +#if GCC_VERSION >= 50000 +#define CC_HAVE_BUILTIN_OVERFLOW +#endif + #endif /* gcc version >= 40000 specific checks */ #if !defined(__noclone) diff --git a/include/linux/overflow-arith.h b/include/linux/overflow-arith.h new file mode 100644 index 000000000000..e12ccf854a70 --- /dev/null +++ b/include/linux/overflow-arith.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#ifdef CC_HAVE_BUILTIN_OVERFLOW + +#define overflow_usub __builtin_usub_overflow + +#else + +static inline bool overflow_usub(unsigned int a, unsigned int b, + unsigned int *res) +{ + *res = a - b; + return *res > a ? true : false; +} + +#endif diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index d03d6da772f3..8dddb45c433e 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -584,7 +585,10 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb, if (np->frag_size) mtu = np->frag_size; } - mtu -= hlen + sizeof(struct frag_hdr); + + if (overflow_usub(mtu, hlen + sizeof(struct frag_hdr), &mtu) || + mtu <= 7) + goto fail_toobig; frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr, &ipv6_hdr(skb)->saddr);