|
@@ -45,12 +45,11 @@ static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {
|
|
|
};
|
|
|
|
|
|
static int validate_nla_bitfield32(const struct nlattr *nla,
|
|
|
- u32 *valid_flags_allowed)
|
|
|
+ const u32 *valid_flags_mask)
|
|
|
{
|
|
|
const struct nla_bitfield32 *bf = nla_data(nla);
|
|
|
- u32 *valid_flags_mask = valid_flags_allowed;
|
|
|
|
|
|
- if (!valid_flags_allowed)
|
|
|
+ if (!valid_flags_mask)
|
|
|
return -EINVAL;
|
|
|
|
|
|
/*disallow invalid bit selector */
|
|
@@ -68,12 +67,41 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
|
|
|
+ const struct nla_policy *policy,
|
|
|
+ struct netlink_ext_ack *extack)
|
|
|
+{
|
|
|
+ const struct nlattr *entry;
|
|
|
+ int rem;
|
|
|
+
|
|
|
+ nla_for_each_attr(entry, head, len, rem) {
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (nla_len(entry) == 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (nla_len(entry) < NLA_HDRLEN) {
|
|
|
+ NL_SET_ERR_MSG_ATTR(extack, entry,
|
|
|
+ "Array element too short");
|
|
|
+ return -ERANGE;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = nla_validate(nla_data(entry), nla_len(entry),
|
|
|
+ maxtype, policy, extack);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int validate_nla(const struct nlattr *nla, int maxtype,
|
|
|
const struct nla_policy *policy,
|
|
|
- const char **error_msg)
|
|
|
+ struct netlink_ext_ack *extack)
|
|
|
{
|
|
|
const struct nla_policy *pt;
|
|
|
int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla);
|
|
|
+ int err = -ERANGE;
|
|
|
|
|
|
if (type <= 0 || type > maxtype)
|
|
|
return 0;
|
|
@@ -91,24 +119,31 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
|
|
|
switch (pt->type) {
|
|
|
case NLA_EXACT_LEN:
|
|
|
if (attrlen != pt->len)
|
|
|
- return -ERANGE;
|
|
|
+ goto out_err;
|
|
|
break;
|
|
|
|
|
|
case NLA_REJECT:
|
|
|
- if (pt->validation_data && error_msg)
|
|
|
- *error_msg = pt->validation_data;
|
|
|
- return -EINVAL;
|
|
|
+ if (extack && pt->validation_data) {
|
|
|
+ NL_SET_BAD_ATTR(extack, nla);
|
|
|
+ extack->_msg = pt->validation_data;
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out_err;
|
|
|
|
|
|
case NLA_FLAG:
|
|
|
if (attrlen > 0)
|
|
|
- return -ERANGE;
|
|
|
+ goto out_err;
|
|
|
break;
|
|
|
|
|
|
case NLA_BITFIELD32:
|
|
|
if (attrlen != sizeof(struct nla_bitfield32))
|
|
|
- return -ERANGE;
|
|
|
+ goto out_err;
|
|
|
|
|
|
- return validate_nla_bitfield32(nla, pt->validation_data);
|
|
|
+ err = validate_nla_bitfield32(nla, pt->validation_data);
|
|
|
+ if (err)
|
|
|
+ goto out_err;
|
|
|
+ break;
|
|
|
|
|
|
case NLA_NUL_STRING:
|
|
|
if (pt->len)
|
|
@@ -116,13 +151,15 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
|
|
|
else
|
|
|
minlen = attrlen;
|
|
|
|
|
|
- if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL)
|
|
|
- return -EINVAL;
|
|
|
+ if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
/* fall through */
|
|
|
|
|
|
case NLA_STRING:
|
|
|
if (attrlen < 1)
|
|
|
- return -ERANGE;
|
|
|
+ goto out_err;
|
|
|
|
|
|
if (pt->len) {
|
|
|
char *buf = nla_data(nla);
|
|
@@ -131,32 +168,58 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
|
|
|
attrlen--;
|
|
|
|
|
|
if (attrlen > pt->len)
|
|
|
- return -ERANGE;
|
|
|
+ goto out_err;
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case NLA_BINARY:
|
|
|
if (pt->len && attrlen > pt->len)
|
|
|
- return -ERANGE;
|
|
|
+ goto out_err;
|
|
|
break;
|
|
|
|
|
|
- case NLA_NESTED_COMPAT:
|
|
|
- if (attrlen < pt->len)
|
|
|
- return -ERANGE;
|
|
|
- if (attrlen < NLA_ALIGN(pt->len))
|
|
|
- break;
|
|
|
- if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN)
|
|
|
- return -ERANGE;
|
|
|
- nla = nla_data(nla) + NLA_ALIGN(pt->len);
|
|
|
- if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla))
|
|
|
- return -ERANGE;
|
|
|
- break;
|
|
|
case NLA_NESTED:
|
|
|
/* a nested attributes is allowed to be empty; if its not,
|
|
|
* it must have a size of at least NLA_HDRLEN.
|
|
|
*/
|
|
|
if (attrlen == 0)
|
|
|
break;
|
|
|
+ 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, extack);
|
|
|
+ if (err < 0) {
|
|
|
+ /*
|
|
|
+ * return directly to preserve the inner
|
|
|
+ * error message/attribute pointer
|
|
|
+ */
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case NLA_NESTED_ARRAY:
|
|
|
+ /* a nested array attribute is allowed to be empty; if its not,
|
|
|
+ * it must have a size of at least NLA_HDRLEN.
|
|
|
+ */
|
|
|
+ if (attrlen == 0)
|
|
|
+ break;
|
|
|
+ if (attrlen < NLA_HDRLEN)
|
|
|
+ goto out_err;
|
|
|
+ if (pt->validation_data) {
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = nla_validate_array(nla_data(nla), nla_len(nla),
|
|
|
+ pt->len, pt->validation_data,
|
|
|
+ extack);
|
|
|
+ if (err < 0) {
|
|
|
+ /*
|
|
|
+ * return directly to preserve the inner
|
|
|
+ * error message/attribute pointer
|
|
|
+ */
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
default:
|
|
|
if (pt->len)
|
|
|
minlen = pt->len;
|
|
@@ -164,10 +227,13 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
|
|
|
minlen = nla_attr_minlen[pt->type];
|
|
|
|
|
|
if (attrlen < minlen)
|
|
|
- return -ERANGE;
|
|
|
+ goto out_err;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
+out_err:
|
|
|
+ NL_SET_ERR_MSG_ATTR(extack, nla, "Attribute failed policy validation");
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -192,12 +258,10 @@ int nla_validate(const struct nlattr *head, int len, int maxtype,
|
|
|
int rem;
|
|
|
|
|
|
nla_for_each_attr(nla, head, len, rem) {
|
|
|
- int err = validate_nla(nla, maxtype, policy, NULL);
|
|
|
+ int err = validate_nla(nla, maxtype, policy, extack);
|
|
|
|
|
|
- if (err < 0) {
|
|
|
- NL_SET_BAD_ATTR(extack, nla);
|
|
|
+ if (err < 0)
|
|
|
return err;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -253,7 +317,7 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
|
|
|
struct netlink_ext_ack *extack)
|
|
|
{
|
|
|
const struct nlattr *nla;
|
|
|
- int rem, err;
|
|
|
+ int rem;
|
|
|
|
|
|
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
|
|
|
|
|
@@ -261,17 +325,12 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
|
|
|
u16 type = nla_type(nla);
|
|
|
|
|
|
if (type > 0 && type <= maxtype) {
|
|
|
- static const char _msg[] = "Attribute failed policy validation";
|
|
|
- const char *msg = _msg;
|
|
|
-
|
|
|
if (policy) {
|
|
|
- err = validate_nla(nla, maxtype, policy, &msg);
|
|
|
- if (err < 0) {
|
|
|
- NL_SET_BAD_ATTR(extack, nla);
|
|
|
- if (extack)
|
|
|
- extack->_msg = msg;
|
|
|
- goto errout;
|
|
|
- }
|
|
|
+ int err = validate_nla(nla, maxtype, policy,
|
|
|
+ extack);
|
|
|
+
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
tb[type] = (struct nlattr *)nla;
|
|
@@ -282,9 +341,7 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
|
|
|
pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
|
|
|
rem, current->comm);
|
|
|
|
|
|
- err = 0;
|
|
|
-errout:
|
|
|
- return err;
|
|
|
+ return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL(nla_parse);
|
|
|
|