Commit 7690aa1c authored by Johannes Berg's avatar Johannes Berg Committed by David S. Miller

netlink: limit recursion depth in policy validation

Now that we have nested policies, we can theoretically
recurse forever parsing attributes if a (sub-)policy
refers back to a higher level one. This is a situation
that has happened in nl80211, and we've avoided it there
by not linking it.

Add some code to netlink parsing to limit recursion depth.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 47a1494b
...@@ -44,6 +44,20 @@ static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = { ...@@ -44,6 +44,20 @@ static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {
[NLA_S64] = sizeof(s64), [NLA_S64] = sizeof(s64),
}; };
/*
* Nested policies might refer back to the original
* policy in some cases, and userspace could try to
* abuse that and recurse by nesting in the right
* ways. Limit recursion to avoid this problem.
*/
#define MAX_POLICY_RECURSION_DEPTH 10
static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
const struct nla_policy *policy,
unsigned int validate,
struct netlink_ext_ack *extack,
struct nlattr **tb, unsigned int depth);
static int validate_nla_bitfield32(const struct nlattr *nla, static int validate_nla_bitfield32(const struct nlattr *nla,
const u32 valid_flags_mask) const u32 valid_flags_mask)
{ {
...@@ -70,7 +84,7 @@ static int validate_nla_bitfield32(const struct nlattr *nla, ...@@ -70,7 +84,7 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
static int nla_validate_array(const struct nlattr *head, int len, int maxtype, static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
const struct nla_policy *policy, const struct nla_policy *policy,
struct netlink_ext_ack *extack, struct netlink_ext_ack *extack,
unsigned int validate) unsigned int validate, unsigned int depth)
{ {
const struct nlattr *entry; const struct nlattr *entry;
int rem; int rem;
...@@ -87,8 +101,9 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype, ...@@ -87,8 +101,9 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
return -ERANGE; return -ERANGE;
} }
ret = __nla_validate(nla_data(entry), nla_len(entry), ret = __nla_validate_parse(nla_data(entry), nla_len(entry),
maxtype, policy, validate, extack); maxtype, policy, validate, extack,
NULL, depth + 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
...@@ -156,7 +171,7 @@ static int nla_validate_int_range(const struct nla_policy *pt, ...@@ -156,7 +171,7 @@ static int nla_validate_int_range(const struct nla_policy *pt,
static int validate_nla(const struct nlattr *nla, int maxtype, static int validate_nla(const struct nlattr *nla, int maxtype,
const struct nla_policy *policy, unsigned int validate, const struct nla_policy *policy, unsigned int validate,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack, unsigned int depth)
{ {
u16 strict_start_type = policy[0].strict_start_type; u16 strict_start_type = policy[0].strict_start_type;
const struct nla_policy *pt; const struct nla_policy *pt;
...@@ -269,9 +284,10 @@ static int validate_nla(const struct nlattr *nla, int maxtype, ...@@ -269,9 +284,10 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
if (attrlen < NLA_HDRLEN) if (attrlen < NLA_HDRLEN)
goto out_err; goto out_err;
if (pt->nested_policy) { if (pt->nested_policy) {
err = __nla_validate(nla_data(nla), nla_len(nla), pt->len, err = __nla_validate_parse(nla_data(nla), nla_len(nla),
pt->nested_policy, validate, pt->len, pt->nested_policy,
extack); validate, extack, NULL,
depth + 1);
if (err < 0) { if (err < 0) {
/* /*
* return directly to preserve the inner * return directly to preserve the inner
...@@ -294,7 +310,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype, ...@@ -294,7 +310,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
err = nla_validate_array(nla_data(nla), nla_len(nla), err = nla_validate_array(nla_data(nla), nla_len(nla),
pt->len, pt->nested_policy, pt->len, pt->nested_policy,
extack, validate); extack, validate, depth);
if (err < 0) { if (err < 0) {
/* /*
* return directly to preserve the inner * return directly to preserve the inner
...@@ -358,11 +374,17 @@ static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype, ...@@ -358,11 +374,17 @@ static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
const struct nla_policy *policy, const struct nla_policy *policy,
unsigned int validate, unsigned int validate,
struct netlink_ext_ack *extack, struct netlink_ext_ack *extack,
struct nlattr **tb) struct nlattr **tb, unsigned int depth)
{ {
const struct nlattr *nla; const struct nlattr *nla;
int rem; int rem;
if (depth >= MAX_POLICY_RECURSION_DEPTH) {
NL_SET_ERR_MSG(extack,
"allowed policy recursion depth exceeded");
return -EINVAL;
}
if (tb) if (tb)
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
...@@ -379,7 +401,7 @@ static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype, ...@@ -379,7 +401,7 @@ static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
} }
if (policy) { if (policy) {
int err = validate_nla(nla, maxtype, policy, int err = validate_nla(nla, maxtype, policy,
validate, extack); validate, extack, depth);
if (err < 0) if (err < 0)
return err; return err;
...@@ -421,7 +443,7 @@ int __nla_validate(const struct nlattr *head, int len, int maxtype, ...@@ -421,7 +443,7 @@ int __nla_validate(const struct nlattr *head, int len, int maxtype,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
return __nla_validate_parse(head, len, maxtype, policy, validate, return __nla_validate_parse(head, len, maxtype, policy, validate,
extack, NULL); extack, NULL, 0);
} }
EXPORT_SYMBOL(__nla_validate); EXPORT_SYMBOL(__nla_validate);
...@@ -476,7 +498,7 @@ int __nla_parse(struct nlattr **tb, int maxtype, ...@@ -476,7 +498,7 @@ int __nla_parse(struct nlattr **tb, int maxtype,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
return __nla_validate_parse(head, len, maxtype, policy, validate, return __nla_validate_parse(head, len, maxtype, policy, validate,
extack, tb); extack, tb, 0);
} }
EXPORT_SYMBOL(__nla_parse); EXPORT_SYMBOL(__nla_parse);
......
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