Commit a5f6cba2 authored by David Ahern's avatar David Ahern Committed by David S. Miller

netlink: Add strict version of nlmsg_parse and nla_parse

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: default avatarDavid Ahern <dsahern@gmail.com>
Acked-by: default avatarChristian Brauner <christian@brauner.io>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent dac9c979
...@@ -373,6 +373,9 @@ int nla_validate(const struct nlattr *head, int len, int maxtype, ...@@ -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 nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
int len, const struct nla_policy *policy, int len, const struct nla_policy *policy,
struct netlink_ext_ack *extack); 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); int nla_policy_len(const struct nla_policy *, int);
struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype); 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); 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, ...@@ -525,6 +528,20 @@ static inline int nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen,
nlmsg_attrlen(nlh, hdrlen), policy, extack); 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 * nlmsg_find_attr - find a specific attribute in a netlink message
* @nlh: netlink message header * @nlh: netlink message header
......
...@@ -391,8 +391,9 @@ EXPORT_SYMBOL(nla_policy_len); ...@@ -391,8 +391,9 @@ EXPORT_SYMBOL(nla_policy_len);
* *
* Returns 0 on success or a negative error code. * Returns 0 on success or a negative error code.
*/ */
int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head, static int __nla_parse(struct nlattr **tb, int maxtype,
int len, const struct nla_policy *policy, const struct nlattr *head, int len,
bool strict, const struct nla_policy *policy,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
const struct nlattr *nla; const struct nlattr *nla;
...@@ -403,10 +404,15 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head, ...@@ -403,10 +404,15 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
nla_for_each_attr(nla, head, len, rem) { nla_for_each_attr(nla, head, len, rem) {
u16 type = nla_type(nla); u16 type = nla_type(nla);
if (type > 0 && type <= maxtype) { if (type == 0 || type > maxtype) {
if (strict) {
NL_SET_ERR_MSG(extack, "Unknown attribute type");
return -EINVAL;
}
continue;
}
if (policy) { if (policy) {
int err = validate_nla(nla, maxtype, policy, int err = validate_nla(nla, maxtype, policy, extack);
extack);
if (err < 0) if (err < 0)
return err; return err;
...@@ -414,16 +420,34 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head, ...@@ -414,16 +420,34 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
tb[type] = (struct nlattr *)nla; 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", pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
rem, current->comm); rem, current->comm);
NL_SET_ERR_MSG(extack, "bytes leftover after parsing attributes");
if (strict)
return -EINVAL;
}
return 0; 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); 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 * nla_find - Find a specific attribute in a stream of attributes
* @head: head of attribute stream * @head: head of attribute stream
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment