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.
Unified · Split
| @@ -200,6 +200,7 @@ enum nla_policy_validation { · 199 unmodified lines | |||
| 200 | NLA_VALIDATE_RANGE_WARN_TOO_LONG, | 200 | NLA_VALIDATE_RANGE_WARN_TOO_LONG, |
| 201 | NLA_VALIDATE_MIN, | 201 | NLA_VALIDATE_MIN, |
| 202 | NLA_VALIDATE_MAX, | 202 | NLA_VALIDATE_MAX, |
| 203 | NLA_VALIDATE_MASK, | ||
| 203 | NLA_VALIDATE_RANGE_PTR, | 204 | NLA_VALIDATE_RANGE_PTR, |
| 204 | NLA_VALIDATE_FUNCTION, | 205 | NLA_VALIDATE_FUNCTION, |
| 205 | }; | 206 | }; |
| @@ -317,6 +318,7 @@ struct nla_policy { · 111 unmodified lines | |||
| 317 | u16 len; | 318 | u16 len; |
| 318 | union { | 319 | union { |
| 319 | const u32 bitfield32_valid; | 320 | const u32 bitfield32_valid; |
| 321 | const u32 mask; | ||
| 320 | const char *reject_message; | 322 | const char *reject_message; |
| 321 | const struct nla_policy *nested_policy; | 323 | const struct nla_policy *nested_policy; |
| 322 | struct netlink_range_validation *range; | 324 | struct netlink_range_validation *range; |
| @@ -368,6 +370,8 @@ struct nla_policy { · 45 unmodified lines | |||
| 368 | (tp == NLA_S8 || tp == NLA_S16 || tp == NLA_S32 || tp == NLA_S64) | 370 | (tp == NLA_S8 || tp == NLA_S16 || tp == NLA_S32 || tp == NLA_S64) |
| 369 | 371 | ||
| 370 | #define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition)) | 372 | #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) | ||
| 371 | #define NLA_ENSURE_UINT_OR_BINARY_TYPE(tp) \ | 375 | #define NLA_ENSURE_UINT_OR_BINARY_TYPE(tp) \ |
| 372 | (__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp) || \ | 376 | (__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp) || \ |
| 373 | tp == NLA_MSECS || \ | 377 | tp == NLA_MSECS || \ |
| @@ -416,6 +420,12 @@ struct nla_policy { · 42 unmodified lines | |||
| 416 | .max = _max, \ | 420 | .max = _max, \ |
| 417 | } | 421 | } |
| 418 | 422 | ||
| 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 | |||
| 419 | #define NLA_POLICY_VALIDATE_FN(tp, fn, ...) { \ | 429 | #define NLA_POLICY_VALIDATE_FN(tp, fn, ...) { \ |
| 420 | .type = NLA_ENSURE_NO_VALIDATION_PTR(tp), \ | 430 | .type = NLA_ENSURE_NO_VALIDATION_PTR(tp), \ |
| 421 | .validation_type = NLA_VALIDATE_FUNCTION, \ | 431 | .validation_type = NLA_VALIDATE_FUNCTION, \ |
| @@ -331,6 +331,7 @@ enum netlink_attribute_type { · 330 unmodified lines | |||
| 331 | * the index, if limited inside the nesting (U32) | 331 | * the index, if limited inside the nesting (U32) |
| 332 | * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the | 332 | * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the |
| 333 | * bitfield32 type (U32) | 333 | * bitfield32 type (U32) |
| 334 | * @NL_POLICY_TYPE_ATTR_MASK: mask of valid bits for unsigned integers (U64) | ||
| 334 | * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment | 335 | * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment |
| 335 | */ | 336 | */ |
| 336 | enum netlink_policy_type_attr { | 337 | enum netlink_policy_type_attr { |
| @@ -346,6 +347,7 @@ enum netlink_policy_type_attr { · 9 unmodified lines | |||
| 346 | NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, | 347 | NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, |
| 347 | NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, | 348 | NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, |
| 348 | NL_POLICY_TYPE_ATTR_PAD, | 349 | NL_POLICY_TYPE_ATTR_PAD, |
| 350 | NL_POLICY_TYPE_ATTR_MASK, | ||
| 349 | 351 | ||
| 350 | /* keep last */ | 352 | /* keep last */ |
| 351 | __NL_POLICY_TYPE_ATTR_MAX, | 353 | __NL_POLICY_TYPE_ATTR_MAX, |
| @@ -323,6 +323,37 @@ static int nla_validate_int_range(const struct nla_policy *pt, · 322 unmodified lines | |||
| 323 | } | 323 | } |
| 324 | } | 324 | } |
| 325 | 325 | ||
| 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 | |||
| 326 | static int validate_nla(const struct nlattr *nla, int maxtype, | 357 | static int validate_nla(const struct nlattr *nla, int maxtype, |
| 327 | const struct nla_policy *policy, unsigned int validate, | 358 | const struct nla_policy *policy, unsigned int validate, |
| 328 | struct netlink_ext_ack *extack, unsigned int depth) | 359 | 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 | |||
| 503 | if (err) | 534 | if (err) |
| 504 | return err; | 535 | return err; |
| 505 | break; | 536 | break; |
| 537 | case NLA_VALIDATE_MASK: | ||
| 538 | err = nla_validate_mask(pt, nla, extack); | ||
| 539 | if (err) | ||
| 540 | return err; | ||
| 541 | break; | ||
| 506 | case NLA_VALIDATE_FUNCTION: | 542 | case NLA_VALIDATE_FUNCTION: |
| 507 | if (pt->validate) { | 543 | if (pt->validate) { |
| 508 | err = pt->validate(nla, extack); | 544 | err = pt->validate(nla, extack); |
| @@ -263,6 +263,14 @@ send_attribute: · 262 unmodified lines | |||
| 263 | else | 263 | else |
| 264 | type = NL_ATTR_TYPE_U64; | 264 | type = NL_ATTR_TYPE_U64; |
| 265 | 265 | ||
| 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 | |||
| 266 | nla_get_range_unsigned(pt, &range); | 274 | nla_get_range_unsigned(pt, &range); |
| 267 | 275 | ||
| 268 | if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U, | 276 | if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U, |