From: Kees Cook <keescook@chromium.org>
To: Jakub Kicinski <kuba@kernel.org>
Cc: Dmitry Vyukov <dvyukov@google.com>,
syzbot <syzbot+3a080099974c271cd7e9@syzkaller.appspotmail.com>,
bpf@vger.kernel.org, davem@davemloft.net, edumazet@google.com,
fw@strlen.de, harshit.m.mogalapalli@oracle.com,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
pabeni@redhat.com, syzkaller-bugs@googlegroups.com,
linux-hardening@vger.kernel.org
Subject: Re: [syzbot] upstream boot error: WARNING in netlink_ack
Date: Tue, 4 Oct 2022 16:40:32 -0700 [thread overview]
Message-ID: <202210041600.7C90DF917@keescook> (raw)
In-Reply-To: <20221004104253.29c1f3c7@kernel.org>
On Tue, Oct 04, 2022 at 10:42:53AM -0700, Jakub Kicinski wrote:
> On Tue, 04 Oct 2022 07:36:55 -0700 Kees Cook wrote:
> > This is fixed in the pending netdev tree coming for the merge window.
>
> This has been weighing on my conscience a little, I don't like how we
> still depend on putting one length in the skb and then using a
> different one for the actual memcpy(). How would you feel about this
> patch on top (untested):
tl;dr: yes, I like it. Please add a nlmsg_contents member. :)
Rambling below...
>
> diff --git a/include/net/netlink.h b/include/net/netlink.h
> index 4418b1981e31..6ad671441dff 100644
> --- a/include/net/netlink.h
> +++ b/include/net/netlink.h
> @@ -931,6 +931,29 @@ static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 portid, u32 se
> return __nlmsg_put(skb, portid, seq, type, payload, flags);
> }
>
> +/**
> + * nlmsg_append - Add more data to a nlmsg in a skb
> + * @skb: socket buffer to store message in
> + * @nlh: message header
> + * @payload: length of message payload
> + *
> + * Append data to an existing nlmsg, used when constructing a message
> + * with multiple fixed-format headers (which is rare).
> + * Returns NULL if the tailroom of the skb is insufficient to store
> + * the extra payload.
> + */
> +static inline void *nlmsg_append(struct sk_buff *skb, struct nlmsghdr *nlh,
nlh not needed here?
> + u32 size)
> +{
> + if (unlikely(skb_tailroom(skb) < NLMSG_ALIGN(size)))
> + return NULL;
> +
> + if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0)
why does a fixed size mean no memset?
> + memset(skb_tail_pointer(skb) + size, 0,
> + NLMSG_ALIGN(size) - size);
> + return __skb_put(NLMSG_ALIGN(size));
> +}
> +
> /**
> * nlmsg_put_answer - Add a new callback based netlink message to an skb
> * @skb: socket buffer to store message in
> diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
> index a662e8a5ff84..bb3d855d1f57 100644
> --- a/net/netlink/af_netlink.c
> +++ b/net/netlink/af_netlink.c
> @@ -2488,19 +2488,28 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
> flags |= NLM_F_ACK_TLVS;
>
> skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);
> - if (!skb) {
> - NETLINK_CB(in_skb).sk->sk_err = ENOBUFS;
> - sk_error_report(NETLINK_CB(in_skb).sk);
> - return;
> - }
> + if (!skb)
> + goto err_bad_put;
>
> rep = nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
> - NLMSG_ERROR, payload, flags);
> + NLMSG_ERROR, sizeof(*errmsg), flags);
> + if (!rep)
> + goto err_bad_put;
> errmsg = nlmsg_data(rep);
> errmsg->error = err;
> - unsafe_memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg)
> - ? nlh->nlmsg_len : sizeof(*nlh),
> - /* Bounds checked by the skb layer. */);
> + memcpy(&errmsg->msg, nlh, sizeof(*nlh));
> +
> + if (!(flags & NLM_F_CAPPED)) {
Should it test this flag, or test if the sizes show the need for "extra"
payload length?
I always found the progression of sizes here to be confusing. "payload"
starts as sizeof(*errmsg), and gets nlmsg_len(nlh) added but only when if
"(err && !(nlk->flags & NETLINK_F_CAP_ACK)" was true. Why is
nlmsg_len(nlh) _wrong_ if the rest of its contents are correct? If this
was "0" in the other state, the logic would just be:
nlh_bytes = nlmsg_len(nlh);
total = sizeof(*errmsg);
total += nlh_bytes;
total += tlvlen;
and:
nlmsg_new(total, ...);
... nlmsg_put(..., sizeof(*errmsg), ...);
...
errmsg->error = err;
errmsg->nlh = *nlh;
if (nlh_bytes) {
data = nlmsg_append(..., nlh_bytes), ...);
...
memcpy(data, nlh->nlmsg_contents, nlh_bytes);
}
> + size_t data_len = nlh->nlmsg_len - sizeof(*nlh);
I think data_len here is also "payload - sizeof(*errmsg)"? So if it's >0,
we need to append the nlh contents.
> + void *data;
> +
> + data = nlmsg_append(skb, rep, data_len);
> + if (!data)
> + goto err_bad_put;
> +
> + /* the nlh + 1 is probably going to make you unhappy? */
Right, the compiler may think it is an object no larger than sizeof(*nlh).
My earliest attempt at changes here introduced a flex-array for the
contents, and split the memcpy:
https://lore.kernel.org/lkml/d7251d92-150b-5346-6237-52afc154bb00@rasmusvillemoes.dk/
which is basically the solution you have here, except it wasn't having
the nlmsg_*-helpers do the bounds checking.
> + memcpy(data, nlh + 1, data_len);
So with the struct nlmsghdr::nlmsg_contents member, this becomes:
memcpy(data, nlh->nlmsg_contents, data_len);
--
Kees Cook
next prev parent reply other threads:[~2022-10-04 23:40 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-04 8:27 [syzbot] upstream boot error: WARNING in netlink_ack syzbot
2022-10-04 8:33 ` Dmitry Vyukov
2022-10-04 14:36 ` Kees Cook
2022-10-04 17:42 ` Jakub Kicinski
2022-10-04 23:40 ` Kees Cook [this message]
2022-10-05 0:04 ` Jakub Kicinski
2022-10-05 0:23 ` Jakub Kicinski
2022-10-05 0:28 ` [RFC] netlink: split up copies in the ack construction Jakub Kicinski
2022-10-05 3:03 ` Kees Cook
[not found] <PH8PR10MB6290511E9C0A3D20E1C222EEC25A9@PH8PR10MB6290.namprd10.prod.outlook.com>
2022-10-04 12:57 ` [syzbot] upstream boot error: WARNING in netlink_ack syzbot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=202210041600.7C90DF917@keescook \
--to=keescook@chromium.org \
--cc=bpf@vger.kernel.org \
--cc=davem@davemloft.net \
--cc=dvyukov@google.com \
--cc=edumazet@google.com \
--cc=fw@strlen.de \
--cc=harshit.m.mogalapalli@oracle.com \
--cc=kuba@kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=syzbot+3a080099974c271cd7e9@syzkaller.appspotmail.com \
--cc=syzkaller-bugs@googlegroups.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).