summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-10-01 23:05:32 -0700
committerDavid S. Miller <davem@davemloft.net>2018-10-01 23:05:32 -0700
commita1fa80802cb02cb043fd61b87bc81c06a38b4bb1 (patch)
treebd3980a02a9896a5a1d60936b451ce40efc44349
parent6140cc20caf7da861fb113a707e0acc36bd358ec (diff)
parent33188bd6430ef06d206ae4fda2cc92f14f16fd20 (diff)
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>
-rw-r--r--include/net/netlink.h89
-rw-r--r--lib/nlattr.c79
2 files changed, 165 insertions, 3 deletions
diff --git a/include/net/netlink.h b/include/net/netlink.h
index 3698ca8ff92c..6a106ef5ca56 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -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
diff --git a/lib/nlattr.c b/lib/nlattr.c
index 2f8feff669a7..1e900bb414ef 100644
--- a/lib/nlattr.c
+++ b/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");