diff options
author | Johannes Berg <johannes.berg@intel.com> | 2018-09-27 12:28:35 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-10-02 09:05:31 +0300 |
commit | 3e48be05f3c7eb6f6126939e9d957903c5cfeee5 (patch) | |
tree | 866035464b1e44661e6ac5459f80db6580364a93 /lib | |
parent | 6140cc20caf7da861fb113a707e0acc36bd358ec (diff) | |
download | linux-3e48be05f3c7eb6f6126939e9d957903c5cfeee5.tar.xz |
netlink: add attribute range validation to policy
Without further bloating the policy structs, we can overload
the `validation_data' pointer with a struct of s16 min, max
and use those to validate ranges in NLA_{U,S}{8,16,32,64}
attributes.
It may sound strange to validate NLA_U32 with a s16 max, but
in many cases NLA_U32 is used for enums etc. since there's no
size benefit in using a smaller attribute width anyway, due
to netlink attribute alignment; in cases like that it's still
useful, particularly when the attribute really transports an
enum value.
Doing so lets us remove quite a bit of validation code, if we
can be sure that these attributes aren't used by userspace in
places where they're ignored today.
To achieve all this, split the 'type' field and introduce a
new 'validation_type' field which indicates what further
validation (beyond the validation prescribed by the type of
the attribute) is done. This currently allows for no further
validation (the default), as well as min, max and range checks.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/nlattr.c | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/lib/nlattr.c b/lib/nlattr.c index 2f8feff669a7..5670e4b7dfef 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,20 @@ 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; + } + return 0; out_err: NL_SET_ERR_MSG_ATTR(extack, nla, "Attribute failed policy validation"); |