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" <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Jakub Kicinski <[email protected]>
Cc: Paolo Abeni <[email protected]>
Cc: Pablo Neira Ayuso <[email protected]>
Cc: Jozsef Kadlecsik <[email protected]>
Cc: Florian Westphal <[email protected]>
Cc: syzbot <[email protected]>
Cc: Yajun Deng <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Kees Cook <[email protected]>
---
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 <[email protected]> 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(...);