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