On Sun, Oct 07, 2018 at 08:16:25PM -0700, David Ahern wrote:
> From: David Ahern <dsah...@gmail.com>
> 
> nla_parse is currently lenient on message parsing, allowing type to be 0
> or greater than max expected and only logging a message
> 
>     "netlink: %d bytes leftover after parsing attributes in process `%s'."
> 
> if the netlink message has unknown data at the end after parsing. What this
> could mean is that the header at the front of the attributes is actually
> wrong and the parsing is shifted from what is expected.
> 
> Add a new strict version that actually fails with EINVAL if there are any
> bytes remaining after the parsing loop completes, if the atttrbitue type
> is 0 or greater than max expected.
> 
> Signed-off-by: David Ahern <dsah...@gmail.com>

+1

Acked-by: Christian Brauner <christ...@brauner.io>

> ---
>  include/net/netlink.h | 17 +++++++++++++++++
>  lib/nlattr.c          | 48 ++++++++++++++++++++++++++++++++++++------------
>  2 files changed, 53 insertions(+), 12 deletions(-)
> 
> diff --git a/include/net/netlink.h b/include/net/netlink.h
> index 9522a0bf1f3a..f1db8e594847 100644
> --- a/include/net/netlink.h
> +++ b/include/net/netlink.h
> @@ -373,6 +373,9 @@ int nla_validate(const struct nlattr *head, int len, int 
> maxtype,
>  int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
>             int len, const struct nla_policy *policy,
>             struct netlink_ext_ack *extack);
> +int nla_parse_strict(struct nlattr **tb, int maxtype, const struct nlattr 
> *head,
> +                  int len, const struct nla_policy *policy,
> +                  struct netlink_ext_ack *extack);
>  int nla_policy_len(const struct nla_policy *, int);
>  struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype);
>  size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize);
> @@ -525,6 +528,20 @@ static inline int nlmsg_parse(const struct nlmsghdr 
> *nlh, int hdrlen,
>                        nlmsg_attrlen(nlh, hdrlen), policy, extack);
>  }
>  
> +static inline int nlmsg_parse_strict(const struct nlmsghdr *nlh, int hdrlen,
> +                                  struct nlattr *tb[], int maxtype,
> +                                  const struct nla_policy *policy,
> +                                  struct netlink_ext_ack *extack)
> +{
> +     if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) {
> +             NL_SET_ERR_MSG(extack, "Invalid header length");
> +             return -EINVAL;
> +     }
> +
> +     return nla_parse_strict(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
> +                             nlmsg_attrlen(nlh, hdrlen), policy, extack);
> +}
> +
>  /**
>   * nlmsg_find_attr - find a specific attribute in a netlink message
>   * @nlh: netlink message header
> diff --git a/lib/nlattr.c b/lib/nlattr.c
> index 1e900bb414ef..d26de6156b97 100644
> --- a/lib/nlattr.c
> +++ b/lib/nlattr.c
> @@ -391,9 +391,10 @@ EXPORT_SYMBOL(nla_policy_len);
>   *
>   * Returns 0 on success or a negative error code.
>   */
> -int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
> -           int len, const struct nla_policy *policy,
> -           struct netlink_ext_ack *extack)
> +static int __nla_parse(struct nlattr **tb, int maxtype,
> +                    const struct nlattr *head, int len,
> +                    bool strict, const struct nla_policy *policy,
> +                    struct netlink_ext_ack *extack)
>  {
>       const struct nlattr *nla;
>       int rem;
> @@ -403,27 +404,50 @@ int nla_parse(struct nlattr **tb, int maxtype, const 
> struct nlattr *head,
>       nla_for_each_attr(nla, head, len, rem) {
>               u16 type = nla_type(nla);
>  
> -             if (type > 0 && type <= maxtype) {
> -                     if (policy) {
> -                             int err = validate_nla(nla, maxtype, policy,
> -                                                    extack);
> -
> -                             if (err < 0)
> -                                     return err;
> +             if (type == 0 || type > maxtype) {
> +                     if (strict) {
> +                             NL_SET_ERR_MSG(extack, "Unknown attribute 
> type");
> +                             return -EINVAL;
>                       }
> +                     continue;
> +             }
> +             if (policy) {
> +                     int err = validate_nla(nla, maxtype, policy, extack);
>  
> -                     tb[type] = (struct nlattr *)nla;
> +                     if (err < 0)
> +                             return err;
>               }
> +
> +             tb[type] = (struct nlattr *)nla;
>       }
>  
> -     if (unlikely(rem > 0))
> +     if (unlikely(rem > 0)) {
>               pr_warn_ratelimited("netlink: %d bytes leftover after parsing 
> attributes in process `%s'.\n",
>                                   rem, current->comm);
> +             NL_SET_ERR_MSG(extack, "bytes leftover after parsing 
> attributes");
> +             if (strict)
> +                     return -EINVAL;
> +     }
>  
>       return 0;
>  }
> +
> +int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
> +           int len, const struct nla_policy *policy,
> +           struct netlink_ext_ack *extack)
> +{
> +     return __nla_parse(tb, maxtype, head, len, false, policy, extack);
> +}
>  EXPORT_SYMBOL(nla_parse);
>  
> +int nla_parse_strict(struct nlattr **tb, int maxtype, const struct nlattr 
> *head,
> +                  int len, const struct nla_policy *policy,
> +                  struct netlink_ext_ack *extack)
> +{
> +     return __nla_parse(tb, maxtype, head, len, true, policy, extack);
> +}
> +EXPORT_SYMBOL(nla_parse_strict);
> +
>  /**
>   * nla_find - Find a specific attribute in a stream of attributes
>   * @head: head of attribute stream
> -- 
> 2.11.0
> 

Reply via email to