public inbox for netdev@vger.kernel.org 
 help / color / mirror / Atom feed
From: Johannes Berg <johannes@sipsolutions•net>
To: David Ahern <dsahern@gmail•com>, netdev@vger•kernel.org
Subject: Re: [net] netlink: Relax attr validation for fixed length types
Date: Wed, 06 Dec 2017 09:06:11 +0100	[thread overview]
Message-ID: <1512547571.26976.34.camel@sipsolutions.net> (raw)
In-Reply-To: <20171205195540.41822-1-dsahern@gmail.com>

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 <johannes@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

  parent reply	other threads:[~2017-12-06  8:06 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-12-05 19:55 [PATCH net] netlink: Relax attr validation for fixed length types David Ahern
2017-12-05 22:58 ` David Miller
2017-12-06  8:06 ` Johannes Berg [this message]
2017-12-06 19:12 ` David Miller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1512547571.26976.34.camel@sipsolutions.net \
    --to=johannes@sipsolutions$(echo .)net \
    --cc=dsahern@gmail$(echo .)com \
    --cc=netdev@vger$(echo .)kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox