Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+86 −3
Original line number Diff line number Diff line
@@ -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;
	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 −0
Original line number Diff line number Diff line
@@ -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");