mirror of https://gitee.com/openkylin/linux.git
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
a1fa80802c
|
@ -188,9 +188,20 @@ enum {
|
|||
|
||||
#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
|
||||
* @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
|
||||
*
|
||||
* Policies are defined as arrays of this struct, the array must be
|
||||
|
@ -238,7 +249,33 @@ enum {
|
|||
* nested attributes directly inside, while an array has
|
||||
* the nested attributes at another level down and the
|
||||
* 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:
|
||||
* static const struct nla_policy my_policy[ATTR_MAX+1] = {
|
||||
|
@ -249,9 +286,17 @@ enum {
|
|||
* };
|
||||
*/
|
||||
struct nla_policy {
|
||||
u16 type;
|
||||
u8 type;
|
||||
u8 validation_type;
|
||||
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 }
|
||||
|
@ -266,6 +311,44 @@ struct nla_policy {
|
|||
#define NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
|
||||
{ .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
|
||||
* @nlh: Netlink message header of original request
|
||||
|
|
79
lib/nlattr.c
79
lib/nlattr.c
|
@ -95,6 +95,64 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
|
|||
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,
|
||||
const struct nla_policy *policy,
|
||||
struct netlink_ext_ack *extack)
|
||||
|
@ -230,6 +288,27 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
|
|||
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;
|
||||
out_err:
|
||||
NL_SET_ERR_MSG_ATTR(extack, nla, "Attribute failed policy validation");
|
||||
|
|
Loading…
Reference in New Issue