The nlmsg_len() helper returned "int" from a u32 calculation that could
possible go negative. WARN() if this calculation ever goes negative
(instead returning 0), or if the result would be larger than INT_MAX
(instead returning INT_MAX).
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Pablo Neira Ayuso <pablo@netfilter.org>
Cc: Jozsef Kadlecsik <kadlec@netfilter.org>
Cc: Florian Westphal <fw@strlen.de>
Cc: syzbot <syzkaller@googlegroups.com>
Cc: Yajun Deng <yajun.deng@linux.dev>
Cc: netdev@vger.kernel.org
Cc: netfilter-devel@vger.kernel.org
Cc: coreteam@netfilter.org
Signed-off-by: Kees Cook <keescook@chromium.org>
---
include/net/netlink.h | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/include/net/netlink.h b/include/net/netlink.h
index 7a2a9d3144ba..f8cb0543635e 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -576,7 +576,15 @@ static inline void *nlmsg_data(const struct nlmsghdr *nlh)
*/
static inline int nlmsg_len(const struct nlmsghdr *nlh)
{
- return nlh->nlmsg_len - NLMSG_HDRLEN;
+ u32 nlmsg_contents_len;
+
+ if (WARN_ON_ONCE(check_sub_overflow(nlh->nlmsg_len,
+ (u32)NLMSG_HDRLEN,
+ &nlmsg_contents_len)))
+ return 0;
+ if (WARN_ON_ONCE(nlmsg_contents_len > INT_MAX))
+ return INT_MAX;
+ return nlmsg_contents_len;
}
/**
--
2.34.1
On Wed, 31 Aug 2022 20:06:09 -0700 Kees Cook wrote:
> static inline int nlmsg_len(const struct nlmsghdr *nlh)
> {
> - return nlh->nlmsg_len - NLMSG_HDRLEN;
> + u32 nlmsg_contents_len;
> +
> + if (WARN_ON_ONCE(check_sub_overflow(nlh->nlmsg_len,
> + (u32)NLMSG_HDRLEN,
> + &nlmsg_contents_len)))
> + return 0;
> + if (WARN_ON_ONCE(nlmsg_contents_len > INT_MAX))
> + return INT_MAX;
> + return nlmsg_contents_len;
We check the messages on input, making sure the length is valid wrt
skb->len, and sane, ie > NLMSG_HDRLEN. See netlink_rcv_skb().
Can we not, pretty please? :(
On Wed, Aug 31, 2022 at 08:18:25PM -0700, Jakub Kicinski wrote:
> On Wed, 31 Aug 2022 20:06:09 -0700 Kees Cook wrote:
> > static inline int nlmsg_len(const struct nlmsghdr *nlh)
> > {
> > - return nlh->nlmsg_len - NLMSG_HDRLEN;
> > + u32 nlmsg_contents_len;
> > +
> > + if (WARN_ON_ONCE(check_sub_overflow(nlh->nlmsg_len,
> > + (u32)NLMSG_HDRLEN,
> > + &nlmsg_contents_len)))
> > + return 0;
> > + if (WARN_ON_ONCE(nlmsg_contents_len > INT_MAX))
> > + return INT_MAX;
> > + return nlmsg_contents_len;
>
> We check the messages on input, making sure the length is valid wrt
> skb->len, and sane, ie > NLMSG_HDRLEN. See netlink_rcv_skb().
>
> Can we not, pretty please? :(
This would catch corrupted values...
Is the concern the growth in image size? The check_sub_overflow() isn't
large at all -- it's just adding a single overflow bit test. The WARNs
are heavier, but they're all out-of-line.
--
Kees Cook
On Wed, 31 Aug 2022 23:27:08 -0700 Kees Cook wrote: > This would catch corrupted values... > > Is the concern the growth in image size? The check_sub_overflow() isn't > large at all -- it's just adding a single overflow bit test. The WARNs > are heavier, but they're all out-of-line. It turns the most obvious function into a noodle bar :( Looking at this function in particular is quite useful, because it clearly indicates that the nlmsg_len includes the header. How about we throw in a WARN_ON_ONCE(nlh->nlmsg_len < NLMSG_HDRLEN || nlh->nlmsg_len > INT_MAX); but leave the actual calculation human readable C?
On Thu, Sep 1, 2022 at 12:49 PM Jakub Kicinski <kuba@kernel.org> wrote: > > On Wed, 31 Aug 2022 23:27:08 -0700 Kees Cook wrote: > > This would catch corrupted values... > > > > Is the concern the growth in image size? The check_sub_overflow() isn't > > large at all -- it's just adding a single overflow bit test. The WARNs > > are heavier, but they're all out-of-line. > > It turns the most obvious function into a noodle bar :( > > Looking at this function in particular is quite useful, because > it clearly indicates that the nlmsg_len includes the header. > > How about we throw in a > > WARN_ON_ONCE(nlh->nlmsg_len < NLMSG_HDRLEN || > nlh->nlmsg_len > INT_MAX); > > but leave the actual calculation human readable C? This is inlined, and will add a lot of extra code. We are making the kernel slower at each release. What about letting fuzzers like syzbot find the potential issues ? DEBUG_NET_WARN_ON_ONCE(...);
© 2016 - 2026 Red Hat, Inc.