Commit bdbb4e29 authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller

netlink: add mask validation

We don't have good validation policy for existing unsigned int attrs
which serve as flags (for new ones we could use NLA_BITFIELD32).
With increased use of policy dumping having the validation be
expressed as part of the policy is important. Add validation
policy in form of a mask of supported/valid bits.

Support u64 in the uAPI to be future-proof, but really for now
the embedded mask member can only hold 32 bits, so anything with
bit 32+ set will always fail validation.
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ddcf3b70
...@@ -200,6 +200,7 @@ enum nla_policy_validation { ...@@ -200,6 +200,7 @@ enum nla_policy_validation {
NLA_VALIDATE_RANGE_WARN_TOO_LONG, NLA_VALIDATE_RANGE_WARN_TOO_LONG,
NLA_VALIDATE_MIN, NLA_VALIDATE_MIN,
NLA_VALIDATE_MAX, NLA_VALIDATE_MAX,
NLA_VALIDATE_MASK,
NLA_VALIDATE_RANGE_PTR, NLA_VALIDATE_RANGE_PTR,
NLA_VALIDATE_FUNCTION, NLA_VALIDATE_FUNCTION,
}; };
...@@ -317,6 +318,7 @@ struct nla_policy { ...@@ -317,6 +318,7 @@ struct nla_policy {
u16 len; u16 len;
union { union {
const u32 bitfield32_valid; const u32 bitfield32_valid;
const u32 mask;
const char *reject_message; const char *reject_message;
const struct nla_policy *nested_policy; const struct nla_policy *nested_policy;
struct netlink_range_validation *range; struct netlink_range_validation *range;
...@@ -368,6 +370,8 @@ struct nla_policy { ...@@ -368,6 +370,8 @@ struct nla_policy {
(tp == NLA_S8 || tp == NLA_S16 || tp == NLA_S32 || tp == NLA_S64) (tp == NLA_S8 || tp == NLA_S16 || tp == NLA_S32 || tp == NLA_S64)
#define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition)) #define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition))
#define NLA_ENSURE_UINT_TYPE(tp) \
(__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp)) + tp)
#define NLA_ENSURE_UINT_OR_BINARY_TYPE(tp) \ #define NLA_ENSURE_UINT_OR_BINARY_TYPE(tp) \
(__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp) || \ (__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp) || \
tp == NLA_MSECS || \ tp == NLA_MSECS || \
...@@ -416,6 +420,12 @@ struct nla_policy { ...@@ -416,6 +420,12 @@ struct nla_policy {
.max = _max, \ .max = _max, \
} }
#define NLA_POLICY_MASK(tp, _mask) { \
.type = NLA_ENSURE_UINT_TYPE(tp), \
.validation_type = NLA_VALIDATE_MASK, \
.mask = _mask, \
}
#define NLA_POLICY_VALIDATE_FN(tp, fn, ...) { \ #define NLA_POLICY_VALIDATE_FN(tp, fn, ...) { \
.type = NLA_ENSURE_NO_VALIDATION_PTR(tp), \ .type = NLA_ENSURE_NO_VALIDATION_PTR(tp), \
.validation_type = NLA_VALIDATE_FUNCTION, \ .validation_type = NLA_VALIDATE_FUNCTION, \
......
...@@ -331,6 +331,7 @@ enum netlink_attribute_type { ...@@ -331,6 +331,7 @@ enum netlink_attribute_type {
* the index, if limited inside the nesting (U32) * the index, if limited inside the nesting (U32)
* @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the
* bitfield32 type (U32) * bitfield32 type (U32)
* @NL_POLICY_TYPE_ATTR_MASK: mask of valid bits for unsigned integers (U64)
* @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment
*/ */
enum netlink_policy_type_attr { enum netlink_policy_type_attr {
...@@ -346,6 +347,7 @@ enum netlink_policy_type_attr { ...@@ -346,6 +347,7 @@ enum netlink_policy_type_attr {
NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
NL_POLICY_TYPE_ATTR_PAD, NL_POLICY_TYPE_ATTR_PAD,
NL_POLICY_TYPE_ATTR_MASK,
/* keep last */ /* keep last */
__NL_POLICY_TYPE_ATTR_MAX, __NL_POLICY_TYPE_ATTR_MAX,
......
...@@ -323,6 +323,37 @@ static int nla_validate_int_range(const struct nla_policy *pt, ...@@ -323,6 +323,37 @@ static int nla_validate_int_range(const struct nla_policy *pt,
} }
} }
static int nla_validate_mask(const struct nla_policy *pt,
const struct nlattr *nla,
struct netlink_ext_ack *extack)
{
u64 value;
switch (pt->type) {
case NLA_U8:
value = nla_get_u8(nla);
break;
case NLA_U16:
value = nla_get_u16(nla);
break;
case NLA_U32:
value = nla_get_u32(nla);
break;
case NLA_U64:
value = nla_get_u64(nla);
break;
default:
return -EINVAL;
}
if (value & ~(u64)pt->mask) {
NL_SET_ERR_MSG_ATTR(extack, nla, "reserved bit set");
return -EINVAL;
}
return 0;
}
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, unsigned int depth) struct netlink_ext_ack *extack, unsigned int depth)
...@@ -503,6 +534,11 @@ static int validate_nla(const struct nlattr *nla, int maxtype, ...@@ -503,6 +534,11 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
if (err) if (err)
return err; return err;
break; break;
case NLA_VALIDATE_MASK:
err = nla_validate_mask(pt, nla, extack);
if (err)
return err;
break;
case NLA_VALIDATE_FUNCTION: case NLA_VALIDATE_FUNCTION:
if (pt->validate) { if (pt->validate) {
err = pt->validate(nla, extack); err = pt->validate(nla, extack);
......
...@@ -263,6 +263,14 @@ int netlink_policy_dump_write(struct sk_buff *skb, ...@@ -263,6 +263,14 @@ int netlink_policy_dump_write(struct sk_buff *skb,
else else
type = NL_ATTR_TYPE_U64; type = NL_ATTR_TYPE_U64;
if (pt->validation_type == NLA_VALIDATE_MASK) {
if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK,
pt->mask,
NL_POLICY_TYPE_ATTR_PAD))
goto nla_put_failure;
break;
}
nla_get_range_unsigned(pt, &range); nla_get_range_unsigned(pt, &range);
if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U, if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
......
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