On Tue, 2017-12-05 at 12:55 -0700, David Ahern wrote:
> @@ -70,10 +78,9 @@ static int validate_nla(const struct nlattr *nla, int 
> maxtype,
>       BUG_ON(pt->type > NLA_TYPE_MAX);
>  
>       /* for data types NLA_U* and NLA_S* require exact length */

You should update the comment now :-)

And the comment on nla_attr_len as well.

With the comments fixed, this looks fine to me.

Reviewed-by: Johannes Berg <johan...@sipsolutions.net>


Since we already have two tables now and may want to add attribute
types with exact enforcement in the future (like the BITFIELD32 one),
I'd actually do things a bit more data driven, but I haven't tested it
right now, and it's better done in net-next after this fix.


diff --git a/lib/nlattr.c b/lib/nlattr.c
index 8bf78b4b78f0..e65eb5400a1a 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -15,21 +15,67 @@
 #include <linux/types.h>
 #include <net/netlink.h>
 
-/* for these data types attribute length must be exactly given size */
-static const u8 nla_attr_len[NLA_TYPE_MAX+1] = {
-       [NLA_U8]        = sizeof(u8),
-       [NLA_U16]       = sizeof(u16),
-       [NLA_U32]       = sizeof(u32),
-       [NLA_U64]       = sizeof(u64),
-       [NLA_S8]        = sizeof(s8),
-       [NLA_S16]       = sizeof(s16),
-       [NLA_S32]       = sizeof(s32),
-       [NLA_S64]       = sizeof(s64),
-};
-
-static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {
-       [NLA_MSECS]     = sizeof(u64),
-       [NLA_NESTED]    = NLA_HDRLEN,
+/* netlink validation flags */
+#define NLVF_WARN_WRONG_LEN    BIT(0)
+#define NLVF_REJECT_WRONG_LEN  BIT(1)
+
+static const struct {
+       u8 len, flags;
+} nla_attr_val[NLA_TYPE_MAX + 1] = {
+       [NLA_FLAG] = {
+               .len = 0,
+               .flags = NLVF_REJECT_WRONG_LEN,
+       },
+       [NLA_BITFIELD32] = {
+               /* further checks below */
+               .len = sizeof(struct nla_bitfield32),
+               .flags = NLVF_REJECT_WRONG_LEN,
+       },
+       [NLA_U8] = {
+               .len = sizeof(u8),
+               .flags = NLVF_WARN_WRONG_LEN,
+       },
+       [NLA_U16] = {
+               .len = sizeof(u16),
+               .flags = NLVF_WARN_WRONG_LEN,
+       },
+       [NLA_U32] = {
+               .len = sizeof(u32),
+               .flags = NLVF_WARN_WRONG_LEN,
+       },
+       [NLA_U64] = {
+               .len = sizeof(u64),
+               .flags = NLVF_WARN_WRONG_LEN,
+       },
+       [NLA_S8] = {
+               .len = sizeof(s8),
+               .flags = NLVF_WARN_WRONG_LEN,
+       },
+       [NLA_S16] = {
+               .len = sizeof(s16),
+               .flags = NLVF_WARN_WRONG_LEN,
+       },
+       [NLA_S32] = {
+               .len = sizeof(s32),
+               .flags = NLVF_WARN_WRONG_LEN,
+       },
+       [NLA_S64] = {
+               .len = sizeof(s64),
+               .flags = NLVF_WARN_WRONG_LEN,
+       },
+       [NLA_MSECS] = {
+               .len = sizeof(s64),
+               .flags = NLVF_WARN_WRONG_LEN,
+       },
+       [NLA_NESTED] = {
+               /* minimum length */
+               .len = NLA_HDRLEN,
+       },
+       [NLA_STRING] = {
+               /* minimum length, further checks below */
+               .len = 1,
+       },
+       /* others have .len = 0 and no flags */
 };
 
 static int validate_nla_bitfield32(const struct nlattr *nla,
@@ -60,7 +106,7 @@ static int validate_nla(const struct nlattr *nla, int 
maxtype,
                        const struct nla_policy *policy)
 {
        const struct nla_policy *pt;
-       int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla);
+       int minlen, attrlen = nla_len(nla), type = nla_type(nla);
 
        if (type <= 0 || type > maxtype)
                return 0;
@@ -69,23 +115,51 @@ static int validate_nla(const struct nlattr *nla, int 
maxtype,
 
        BUG_ON(pt->type > NLA_TYPE_MAX);
 
-       /* for data types NLA_U* and NLA_S* require exact length */
-       if (nla_attr_len[pt->type]) {
-               if (attrlen != nla_attr_len[pt->type])
+       switch (pt->type) {
+       case NLA_BINARY:
+               if (pt->len && attrlen > pt->len)
                        return -ERANGE;
-               return 0;
-       }
+               break;
 
-       switch (pt->type) {
-       case NLA_FLAG:
-               if (attrlen > 0)
+       case NLA_NESTED_COMPAT:
+               if (attrlen < pt->len)
+                       return -ERANGE;
+               if (attrlen < NLA_ALIGN(pt->len))
+                       break;
+               if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN)
+                       return -ERANGE;
+               nla = nla_data(nla) + NLA_ALIGN(pt->len);
+               if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla))
                        return -ERANGE;
                break;
 
-       case NLA_BITFIELD32:
-               if (attrlen != sizeof(struct nla_bitfield32))
+       case NLA_NESTED:
+               /* a nested attributes is allowed to be empty; if its not,
+                * it must have a size of at least NLA_HDRLEN.
+                */
+               if (attrlen == 0)
+                       break;
+               /* fall through */
+       default:
+               minlen = nla_attr_val[pt->type].len;
+
+               if (nla_attr_val[pt->type].flags & NLVF_REJECT_WRONG_LEN &&
+                   minlen != attrlen)
+                       return -ERANGE;
+               if (nla_attr_val[pt->type].flags & NLVF_WARN_WRONG_LEN &&
+                   minlen != attrlen)
+                       pr_warn_ratelimited("netlink: '%s': attribute type %d 
has an invalid length.\n",
+                                           current->comm, type);
+
+               if (pt->len)
+                       minlen = pt->len;
+
+               if (attrlen < minlen)
                        return -ERANGE;
+       }
 
+       switch (pt->type) {
+       case NLA_BITFIELD32:
                return validate_nla_bitfield32(nla, pt->validation_data);
 
        case NLA_NUL_STRING:
@@ -99,9 +173,6 @@ static int validate_nla(const struct nlattr *nla, int 
maxtype,
                /* fall through */
 
        case NLA_STRING:
-               if (attrlen < 1)
-                       return -ERANGE;
-
                if (pt->len) {
                        char *buf = nla_data(nla);
 
@@ -112,37 +183,6 @@ static int validate_nla(const struct nlattr *nla, int 
maxtype,
                                return -ERANGE;
                }
                break;
-
-       case NLA_BINARY:
-               if (pt->len && attrlen > pt->len)
-                       return -ERANGE;
-               break;
-
-       case NLA_NESTED_COMPAT:
-               if (attrlen < pt->len)
-                       return -ERANGE;
-               if (attrlen < NLA_ALIGN(pt->len))
-                       break;
-               if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN)
-                       return -ERANGE;
-               nla = nla_data(nla) + NLA_ALIGN(pt->len);
-               if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla))
-                       return -ERANGE;
-               break;
-       case NLA_NESTED:
-               /* a nested attributes is allowed to be empty; if its not,
-                * it must have a size of at least NLA_HDRLEN.
-                */
-               if (attrlen == 0)
-                       break;
-       default:
-               if (pt->len)
-                       minlen = pt->len;
-               else if (pt->type != NLA_UNSPEC)
-                       minlen = nla_attr_minlen[pt->type];
-
-               if (attrlen < minlen)
-                       return -ERANGE;
        }
 
        return 0;

johannes

Reply via email to