Commit a1fa8080 authored by David S. Miller's avatar David S. Miller

Merge branch 'netlink-extended-attribute-validation'

Johannes Berg says:

====================
netlink: extended attribute validation

This adds further netlink attribute validation:
 * min/max/range validation
 * validation through a custom function pointer

This is useful to
 * reduce boilerplate code in command handling code, if attributes
   are used commonly across different commands
 * get more extended ACK error messages/attribute pointers
 * ensure attributes are valid even when ignored
   (though this might be a problem when converting existing code)

Changes since v1:
 * split off validate_type from type and use that for min/max/range
   and function; this is better because the range is limited to the
   range of s16 and so things like "u16 with minimum value 1" couldn't
   be expressed earlier
 * add macros for this, e.g. NLA_POLICY_MIN(NLA_U16, 1) for the case
   mentioned in the previous bullet

Using this pretty much in all places where applicable in nl80211
reduces the code size there by about 1.8KiB, with just a minimal
code increase in lib/nlattr.o.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6140cc20 33188bd6
...@@ -188,9 +188,20 @@ enum { ...@@ -188,9 +188,20 @@ enum {
#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) #define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
enum nla_policy_validation {
NLA_VALIDATE_NONE,
NLA_VALIDATE_RANGE,
NLA_VALIDATE_MIN,
NLA_VALIDATE_MAX,
NLA_VALIDATE_FUNCTION,
};
/** /**
* struct nla_policy - attribute validation policy * struct nla_policy - attribute validation policy
* @type: Type of attribute or NLA_UNSPEC * @type: Type of attribute or NLA_UNSPEC
* @validation_type: type of attribute validation done in addition to
* type-specific validation (e.g. range, function call), see
* &enum nla_policy_validation
* @len: Type specific length of payload * @len: Type specific length of payload
* *
* Policies are defined as arrays of this struct, the array must be * Policies are defined as arrays of this struct, the array must be
...@@ -238,7 +249,33 @@ enum { ...@@ -238,7 +249,33 @@ enum {
* nested attributes directly inside, while an array has * nested attributes directly inside, while an array has
* the nested attributes at another level down and the * the nested attributes at another level down and the
* attributes directly in the nesting don't matter. * attributes directly in the nesting don't matter.
* All other Unused * All other Unused - but note that it's a union
*
* Meaning of `min' and `max' fields, use via NLA_POLICY_MIN, NLA_POLICY_MAX
* and NLA_POLICY_RANGE:
* NLA_U8,
* NLA_U16,
* NLA_U32,
* NLA_U64,
* NLA_S8,
* NLA_S16,
* NLA_S32,
* NLA_S64 These are used depending on the validation_type
* field, if that is min/max/range then the minimum,
* maximum and both are used (respectively) to check
* the value of the integer attribute.
* Note that in the interest of code simplicity and
* struct size both limits are s16, so you cannot
* enforce a range that doesn't fall within the range
* of s16 - do that as usual in the code instead.
* All other Unused - but note that it's a union
*
* Meaning of `validate' field, use via NLA_POLICY_VALIDATE_FN:
* NLA_BINARY Validation function called for the attribute,
* not compatible with use of the validation_data
* as in NLA_BITFIELD32, NLA_REJECT, NLA_NESTED and
* NLA_NESTED_ARRAY.
* All other Unused - but note that it's a union
* *
* Example: * Example:
* static const struct nla_policy my_policy[ATTR_MAX+1] = { * static const struct nla_policy my_policy[ATTR_MAX+1] = {
...@@ -249,9 +286,17 @@ enum { ...@@ -249,9 +286,17 @@ enum {
* }; * };
*/ */
struct nla_policy { struct nla_policy {
u16 type; u8 type;
u8 validation_type;
u16 len; u16 len;
const void *validation_data; union {
const void *validation_data;
struct {
s16 min, max;
};
int (*validate)(const struct nlattr *attr,
struct netlink_ext_ack *extack);
};
}; };
#define NLA_POLICY_EXACT_LEN(_len) { .type = NLA_EXACT_LEN, .len = _len } #define NLA_POLICY_EXACT_LEN(_len) { .type = NLA_EXACT_LEN, .len = _len }
...@@ -266,6 +311,44 @@ struct nla_policy { ...@@ -266,6 +311,44 @@ struct nla_policy {
#define NLA_POLICY_NESTED_ARRAY(maxattr, policy) \ #define NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
{ .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr } { .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr }
#define __NLA_ENSURE(condition) (sizeof(char[1 - 2*!(condition)]) - 1)
#define NLA_ENSURE_INT_TYPE(tp) \
(__NLA_ENSURE(tp == NLA_S8 || tp == NLA_U8 || \
tp == NLA_S16 || tp == NLA_U16 || \
tp == NLA_S32 || tp == NLA_U32 || \
tp == NLA_S64 || tp == NLA_U64) + tp)
#define NLA_ENSURE_NO_VALIDATION_PTR(tp) \
(__NLA_ENSURE(tp != NLA_BITFIELD32 && \
tp != NLA_REJECT && \
tp != NLA_NESTED && \
tp != NLA_NESTED_ARRAY) + tp)
#define NLA_POLICY_RANGE(tp, _min, _max) { \
.type = NLA_ENSURE_INT_TYPE(tp), \
.validation_type = NLA_VALIDATE_RANGE, \
.min = _min, \
.max = _max \
}
#define NLA_POLICY_MIN(tp, _min) { \
.type = NLA_ENSURE_INT_TYPE(tp), \
.validation_type = NLA_VALIDATE_MIN, \
.min = _min, \
}
#define NLA_POLICY_MAX(tp, _max) { \
.type = NLA_ENSURE_INT_TYPE(tp), \
.validation_type = NLA_VALIDATE_MAX, \
.max = _max, \
}
#define NLA_POLICY_VALIDATE_FN(tp, fn, ...) { \
.type = NLA_ENSURE_NO_VALIDATION_PTR(tp), \
.validation_type = NLA_VALIDATE_FUNCTION, \
.validate = fn, \
.len = __VA_ARGS__ + 0, \
}
/** /**
* struct nl_info - netlink source information * struct nl_info - netlink source information
* @nlh: Netlink message header of original request * @nlh: Netlink message header of original request
......
...@@ -95,6 +95,64 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype, ...@@ -95,6 +95,64 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
return 0; return 0;
} }
static int nla_validate_int_range(const struct nla_policy *pt,
const struct nlattr *nla,
struct netlink_ext_ack *extack)
{
bool validate_min, validate_max;
s64 value;
validate_min = pt->validation_type == NLA_VALIDATE_RANGE ||
pt->validation_type == NLA_VALIDATE_MIN;
validate_max = pt->validation_type == NLA_VALIDATE_RANGE ||
pt->validation_type == NLA_VALIDATE_MAX;
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_S8:
value = nla_get_s8(nla);
break;
case NLA_S16:
value = nla_get_s16(nla);
break;
case NLA_S32:
value = nla_get_s32(nla);
break;
case NLA_S64:
value = nla_get_s64(nla);
break;
case NLA_U64:
/* treat this one specially, since it may not fit into s64 */
if ((validate_min && nla_get_u64(nla) < pt->min) ||
(validate_max && nla_get_u64(nla) > pt->max)) {
NL_SET_ERR_MSG_ATTR(extack, nla,
"integer out of range");
return -ERANGE;
}
return 0;
default:
WARN_ON(1);
return -EINVAL;
}
if ((validate_min && value < pt->min) ||
(validate_max && value > pt->max)) {
NL_SET_ERR_MSG_ATTR(extack, nla,
"integer out of range");
return -ERANGE;
}
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, const struct nla_policy *policy,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
...@@ -230,6 +288,27 @@ static int validate_nla(const struct nlattr *nla, int maxtype, ...@@ -230,6 +288,27 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
goto out_err; goto out_err;
} }
/* further validation */
switch (pt->validation_type) {
case NLA_VALIDATE_NONE:
/* nothing to do */
break;
case NLA_VALIDATE_RANGE:
case NLA_VALIDATE_MIN:
case NLA_VALIDATE_MAX:
err = nla_validate_int_range(pt, nla, extack);
if (err)
return err;
break;
case NLA_VALIDATE_FUNCTION:
if (pt->validate) {
err = pt->validate(nla, extack);
if (err)
return err;
}
break;
}
return 0; return 0;
out_err: out_err:
NL_SET_ERR_MSG_ATTR(extack, nla, "Attribute failed policy validation"); NL_SET_ERR_MSG_ATTR(extack, nla, "Attribute failed policy validation");
......
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