Skip to content

Commit a5f6cba

Browse files
dsaherndavem330
authored andcommitted
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: David Ahern <[email protected]> Acked-by: Christian Brauner <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent dac9c97 commit a5f6cba

File tree

2 files changed

+53
-12
lines changed

2 files changed

+53
-12
lines changed

include/net/netlink.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ int nla_validate(const struct nlattr *head, int len, int maxtype,
373373
int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
374374
int len, const struct nla_policy *policy,
375375
struct netlink_ext_ack *extack);
376+
int nla_parse_strict(struct nlattr **tb, int maxtype, const struct nlattr *head,
377+
int len, const struct nla_policy *policy,
378+
struct netlink_ext_ack *extack);
376379
int nla_policy_len(const struct nla_policy *, int);
377380
struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype);
378381
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,
525528
nlmsg_attrlen(nlh, hdrlen), policy, extack);
526529
}
527530

531+
static inline int nlmsg_parse_strict(const struct nlmsghdr *nlh, int hdrlen,
532+
struct nlattr *tb[], int maxtype,
533+
const struct nla_policy *policy,
534+
struct netlink_ext_ack *extack)
535+
{
536+
if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) {
537+
NL_SET_ERR_MSG(extack, "Invalid header length");
538+
return -EINVAL;
539+
}
540+
541+
return nla_parse_strict(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
542+
nlmsg_attrlen(nlh, hdrlen), policy, extack);
543+
}
544+
528545
/**
529546
* nlmsg_find_attr - find a specific attribute in a netlink message
530547
* @nlh: netlink message header

lib/nlattr.c

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -391,9 +391,10 @@ EXPORT_SYMBOL(nla_policy_len);
391391
*
392392
* Returns 0 on success or a negative error code.
393393
*/
394-
int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
395-
int len, const struct nla_policy *policy,
396-
struct netlink_ext_ack *extack)
394+
static int __nla_parse(struct nlattr **tb, int maxtype,
395+
const struct nlattr *head, int len,
396+
bool strict, const struct nla_policy *policy,
397+
struct netlink_ext_ack *extack)
397398
{
398399
const struct nlattr *nla;
399400
int rem;
@@ -403,27 +404,50 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
403404
nla_for_each_attr(nla, head, len, rem) {
404405
u16 type = nla_type(nla);
405406

406-
if (type > 0 && type <= maxtype) {
407-
if (policy) {
408-
int err = validate_nla(nla, maxtype, policy,
409-
extack);
410-
411-
if (err < 0)
412-
return err;
407+
if (type == 0 || type > maxtype) {
408+
if (strict) {
409+
NL_SET_ERR_MSG(extack, "Unknown attribute type");
410+
return -EINVAL;
413411
}
412+
continue;
413+
}
414+
if (policy) {
415+
int err = validate_nla(nla, maxtype, policy, extack);
414416

415-
tb[type] = (struct nlattr *)nla;
417+
if (err < 0)
418+
return err;
416419
}
420+
421+
tb[type] = (struct nlattr *)nla;
417422
}
418423

419-
if (unlikely(rem > 0))
424+
if (unlikely(rem > 0)) {
420425
pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
421426
rem, current->comm);
427+
NL_SET_ERR_MSG(extack, "bytes leftover after parsing attributes");
428+
if (strict)
429+
return -EINVAL;
430+
}
422431

423432
return 0;
424433
}
434+
435+
int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
436+
int len, const struct nla_policy *policy,
437+
struct netlink_ext_ack *extack)
438+
{
439+
return __nla_parse(tb, maxtype, head, len, false, policy, extack);
440+
}
425441
EXPORT_SYMBOL(nla_parse);
426442

443+
int nla_parse_strict(struct nlattr **tb, int maxtype, const struct nlattr *head,
444+
int len, const struct nla_policy *policy,
445+
struct netlink_ext_ack *extack)
446+
{
447+
return __nla_parse(tb, maxtype, head, len, true, policy, extack);
448+
}
449+
EXPORT_SYMBOL(nla_parse_strict);
450+
427451
/**
428452
* nla_find - Find a specific attribute in a stream of attributes
429453
* @head: head of attribute stream

0 commit comments

Comments
 (0)