[SRU][Focal][PATCH 1/1] netlink: limit recursion depth in policy validation
Yuxuan Luo
yuxuan.luo at canonical.com
Sun May 28 19:00:09 UTC 2023
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: Johannes Berg <johannes.berg at intel.com>
Signed-off-by: David S. Miller <davem at davemloft.net>
(backported from commit 7690aa1cdf7c4565ad6b013b324c28b685505e24)
[yuxuan.luo: skip the 47a1494b8208 commit and backport the fix commit directly
by keep the validation_data parameter and add the new depth parameter.
]
CVE-2020-36691
Signed-off-by: Yuxuan Luo <yuxuan.luo at canonical.com>
---
lib/nlattr.c | 46 ++++++++++++++++++++++++++++++++++------------
1 file changed, 34 insertions(+), 12 deletions(-)
diff --git a/lib/nlattr.c b/lib/nlattr.c
index b5ce5e46c06e0..c770b7bd6bc70 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -45,6 +45,20 @@ static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {
[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,
const u32 *valid_flags_mask)
{
@@ -71,7 +85,7 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
const struct nla_policy *policy,
struct netlink_ext_ack *extack,
- unsigned int validate)
+ unsigned int validate, unsigned int depth)
{
const struct nlattr *entry;
int rem;
@@ -88,8 +102,9 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
return -ERANGE;
}
- ret = __nla_validate(nla_data(entry), nla_len(entry),
- maxtype, policy, validate, extack);
+ ret = __nla_validate_parse(nla_data(entry), nla_len(entry),
+ maxtype, policy, validate, extack,
+ NULL, depth + 1);
if (ret < 0)
return ret;
}
@@ -157,7 +172,7 @@ static int nla_validate_int_range(const struct nla_policy *pt,
static int validate_nla(const struct nlattr *nla, int maxtype,
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;
const struct nla_policy *pt;
@@ -271,9 +286,10 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
if (attrlen < NLA_HDRLEN)
goto out_err;
if (pt->validation_data) {
- err = __nla_validate(nla_data(nla), nla_len(nla), pt->len,
- pt->validation_data, validate,
- extack);
+ err = __nla_validate_parse(nla_data(nla), nla_len(nla),
+ pt->len, pt->validation_data,
+ validate, extack, NULL,
+ depth + 1);
if (err < 0) {
/*
* return directly to preserve the inner
@@ -296,7 +312,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
err = nla_validate_array(nla_data(nla), nla_len(nla),
pt->len, pt->validation_data,
- extack, validate);
+ extack, validate, depth);
if (err < 0) {
/*
* return directly to preserve the inner
@@ -360,11 +376,17 @@ 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)
+ struct nlattr **tb, unsigned int depth)
{
const struct nlattr *nla;
int rem;
+ if (depth >= MAX_POLICY_RECURSION_DEPTH) {
+ NL_SET_ERR_MSG(extack,
+ "allowed policy recursion depth exceeded");
+ return -EINVAL;
+ }
+
if (tb)
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
@@ -382,7 +404,7 @@ static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
type = array_index_nospec(type, maxtype + 1);
if (policy) {
int err = validate_nla(nla, maxtype, policy,
- validate, extack);
+ validate, extack, depth);
if (err < 0)
return err;
@@ -424,7 +446,7 @@ int __nla_validate(const struct nlattr *head, int len, int maxtype,
struct netlink_ext_ack *extack)
{
return __nla_validate_parse(head, len, maxtype, policy, validate,
- extack, NULL);
+ extack, NULL, 0);
}
EXPORT_SYMBOL(__nla_validate);
@@ -479,7 +501,7 @@ int __nla_parse(struct nlattr **tb, int maxtype,
struct netlink_ext_ack *extack)
{
return __nla_validate_parse(head, len, maxtype, policy, validate,
- extack, tb);
+ extack, tb, 0);
}
EXPORT_SYMBOL(__nla_parse);
--
2.34.1
More information about the kernel-team
mailing list