From: Donald Hunter <donald.hunter@gmail.com>
To: Jakub Kicinski <kuba@kernel.org>
Cc: davem@davemloft.net, netdev@vger.kernel.org,
edumazet@google.com, pabeni@redhat.com, jiri@resnulli.us,
sdf@google.com, nicolas.dichtel@6wind.com
Subject: Re: [PATCH net-next 00/15] tools: ynl: stop using libmnl
Date: Tue, 27 Feb 2024 10:49:42 +0000 [thread overview]
Message-ID: <m2ttlumbax.fsf@gmail.com> (raw)
In-Reply-To: <20240226100020.2aa27e8f@kernel.org> (Jakub Kicinski's message of "Mon, 26 Feb 2024 10:00:20 -0800")
Jakub Kicinski <kuba@kernel.org> writes:
> On Mon, 26 Feb 2024 09:04:12 +0000 Donald Hunter wrote:
>> > On Fri, 23 Feb 2024 16:26:33 +0000 Donald Hunter wrote:
>> >> Is the absence of buffer bounds checking intentional, i.e. relying on libasan?
>> >
>> > In ynl.c or the generated code?
>>
>> I'm looking at ynl_attr_nest_start() and ynl_attr_put*() in ynl-priv.h
>> and there's no checks for buffer overrun. It is admittedly a big
>> buffer, with rx following tx, but still.
>
> You're right. But this series isn't making it worse, AFAIU.
> We weren't checking before, we aren't checking now.
Agreed, libmnl had the same issue.
> I don't want to have to add another arg to all put() calls.
> How about we sash the max len on nlmsg_pid?
Seems reasonable. Minor comments below.
> Something like:
>
> diff --git a/tools/net/ynl/lib/ynl-priv.h b/tools/net/ynl/lib/ynl-priv.h
> index 6361318e5c4c..d4ffe18b00f9 100644
> --- a/tools/net/ynl/lib/ynl-priv.h
> +++ b/tools/net/ynl/lib/ynl-priv.h
> @@ -135,6 +135,8 @@ int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg);
>
> /* Netlink message handling helpers */
>
> +#define YNL_MSG_OVERFLOW 1
> +
> static inline struct nlmsghdr *ynl_nlmsg_put_header(void *buf)
> {
> struct nlmsghdr *nlh = buf;
> @@ -239,11 +241,26 @@ ynl_attr_first(const void *start, size_t len, size_t skip)
> return ynl_attr_if_good(start + len, attr);
> }
>
> +static inline bool
> +__ynl_attr_put_overflow(struct nlmsghdr *nlh, size_t size)
> +{
> + bool o;
> +
> + /* We stash buffer length on nlmsg_pid. */
> + o = nlh->nlmsg_len + NLA_HDRLEN + NLMSG_ALIGN(size) > nlh->nlmsg_pid;
The comment confused me here. How about "We compare against stashed buffer
length in nlmsg_pid".
> + if (o)
> + nlh->nlmsg_pid = YNL_MSG_OVERFLOW;
It took me a moment to realise that this behaves like a very short
buffer length for subsequent calls to __ynl_attr_put_overflow(). Is it
worth extending the comment in ynl_msg_start() to say "buffer length or
overflow status"?
> + return o;
> +}
> +
> static inline struct nlattr *
> ynl_attr_nest_start(struct nlmsghdr *nlh, unsigned int attr_type)
> {
> struct nlattr *attr;
>
> + if (__ynl_attr_put_overflow(nlh, 0))
> + return ynl_nlmsg_end_addr(nlh) - NLA_HDRLEN;
Is the idea here to return a struct nlattr * that is safe to use?
Shouldn't we zero the values in the buffer first?
> +
> attr = ynl_nlmsg_end_addr(nlh);
> attr->nla_type = attr_type | NLA_F_NESTED;
> nlh->nlmsg_len += NLMSG_ALIGN(sizeof(struct nlattr));
> @@ -263,6 +280,9 @@ ynl_attr_put(struct nlmsghdr *nlh, unsigned int attr_type,
> {
> struct nlattr *attr;
>
> + if (__ynl_attr_put_overflow(nlh, size))
> + return;
> +
> attr = ynl_nlmsg_end_addr(nlh);
> attr->nla_type = attr_type;
> attr->nla_len = NLA_HDRLEN + size;
> @@ -276,14 +296,17 @@ static inline void
> ynl_attr_put_str(struct nlmsghdr *nlh, unsigned int attr_type, const char *str)
> {
> struct nlattr *attr;
> - const char *end;
> + size_t len;
> +
> + len = strlen(str);
> + if (__ynl_attr_put_overflow(nlh, len))
> + return;
>
> attr = ynl_nlmsg_end_addr(nlh);
> attr->nla_type = attr_type;
>
> - end = stpcpy(ynl_attr_data(attr), str);
> - attr->nla_len =
> - NLA_HDRLEN + NLA_ALIGN(end - (char *)ynl_attr_data(attr));
> + strcpy(ynl_attr_data(attr), str);
> + attr->nla_len = NLA_HDRLEN + NLA_ALIGN(len);
>
> nlh->nlmsg_len += NLMSG_ALIGN(attr->nla_len);
> }
> diff --git a/tools/net/ynl/lib/ynl.c b/tools/net/ynl/lib/ynl.c
> index 86729119e1ef..c2ba72f68028 100644
> --- a/tools/net/ynl/lib/ynl.c
> +++ b/tools/net/ynl/lib/ynl.c
> @@ -404,9 +404,33 @@ struct nlmsghdr *ynl_msg_start(struct ynl_sock *ys, __u32 id, __u16 flags)
> nlh->nlmsg_flags = flags;
> nlh->nlmsg_seq = ++ys->seq;
>
> + /* This is a local YNL hack for length checking, we put the buffer
> + * length in nlmsg_pid, since messages sent to the kernel always use
> + * PID 0. Message needs to be terminated with ynl_msg_end().
> + */
> + nlh->nlmsg_pid = YNL_SOCKET_BUFFER_SIZE;
> +
> return nlh;
> }
>
> +static int ynl_msg_end(struct ynl_sock *ys, struct nlmsghdr *nlh)
> +{
> + /* We stash buffer length on nlmsg_pid */
> + if (nlh->nlmsg_pid == 0) {
> + yerr(ys, YNL_ERROR_INPUT_INVALID,
> + "Unknwon input buffer lenght");
Typo: lenght -> length
> + return -EINVAL;
> + }
> + if (nlh->nlmsg_pid == YNL_MSG_OVERFLOW) {
> + yerr(ys, YNL_ERROR_INPUT_TOO_BIG,
> + "Constructred message longer than internal buffer");
> + return -EMSGSIZE;
> + }
> +
> + nlh->nlmsg_pid = 0;
> + return 0;
> +}
> +
> struct nlmsghdr *
> ynl_gemsg_start(struct ynl_sock *ys, __u32 id, __u16 flags,
> __u8 cmd, __u8 version)
> @@ -606,6 +630,10 @@ static int ynl_sock_read_family(struct ynl_sock *ys, const char *family_name)
> nlh = ynl_gemsg_start_req(ys, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 1);
> ynl_attr_put_str(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
>
> + err = ynl_msg_end(ys, nlh);
> + if (err < 0)
> + return err;
> +
> err = send(ys->socket, nlh, nlh->nlmsg_len, 0);
> if (err < 0) {
> perr(ys, "failed to request socket family info");
> @@ -867,6 +895,10 @@ int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
> {
> int err;
>
> + err = ynl_msg_end(ys, req_nlh);
> + if (err < 0)
> + return err;
> +
> err = send(ys->socket, req_nlh, req_nlh->nlmsg_len, 0);
> if (err < 0)
> return err;
> @@ -920,6 +952,10 @@ int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
> {
> int err;
>
> + err = ynl_msg_end(ys, req_nlh);
> + if (err < 0)
> + return err;
> +
> err = send(ys->socket, req_nlh, req_nlh->nlmsg_len, 0);
> if (err < 0)
> return err;
> diff --git a/tools/net/ynl/lib/ynl.h b/tools/net/ynl/lib/ynl.h
> index dbeeef8ce91a..9842e85a8c57 100644
> --- a/tools/net/ynl/lib/ynl.h
> +++ b/tools/net/ynl/lib/ynl.h
> @@ -20,6 +20,8 @@ enum ynl_error_code {
> YNL_ERROR_ATTR_INVALID,
> YNL_ERROR_UNKNOWN_NTF,
> YNL_ERROR_INV_RESP,
> + YNL_ERROR_INPUT_INVALID,
> + YNL_ERROR_INPUT_TOO_BIG,
> };
>
> /**
next prev parent reply other threads:[~2024-02-27 10:50 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-22 23:55 [PATCH net-next 00/15] tools: ynl: stop using libmnl Jakub Kicinski
2024-02-22 23:56 ` [PATCH net-next 01/15] tools: ynl: give up on libmnl for auto-ints Jakub Kicinski
2024-02-23 13:51 ` Nicolas Dichtel
2024-02-23 14:35 ` Jakub Kicinski
2024-02-23 15:07 ` Nicolas Dichtel
2024-02-23 15:34 ` Donald Hunter
2024-02-22 23:56 ` [PATCH net-next 02/15] tools: ynl: create local attribute helpers Jakub Kicinski
2024-02-23 14:03 ` Nicolas Dichtel
2024-02-23 14:46 ` Jakub Kicinski
2024-02-23 15:09 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 03/15] tools: ynl: create local for_each helpers Jakub Kicinski
2024-02-23 15:16 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 04/15] tools: ynl: create local nlmsg access helpers Jakub Kicinski
2024-02-23 15:20 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 05/15] tools: ynl: create local ARRAY_SIZE() helper Jakub Kicinski
2024-02-23 15:21 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 06/15] tools: ynl: make yarg the first member of struct ynl_dump_state Jakub Kicinski
2024-02-23 15:23 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 07/15] tools: ynl-gen: remove unused parse code Jakub Kicinski
2024-02-23 15:24 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 08/15] tools: ynl: wrap recv() + mnl_cb_run2() into a single helper Jakub Kicinski
2024-02-23 15:33 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 09/15] tools: ynl: use ynl_sock_read_msgs() for ACK handling Jakub Kicinski
2024-02-23 15:35 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 10/15] tools: ynl: stop using mnl_cb_run2() Jakub Kicinski
2024-02-23 15:50 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 11/15] tools: ynl: switch away from mnl_cb_t Jakub Kicinski
2024-02-23 16:04 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 12/15] tools: ynl: switch away from MNL_CB_* Jakub Kicinski
2024-02-23 16:05 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 13/15] tools: ynl: stop using mnl socket helpers Jakub Kicinski
2024-02-23 16:14 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 14/15] tools: ynl: remove the libmnl dependency Jakub Kicinski
2024-02-23 16:14 ` Nicolas Dichtel
2024-02-22 23:56 ` [PATCH net-next 15/15] tools: ynl: use MSG_DONTWAIT for getting notifications Jakub Kicinski
2024-02-23 16:17 ` Nicolas Dichtel
2024-02-23 16:26 ` [PATCH net-next 00/15] tools: ynl: stop using libmnl Donald Hunter
2024-02-23 16:34 ` Jakub Kicinski
2024-02-26 9:04 ` Donald Hunter
2024-02-26 18:00 ` Jakub Kicinski
2024-02-27 10:49 ` Donald Hunter [this message]
2024-02-27 15:56 ` Jakub Kicinski
2024-02-27 16:29 ` Donald Hunter
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=m2ttlumbax.fsf@gmail.com \
--to=donald.hunter@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=jiri@resnulli.us \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=nicolas.dichtel@6wind.com \
--cc=pabeni@redhat.com \
--cc=sdf@google.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.