History

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.

Jakub Kicinski committed 6 years ago

Commit bdbb4e21 parent ddcf3b74 files · +56 −0Browse files

Unified · Split

4 files
  1. include/net/netlink.h +10 −0
  2. include/uapi/linux/netlink.h +2 −0
  3. lib/nlattr.c +36 −0
  4. net/netlink/policy.c +8 −0
include/net/netlink.h+10−0
@@ -200,6 +200,7 @@ enum nla_policy_validation { · 199 unmodified lines
200200 NLA_VALIDATE_RANGE_WARN_TOO_LONG,
201201 NLA_VALIDATE_MIN,
202202 NLA_VALIDATE_MAX,
203+ NLA_VALIDATE_MASK,
203204 NLA_VALIDATE_RANGE_PTR,
204205 NLA_VALIDATE_FUNCTION,
205206 };
@@ -317,6 +318,7 @@ struct nla_policy { · 111 unmodified lines
317318 u16 len;
318319 union {
319320 const u32 bitfield32_valid;
321+ const u32 mask;
320322 const char *reject_message;
321323 const struct nla_policy *nested_policy;
322324 struct netlink_range_validation *range;
@@ -368,6 +370,8 @@ struct nla_policy { · 45 unmodified lines
368370 (tp == NLA_S8 || tp == NLA_S16 || tp == NLA_S32 || tp == NLA_S64)
369371
370372 #define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition))
373+#define NLA_ENSURE_UINT_TYPE(tp) \
374+ (__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp)) + tp)
371375 #define NLA_ENSURE_UINT_OR_BINARY_TYPE(tp) \
372376 (__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp) || \
373377 tp == NLA_MSECS || \
@@ -416,6 +420,12 @@ struct nla_policy { · 42 unmodified lines
416420 .max = _max, \
417421 }
418422
423+#define NLA_POLICY_MASK(tp, _mask) { \
424+ .type = NLA_ENSURE_UINT_TYPE(tp), \
425+ .validation_type = NLA_VALIDATE_MASK, \
426+ .mask = _mask, \
427+}
428+
419429 #define NLA_POLICY_VALIDATE_FN(tp, fn, ...) { \
420430 .type = NLA_ENSURE_NO_VALIDATION_PTR(tp), \
421431 .validation_type = NLA_VALIDATE_FUNCTION, \
include/uapi/linux/netlink.h+2−0
@@ -331,6 +331,7 @@ enum netlink_attribute_type { · 330 unmodified lines
331331 * the index, if limited inside the nesting (U32)
332332 * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the
333333 * bitfield32 type (U32)
334+ * @NL_POLICY_TYPE_ATTR_MASK: mask of valid bits for unsigned integers (U64)
334335 * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment
335336 */
336337 enum netlink_policy_type_attr {
@@ -346,6 +347,7 @@ enum netlink_policy_type_attr { · 9 unmodified lines
346347 NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
347348 NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
348349 NL_POLICY_TYPE_ATTR_PAD,
350+ NL_POLICY_TYPE_ATTR_MASK,
349351
350352 /* keep last */
351353 __NL_POLICY_TYPE_ATTR_MAX,
lib/nlattr.c+36−0
@@ -323,6 +323,37 @@ static int nla_validate_int_range(const struct nla_policy *pt, · 322 unmodified lines
323323 }
324324 }
325325
326+static int nla_validate_mask(const struct nla_policy *pt,
327+ const struct nlattr *nla,
328+ struct netlink_ext_ack *extack)
329+{
330+ u64 value;
331+
332+ switch (pt->type) {
333+ case NLA_U8:
334+ value = nla_get_u8(nla);
335+ break;
336+ case NLA_U16:
337+ value = nla_get_u16(nla);
338+ break;
339+ case NLA_U32:
340+ value = nla_get_u32(nla);
341+ break;
342+ case NLA_U64:
343+ value = nla_get_u64(nla);
344+ break;
345+ default:
346+ return -EINVAL;
347+ }
348+
349+ if (value & ~(u64)pt->mask) {
350+ NL_SET_ERR_MSG_ATTR(extack, nla, "reserved bit set");
351+ return -EINVAL;
352+ }
353+
354+ return 0;
355+}
356+
326357 static int validate_nla(const struct nlattr *nla, int maxtype,
327358 const struct nla_policy *policy, unsigned int validate,
328359 struct netlink_ext_ack *extack, unsigned int depth)
@@ -503,6 +534,11 @@ static int validate_nla(const struct nlattr *nla, int maxtype, · 174 unmodified lines
503534 if (err)
504535 return err;
505536 break;
537+ case NLA_VALIDATE_MASK:
538+ err = nla_validate_mask(pt, nla, extack);
539+ if (err)
540+ return err;
541+ break;
506542 case NLA_VALIDATE_FUNCTION:
507543 if (pt->validate) {
508544 err = pt->validate(nla, extack);
net/netlink/policy.c+8−0
@@ -263,6 +263,14 @@ send_attribute: · 262 unmodified lines
263263 else
264264 type = NL_ATTR_TYPE_U64;
265265
266+ if (pt->validation_type == NLA_VALIDATE_MASK) {
267+ if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK,
268+ pt->mask,
269+ NL_POLICY_TYPE_ATTR_PAD))
270+ goto nla_put_failure;
271+ break;
272+ }
273+
266274 nla_get_range_unsigned(pt, &range);
267275
268276 if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U,