Backports Archive on lore.kernel.org
 help / color / Atom feed
From: Hauke Mehrtens <hauke@hauke-m.de>
To: backports@vger.kernel.org
Cc: Johannes Berg <johannes.berg@intel.com>,
	Hauke Mehrtens <hauke@hauke-m.de>
Subject: [PATCH 06/18] backport: Extend netlink parsing with strict validation
Date: Mon,  1 Jul 2019 23:49:01 +0200
Message-ID: <20190701214914.8066-7-hauke@hauke-m.de> (raw)
In-Reply-To: <20190701214914.8066-1-hauke@hauke-m.de>

From: Johannes Berg <johannes.berg@intel.com>

This extends the backported netlink handling with strict parsing. the
struct gen_ops now supports the validate member and does the validation
inside the backport layer.

This is needed to use backports with kernel 5.2.

[Hauke: Remove nla_validate_nested()]
[Hauke: Support struct genl_ops on older kernel versions]
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
 backport/backport-include/net/genetlink.h     |  62 +++-
 backport/backport-include/net/netlink.h       | 341 +++++++++++++-----
 backport/compat/Kconfig                       |   5 +
 backport/compat/Makefile                      |   4 +-
 .../{backport-4.20.c => backport-5.2.c}       | 216 ++++++++---
 backport/compat/backport-genetlink.c          |  26 +-
 6 files changed, 491 insertions(+), 163 deletions(-)
 rename backport/compat/{backport-4.20.c => backport-5.2.c} (58%)

diff --git a/backport/backport-include/net/genetlink.h b/backport/backport-include/net/genetlink.h
index 84011e72..772f10f0 100644
--- a/backport/backport-include/net/genetlink.h
+++ b/backport/backport-include/net/genetlink.h
@@ -91,12 +91,71 @@ void backport_genl_dump_check_consistent(struct netlink_callback *cb,
 #endif
 #endif /* LINUX_VERSION_IS_LESS(4,15,0) */
 
-#if LINUX_VERSION_IS_LESS(4,20,0)
+#if LINUX_VERSION_IS_LESS(5,2,0)
+enum genl_validate_flags {
+	GENL_DONT_VALIDATE_STRICT		= BIT(0),
+	GENL_DONT_VALIDATE_DUMP			= BIT(1),
+	GENL_DONT_VALIDATE_DUMP_STRICT		= BIT(2),
+};
+
+#if LINUX_VERSION_IS_GEQ(3,13,0)
+struct backport_genl_ops {
+	void			*__dummy_was_policy_must_be_null;
+	int		       (*doit)(struct sk_buff *skb,
+				       struct genl_info *info);
+#if LINUX_VERSION_IS_GEQ(4,5,0) || \
+    LINUX_VERSION_IN_RANGE(4,4,104, 4,5,0) || \
+    LINUX_VERSION_IN_RANGE(4,1,48, 4,2,0) || \
+    LINUX_VERSION_IN_RANGE(3,18,86, 3,19,0)
+	int		       (*start)(struct netlink_callback *cb);
+#endif
+	int		       (*dumpit)(struct sk_buff *skb,
+					 struct netlink_callback *cb);
+	int		       (*done)(struct netlink_callback *cb);
+	u8			cmd;
+	u8			internal_flags;
+	u8			flags;
+	u8			validate;
+};
+#else
+struct backport_genl_ops {
+	u8			cmd;
+	u8			internal_flags;
+	unsigned int		flags;
+	void			*__dummy_was_policy_must_be_null;
+	int		       (*doit)(struct sk_buff *skb,
+				       struct genl_info *info);
+	int		       (*dumpit)(struct sk_buff *skb,
+					 struct netlink_callback *cb);
+	int		       (*done)(struct netlink_callback *cb);
+	struct list_head	ops_list;
+	u8			validate;
+};
+#endif
+
 static inline int
 __real_backport_genl_register_family(struct genl_family *family)
 {
+#define OPS_VALIDATE(f) \
+	BUILD_BUG_ON(offsetof(struct genl_ops, f) != \
+		     offsetof(struct backport_genl_ops, f))
+	OPS_VALIDATE(doit);
+#if LINUX_VERSION_IS_GEQ(4,5,0) || \
+    LINUX_VERSION_IN_RANGE(4,4,104, 4,5,0) || \
+    LINUX_VERSION_IN_RANGE(4,1,48, 4,2,0) || \
+    LINUX_VERSION_IN_RANGE(3,18,86, 3,19,0)
+	OPS_VALIDATE(start);
+#endif
+	OPS_VALIDATE(dumpit);
+	OPS_VALIDATE(done);
+	OPS_VALIDATE(cmd);
+	OPS_VALIDATE(internal_flags);
+	OPS_VALIDATE(flags);
+
 	return genl_register_family(family);
 }
+#define genl_ops backport_genl_ops
+
 static inline int
 __real_backport_genl_unregister_family(struct genl_family *family)
 {
@@ -115,6 +174,7 @@ struct backport_genl_family {
 	unsigned int		maxattr;
 	bool			netnsok;
 	bool			parallel_ops;
+	const struct nla_policy *policy;
 	int			(*pre_doit)(__genl_const struct genl_ops *ops,
 					    struct sk_buff *skb,
 					    struct genl_info *info);
diff --git a/backport/backport-include/net/netlink.h b/backport/backport-include/net/netlink.h
index d8237f10..4d73c57f 100644
--- a/backport/backport-include/net/netlink.h
+++ b/backport/backport-include/net/netlink.h
@@ -4,20 +4,7 @@
 #include <linux/version.h>
 #include <linux/in6.h>
 
-#if LINUX_VERSION_IS_LESS(5,1,0)
-#undef NLA_POLICY_NESTED
-#undef NLA_POLICY_NESTED_ARRAY
-#define _NLA_POLICY_NESTED(maxattr, policy) \
-	{ .type = NLA_NESTED, .validation_data = policy, .len = maxattr }
-#define _NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
-	{ .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr }
-#define NLA_POLICY_NESTED(policy) \
-	_NLA_POLICY_NESTED(ARRAY_SIZE(policy) - 1, policy)
-#define NLA_POLICY_NESTED_ARRAY(policy) \
-	_NLA_POLICY_NESTED_ARRAY(ARRAY_SIZE(policy) - 1, policy)
-#endif /* < 5.1 */
-
-#if LINUX_VERSION_IS_LESS(4,20,0)
+#if LINUX_VERSION_IS_LESS(5,2,0)
 /* can't backport using the enum - need to override */
 #define NLA_UNSPEC		0
 #define NLA_U8			1
@@ -39,17 +26,10 @@
 #define NLA_REJECT		17
 #define NLA_EXACT_LEN		18
 #define NLA_EXACT_LEN_WARN	19
-#define __NLA_TYPE_MAX		20
+#define NLA_MIN_LEN		20
+#define __NLA_TYPE_MAX		21
 #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 backport_nla_policy {
 	u8		type;
 	u8		validation_type;
@@ -61,77 +41,169 @@ struct backport_nla_policy {
 		};
 		int (*validate)(const struct nlattr *attr,
 				struct netlink_ext_ack *extack);
+		u16 strict_start_type;
 	};
 };
 #define nla_policy backport_nla_policy
 
-#define NLA_POLICY_EXACT_LEN(_len)	{ .type = NLA_EXACT_LEN, .len = _len }
-#define NLA_POLICY_EXACT_LEN_WARN(_len)	{ .type = NLA_EXACT_LEN_WARN, \
-					  .len = _len }
+#define nla_nest_start_noflag LINUX_BACKPORT(nla_nest_start_noflag)
+static inline struct nlattr *nla_nest_start_noflag(struct sk_buff *skb,
+						   int attrtype)
+{
+	struct nlattr *start = (struct nlattr *)skb_tail_pointer(skb);
 
-#define NLA_POLICY_ETH_ADDR		NLA_POLICY_EXACT_LEN(ETH_ALEN)
-#define NLA_POLICY_ETH_ADDR_COMPAT	NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN)
+	if (nla_put(skb, attrtype, 0, NULL) < 0)
+		return NULL;
 
-#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)
+	return start;
+}
 
-#define NLA_POLICY_RANGE(tp, _min, _max) {		\
-	.type = NLA_ENSURE_INT_TYPE(tp),		\
-	.validation_type = NLA_VALIDATE_RANGE,		\
-	.min = _min,					\
-	.max = _max					\
+#define nla_nest_start LINUX_BACKPORT(nla_nest_start)
+static inline struct nlattr *nla_nest_start(struct sk_buff *skb, int attrtype)
+{
+	return nla_nest_start_noflag(skb, attrtype | NLA_F_NESTED);
 }
 
-#define NLA_POLICY_MIN(tp, _min) {			\
-	.type = NLA_ENSURE_INT_TYPE(tp),		\
-	.validation_type = NLA_VALIDATE_MIN,		\
-	.min = _min,					\
+enum netlink_validation {
+	NL_VALIDATE_LIBERAL = 0,
+	NL_VALIDATE_TRAILING = BIT(0),
+	NL_VALIDATE_MAXTYPE = BIT(1),
+	NL_VALIDATE_UNSPEC = BIT(2),
+	NL_VALIDATE_STRICT_ATTRS = BIT(3),
+	NL_VALIDATE_NESTED = BIT(4),
+};
+
+#define NL_VALIDATE_DEPRECATED_STRICT (NL_VALIDATE_TRAILING |\
+				       NL_VALIDATE_MAXTYPE)
+#define NL_VALIDATE_STRICT (NL_VALIDATE_TRAILING |\
+			    NL_VALIDATE_MAXTYPE |\
+			    NL_VALIDATE_UNSPEC |\
+			    NL_VALIDATE_STRICT_ATTRS |\
+			    NL_VALIDATE_NESTED)
+
+#define __nla_validate LINUX_BACKPORT(__nla_validate)
+int __nla_validate(const struct nlattr *head, int len, int maxtype,
+		   const struct nla_policy *policy, unsigned int validate,
+		   struct netlink_ext_ack *extack);
+#define __nla_parse LINUX_BACKPORT(__nla_parse)
+int __nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
+		int len, const struct nla_policy *policy, unsigned int validate,
+		struct netlink_ext_ack *extack);
+
+#define nla_policy_len LINUX_BACKPORT(nla_policy_len)
+int nla_policy_len(const struct nla_policy *, int);
+
+#define nla_parse LINUX_BACKPORT(nla_parse)
+static inline int nla_parse(struct nlattr **tb, int maxtype,
+			    const struct nlattr *head, int len,
+			    const struct nla_policy *policy,
+			    struct netlink_ext_ack *extack)
+{
+	return __nla_parse(tb, maxtype, head, len, policy,
+			   NL_VALIDATE_STRICT, extack);
 }
 
-#define NLA_POLICY_MAX(tp, _max) {			\
-	.type = NLA_ENSURE_INT_TYPE(tp),		\
-	.validation_type = NLA_VALIDATE_MAX,		\
-	.max = _max,					\
+#define nla_parse_deprecated LINUX_BACKPORT(nla_parse_deprecated)
+static inline int nla_parse_deprecated(struct nlattr **tb, int maxtype,
+				       const struct nlattr *head, int len,
+				       const struct nla_policy *policy,
+				       struct netlink_ext_ack *extack)
+{
+	return __nla_parse(tb, maxtype, head, len, policy,
+			   NL_VALIDATE_LIBERAL, extack);
 }
 
-#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,				\
+#define nla_parse_deprecated_strict LINUX_BACKPORT(nla_parse_deprecated_strict)
+static inline int nla_parse_deprecated_strict(struct nlattr **tb, int maxtype,
+					      const struct nlattr *head,
+					      int len,
+					      const struct nla_policy *policy,
+					      struct netlink_ext_ack *extack)
+{
+	return __nla_parse(tb, maxtype, head, len, policy,
+			   NL_VALIDATE_DEPRECATED_STRICT, extack);
 }
 
-#define nla_validate LINUX_BACKPORT(nla_validate)
-int nla_validate(const struct nlattr *head, int len, int maxtype,
-		 const struct nla_policy *policy,
-		 struct netlink_ext_ack *extack);
-#define nla_parse LINUX_BACKPORT(nla_parse)
-int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
-	      int len, const struct nla_policy *policy,
-	      struct netlink_ext_ack *extack);
-#define nla_policy_len LINUX_BACKPORT(nla_policy_len)
-int nla_policy_len(const struct nla_policy *, int);
+#define __nlmsg_parse LINUX_BACKPORT(__nlmsg_parse)
+static inline int __nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen,
+				struct nlattr *tb[], int maxtype,
+				const struct nla_policy *policy,
+				unsigned int validate,
+				struct netlink_ext_ack *extack)
+{
+	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) {
+		NL_SET_ERR_MSG(extack, "Invalid header length");
+		return -EINVAL;
+	}
+
+	return __nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
+			   nlmsg_attrlen(nlh, hdrlen), policy, validate,
+			   extack);
+}
 
 #define nlmsg_parse LINUX_BACKPORT(nlmsg_parse)
 static inline int nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen,
 			      struct nlattr *tb[], int maxtype,
 			      const struct nla_policy *policy,
 			      struct netlink_ext_ack *extack)
+{
+	return __nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
+			   nlmsg_attrlen(nlh, hdrlen), policy,
+			   NL_VALIDATE_STRICT, extack);
+}
+
+#define nlmsg_parse_deprecated LINUX_BACKPORT(nlmsg_parse_deprecated)
+static inline int nlmsg_parse_deprecated(const struct nlmsghdr *nlh, int hdrlen,
+					 struct nlattr *tb[], int maxtype,
+					 const struct nla_policy *policy,
+					 struct netlink_ext_ack *extack)
+{
+	return __nlmsg_parse(nlh, hdrlen, tb, maxtype, policy,
+			     NL_VALIDATE_LIBERAL, extack);
+}
+
+#define nlmsg_parse_deprecated_strict LINUX_BACKPORT(nlmsg_parse_deprecated_strict)
+static inline int
+nlmsg_parse_deprecated_strict(const struct nlmsghdr *nlh, int hdrlen,
+			      struct nlattr *tb[], int maxtype,
+			      const struct nla_policy *policy,
+			      struct netlink_ext_ack *extack)
+{
+	return __nlmsg_parse(nlh, hdrlen, tb, maxtype, policy,
+			     NL_VALIDATE_DEPRECATED_STRICT, extack);
+}
+
+#define nla_validate_deprecated LINUX_BACKPORT(nla_validate_deprecated)
+static inline int nla_validate_deprecated(const struct nlattr *head, int len,
+					  int maxtype,
+					  const struct nla_policy *policy,
+					  struct netlink_ext_ack *extack)
+{
+	return __nla_validate(head, len, maxtype, policy, NL_VALIDATE_LIBERAL,
+			      extack);
+}
+
+#define nla_validate LINUX_BACKPORT(nla_validate)
+static inline int nla_validate(const struct nlattr *head, int len, int maxtype,
+			       const struct nla_policy *policy,
+			       struct netlink_ext_ack *extack)
+{
+	return __nla_validate(head, len, maxtype, policy, NL_VALIDATE_STRICT,
+			      extack);
+}
+
+#define nlmsg_validate_deprecated LINUX_BACKPORT(nlmsg_validate_deprecated)
+static inline int nlmsg_validate_deprecated(const struct nlmsghdr *nlh,
+					    int hdrlen, int maxtype,
+					    const struct nla_policy *policy,
+					    struct netlink_ext_ack *extack)
 {
 	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
 		return -EINVAL;
 
-	return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
-			 nlmsg_attrlen(nlh, hdrlen), policy, extack);
+	return __nla_validate(nlmsg_attrdata(nlh, hdrlen),
+			      nlmsg_attrlen(nlh, hdrlen), maxtype,
+			      policy, NL_VALIDATE_LIBERAL, extack);
 }
 
 #define nlmsg_validate LINUX_BACKPORT(nlmsg_validate)
@@ -143,9 +215,9 @@ static inline int nlmsg_validate(const struct nlmsghdr *nlh,
 	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
 		return -EINVAL;
 
-	return nla_validate(nlmsg_attrdata(nlh, hdrlen),
-			    nlmsg_attrlen(nlh, hdrlen), maxtype, policy,
-			    extack);
+	return __nla_validate(nlmsg_attrdata(nlh, hdrlen),
+			      nlmsg_attrlen(nlh, hdrlen), maxtype,
+			      policy, NL_VALIDATE_STRICT, extack);
 }
 
 #define nla_parse_nested LINUX_BACKPORT(nla_parse_nested)
@@ -154,17 +226,111 @@ static inline int nla_parse_nested(struct nlattr *tb[], int maxtype,
 				   const struct nla_policy *policy,
 				   struct netlink_ext_ack *extack)
 {
-	return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy,
-			 extack);
+	if (!(nla->nla_type & NLA_F_NESTED)) {
+		NL_SET_ERR_MSG_ATTR(extack, nla, "NLA_F_NESTED is missing");
+		return -EINVAL;
+	}
+
+	return __nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy,
+			   NL_VALIDATE_STRICT, extack);
 }
 
-#define nla_validate_nested LINUX_BACKPORT(nla_validate_nested)
-static inline int nla_validate_nested(const struct nlattr *start, int maxtype,
-				      const struct nla_policy *policy,
-				      struct netlink_ext_ack *extack)
+#define nla_parse_nested_deprecated LINUX_BACKPORT(nla_parse_nested_deprecated)
+static inline int nla_parse_nested_deprecated(struct nlattr *tb[], int maxtype,
+					      const struct nlattr *nla,
+					      const struct nla_policy *policy,
+					      struct netlink_ext_ack *extack)
 {
-	return nla_validate(nla_data(start), nla_len(start), maxtype, policy,
-			    extack);
+	return __nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy,
+			   NL_VALIDATE_LIBERAL, extack);
+}
+
+#define __nla_validate_nested LINUX_BACKPORT(__nla_validate_nested)
+static inline int __nla_validate_nested(const struct nlattr *start, int maxtype,
+					const struct nla_policy *policy,
+					unsigned int validate,
+					struct netlink_ext_ack *extack)
+{
+	return __nla_validate(nla_data(start), nla_len(start), maxtype, policy,
+			      validate, extack);
+}
+
+#define nla_validate_nested_deprecated LINUX_BACKPORT(nla_validate_nested_deprecated)
+static inline int
+nla_validate_nested_deprecated(const struct nlattr *start, int maxtype,
+			       const struct nla_policy *policy,
+			       struct netlink_ext_ack *extack)
+{
+	return __nla_validate_nested(start, maxtype, policy,
+				     NL_VALIDATE_LIBERAL, extack);
+}
+#endif /* < 5.2 */
+
+#if LINUX_VERSION_IS_LESS(5,1,0)
+#undef NLA_POLICY_NESTED
+#undef NLA_POLICY_NESTED_ARRAY
+#define _NLA_POLICY_NESTED(maxattr, policy) \
+	{ .type = NLA_NESTED, .validation_data = policy, .len = maxattr }
+#define _NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
+	{ .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr }
+#define NLA_POLICY_NESTED(policy) \
+	_NLA_POLICY_NESTED(ARRAY_SIZE(policy) - 1, policy)
+#define NLA_POLICY_NESTED_ARRAY(policy) \
+	_NLA_POLICY_NESTED_ARRAY(ARRAY_SIZE(policy) - 1, policy)
+#endif /* < 5.1 */
+
+#if LINUX_VERSION_IS_LESS(4,20,0)
+enum nla_policy_validation {
+	NLA_VALIDATE_NONE,
+	NLA_VALIDATE_RANGE,
+	NLA_VALIDATE_MIN,
+	NLA_VALIDATE_MAX,
+	NLA_VALIDATE_FUNCTION,
+};
+
+#define NLA_POLICY_EXACT_LEN(_len)	{ .type = NLA_EXACT_LEN, .len = _len }
+#define NLA_POLICY_EXACT_LEN_WARN(_len)	{ .type = NLA_EXACT_LEN_WARN, \
+					  .len = _len }
+
+#define NLA_POLICY_ETH_ADDR		NLA_POLICY_EXACT_LEN(ETH_ALEN)
+#define NLA_POLICY_ETH_ADDR_COMPAT	NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN)
+
+#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,				\
 }
 #endif /* < 4.20 */
 
@@ -255,21 +421,6 @@ static inline int _nla_parse_nested4(struct nlattr *tb[], int maxtype,
 #undef nla_parse_nested
 #define nla_parse_nested(...) \
 	macro_dispatcher(_nla_parse_nested, __VA_ARGS__)(__VA_ARGS__)
-
-static inline int _nla_validate_nested4(const struct nlattr *start, int maxtype,
-					const struct nla_policy *policy,
-					struct netlink_ext_ack *extack)
-{
-	return nla_validate_nested(start, maxtype, policy, extack);
-}
-static inline int _nla_validate_nested3(const struct nlattr *start, int maxtype,
-					const struct nla_policy *policy)
-{
-	return nla_validate_nested(start, maxtype, policy, NULL);
-}
-#undef nla_validate_nested
-#define nla_validate_nested(...) \
-	macro_dispatcher(_nla_validate_nested, __VA_ARGS__)(__VA_ARGS__)
 #endif /* LINUX_VERSION_IS_LESS(4,12,0) */
 
 #if LINUX_VERSION_IS_LESS(3,7,0)
diff --git a/backport/compat/Kconfig b/backport/compat/Kconfig
index 20f34f1a..1d85a5da 100644
--- a/backport/compat/Kconfig
+++ b/backport/compat/Kconfig
@@ -62,6 +62,11 @@ config BP_MODULES
 	This symbol is necessary for the newer kconf tool, it looks
 	for the "option modules" to control the 'm' state.
 
+config BPAUTO_BUILD_NLATTR
+	def_bool y
+	depends on KERNEL_5_2
+	#c-file lib/nlattr.c
+
 config BPAUTO_BUILD_CORDIC
 	tristate
 	depends on !CORDIC
diff --git a/backport/compat/Makefile b/backport/compat/Makefile
index 99b52faa..e92e3120 100644
--- a/backport/compat/Makefile
+++ b/backport/compat/Makefile
@@ -37,9 +37,7 @@ compat-$(CPTCFG_KERNEL_4_7) += backport-4.7.o
 compat-$(CPTCFG_KERNEL_4_8) += backport-4.8.o
 compat-$(CPTCFG_KERNEL_4_10) += backport-4.10.o
 compat-$(CPTCFG_KERNEL_4_18) += backport-4.18.o
-compat-$(CPTCFG_KERNEL_4_20) += backport-4.20.o
-
-compat-$(CPTCFG_KERNEL_4_20) += backport-genetlink.o
+compat-$(CPTCFG_KERNEL_5_2) += backport-5.2.o backport-genetlink.o
 
 compat-$(CPTCFG_BPAUTO_BUILD_SYSTEM_DATA_VERIFICATION) += verification/verify.o
 compat-$(CPTCFG_BPAUTO_BUILD_SYSTEM_DATA_VERIFICATION) += verification/pkcs7.asn1.o
diff --git a/backport/compat/backport-4.20.c b/backport/compat/backport-5.2.c
similarity index 58%
rename from backport/compat/backport-4.20.c
rename to backport/compat/backport-5.2.c
index e26f3b52..5624d5fa 100644
--- a/backport/compat/backport-4.20.c
+++ b/backport/compat/backport-5.2.c
@@ -1,19 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (C) 2018 Intel Corporation
+ * NETLINK      Netlink attributes
  *
- * Backport functionality introduced in Linux 4.20.
- * This is basically upstream lib/nlattr.c.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * 		Authors:	Thomas Graf <tgraf@suug.ch>
+ * 				Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
  */
-#include <linux/kernel.h>
+
 #include <linux/export.h>
+#include <linux/kernel.h>
 #include <linux/errno.h>
+#include <linux/jiffies.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
 #include <linux/types.h>
 #include <net/netlink.h>
 
+/* For these data types, attribute length should be exactly the given
+ * size. However, to maintain compatibility with broken commands, if the
+ * attribute length does not match the expected size a warning is emitted
+ * to the user that the command is sending invalid data and needs to be fixed.
+ */
 static const u8 nla_attr_len[NLA_TYPE_MAX+1] = {
 	[NLA_U8]	= sizeof(u8),
 	[NLA_U16]	= sizeof(u16),
@@ -63,7 +69,8 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
 
 static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
 			      const struct nla_policy *policy,
-			      struct netlink_ext_ack *extack)
+			      struct netlink_ext_ack *extack,
+			      unsigned int validate)
 {
 	const struct nlattr *entry;
 	int rem;
@@ -80,8 +87,8 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
 			return -ERANGE;
 		}
 
-		ret = nla_validate(nla_data(entry), nla_len(entry),
-				   maxtype, policy, extack);
+		ret = __nla_validate(nla_data(entry), nla_len(entry),
+				     maxtype, policy, validate, extack);
 		if (ret < 0)
 			return ret;
 	}
@@ -148,13 +155,17 @@ static int nla_validate_int_range(const struct nla_policy *pt,
 }
 
 static int validate_nla(const struct nlattr *nla, int maxtype,
-			const struct nla_policy *policy,
+			const struct nla_policy *policy, unsigned int validate,
 			struct netlink_ext_ack *extack)
 {
+	u16 strict_start_type = policy[0].strict_start_type;
 	const struct nla_policy *pt;
 	int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla);
 	int err = -ERANGE;
 
+	if (strict_start_type && type >= strict_start_type)
+		validate |= NL_VALIDATE_STRICT;
+
 	if (type <= 0 || type > maxtype)
 		return 0;
 
@@ -166,6 +177,26 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 	    (pt->type == NLA_EXACT_LEN_WARN && attrlen != pt->len)) {
 		pr_warn_ratelimited("netlink: '%s': attribute type %d has an invalid length.\n",
 				    current->comm, type);
+		if (validate & NL_VALIDATE_STRICT_ATTRS) {
+			NL_SET_ERR_MSG_ATTR(extack, nla,
+					    "invalid attribute length");
+			return -EINVAL;
+		}
+	}
+
+	if (validate & NL_VALIDATE_NESTED) {
+		if ((pt->type == NLA_NESTED || pt->type == NLA_NESTED_ARRAY) &&
+		    !(nla->nla_type & NLA_F_NESTED)) {
+			NL_SET_ERR_MSG_ATTR(extack, nla,
+					    "NLA_F_NESTED is missing");
+			return -EINVAL;
+		}
+		if (pt->type != NLA_NESTED && pt->type != NLA_NESTED_ARRAY &&
+		    pt->type != NLA_UNSPEC && (nla->nla_type & NLA_F_NESTED)) {
+			NL_SET_ERR_MSG_ATTR(extack, nla,
+					    "NLA_F_NESTED not expected");
+			return -EINVAL;
+		}
 	}
 
 	switch (pt->type) {
@@ -238,8 +269,9 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 		if (attrlen < NLA_HDRLEN)
 			goto out_err;
 		if (pt->validation_data) {
-			err = nla_validate(nla_data(nla), nla_len(nla), pt->len,
-					   pt->validation_data, extack);
+			err = __nla_validate(nla_data(nla), nla_len(nla), pt->len,
+					     pt->validation_data, validate,
+					     extack);
 			if (err < 0) {
 				/*
 				 * return directly to preserve the inner
@@ -262,7 +294,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 
 			err = nla_validate_array(nla_data(nla), nla_len(nla),
 						 pt->len, pt->validation_data,
-						 extack);
+						 extack, validate);
 			if (err < 0) {
 				/*
 				 * return directly to preserve the inner
@@ -272,10 +304,23 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 			}
 		}
 		break;
+
+	case NLA_UNSPEC:
+		if (validate & NL_VALIDATE_UNSPEC) {
+			NL_SET_ERR_MSG_ATTR(extack, nla,
+					    "Unsupported attribute");
+			return -EINVAL;
+		}
+		/* fall through */
+	case NLA_MIN_LEN:
+		if (attrlen < pt->len)
+			goto out_err;
+		break;
+
 	default:
 		if (pt->len)
 			minlen = pt->len;
-		else if (pt->type != NLA_UNSPEC)
+		else
 			minlen = nla_attr_minlen[pt->type];
 
 		if (attrlen < minlen)
@@ -309,25 +354,90 @@ out_err:
 	return err;
 }
 
-int backport_nla_validate(const struct nlattr *head, int len, int maxtype,
-			  const struct nla_policy *policy,
-			  struct netlink_ext_ack *extack)
+static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
+				const struct nla_policy *policy,
+				unsigned int validate,
+				struct netlink_ext_ack *extack,
+				struct nlattr **tb)
 {
 	const struct nlattr *nla;
 	int rem;
 
+	if (tb)
+		memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
+
 	nla_for_each_attr(nla, head, len, rem) {
-		int err = validate_nla(nla, maxtype, policy, extack);
+		u16 type = nla_type(nla);
 
-		if (err < 0)
-			return err;
+		if (type == 0 || type > maxtype) {
+			if (validate & NL_VALIDATE_MAXTYPE) {
+				NL_SET_ERR_MSG_ATTR(extack, nla,
+						    "Unknown attribute type");
+				return -EINVAL;
+			}
+			continue;
+		}
+		if (policy) {
+			int err = validate_nla(nla, maxtype, policy,
+					       validate, extack);
+
+			if (err < 0)
+				return err;
+		}
+
+		if (tb)
+			tb[type] = (struct nlattr *)nla;
+	}
+
+	if (unlikely(rem > 0)) {
+		pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
+				    rem, current->comm);
+		NL_SET_ERR_MSG(extack, "bytes leftover after parsing attributes");
+		if (validate & NL_VALIDATE_TRAILING)
+			return -EINVAL;
 	}
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(backport_nla_validate);
 
-int backport_nla_policy_len(const struct nla_policy *p, int n)
+/**
+ * __nla_validate - Validate a stream of attributes
+ * @head: head of attribute stream
+ * @len: length of attribute stream
+ * @maxtype: maximum attribute type to be expected
+ * @policy: validation policy
+ * @validate: validation strictness
+ * @extack: extended ACK report struct
+ *
+ * Validates all attributes in the specified attribute stream against the
+ * specified policy. Validation depends on the validate flags passed, see
+ * &enum netlink_validation for more details on that.
+ * See documenation of struct nla_policy for more details.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int __nla_validate(const struct nlattr *head, int len, int maxtype,
+		   const struct nla_policy *policy, unsigned int validate,
+		   struct netlink_ext_ack *extack)
+{
+	return __nla_validate_parse(head, len, maxtype, policy, validate,
+				    extack, NULL);
+}
+EXPORT_SYMBOL(__nla_validate);
+
+/**
+ * nla_policy_len - Determin the max. length of a policy
+ * @policy: policy to use
+ * @n: number of policies
+ *
+ * Determines the max. length of the policy.  It is currently used
+ * to allocated Netlink buffers roughly the size of the actual
+ * message.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+nla_policy_len(const struct nla_policy *p, int n)
 {
 	int i, len = 0;
 
@@ -342,38 +452,30 @@ int backport_nla_policy_len(const struct nla_policy *p, int n)
 
 	return len;
 }
-EXPORT_SYMBOL_GPL(backport_nla_policy_len);
-
-int backport_nla_parse(struct nlattr **tb, int maxtype,
-		       const struct nlattr *head,
-		       int len, const struct nla_policy *policy,
-		       struct netlink_ext_ack *extack)
+EXPORT_SYMBOL(nla_policy_len);
+
+/**
+ * __nla_parse - Parse a stream of attributes into a tb buffer
+ * @tb: destination array with maxtype+1 elements
+ * @maxtype: maximum attribute type to be expected
+ * @head: head of attribute stream
+ * @len: length of attribute stream
+ * @policy: validation policy
+ * @validate: validation strictness
+ * @extack: extended ACK pointer
+ *
+ * Parses a stream of attributes and stores a pointer to each attribute in
+ * the tb array accessible via the attribute type.
+ * Validation is controlled by the @validate parameter.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int __nla_parse(struct nlattr **tb, int maxtype,
+		const struct nlattr *head, int len,
+		const struct nla_policy *policy, unsigned int validate,
+		struct netlink_ext_ack *extack)
 {
-	const struct nlattr *nla;
-	int rem;
-
-	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
-
-	nla_for_each_attr(nla, head, len, rem) {
-		u16 type = nla_type(nla);
-
-		if (type > 0 && type <= maxtype) {
-			if (policy) {
-				int err = validate_nla(nla, maxtype, policy,
-						       extack);
-
-				if (err < 0)
-					return err;
-			}
-
-			tb[type] = (struct nlattr *)nla;
-		}
-	}
-
-	if (unlikely(rem > 0))
-		pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
-				    rem, current->comm);
-
-	return 0;
+	return __nla_validate_parse(head, len, maxtype, policy, validate,
+				    extack, tb);
 }
-EXPORT_SYMBOL(backport_nla_parse);
+EXPORT_SYMBOL(__nla_parse);
diff --git a/backport/compat/backport-genetlink.c b/backport/compat/backport-genetlink.c
index 16971ec3..47078589 100644
--- a/backport/compat/backport-genetlink.c
+++ b/backport/compat/backport-genetlink.c
@@ -167,8 +167,15 @@ static int backport_pre_doit(__genl_const struct genl_ops *ops,
 	struct netlink_ext_ack *extack = info->extack;
 #endif
 
-	err = nlmsg_validate(info->nlhdr, GENL_HDRLEN + family->hdrsize,
-			     family->maxattr, ops->policy, extack);
+	if (ops->validate & GENL_DONT_VALIDATE_STRICT)
+		err = nlmsg_validate_deprecated(info->nlhdr,
+						GENL_HDRLEN + family->hdrsize,
+						family->maxattr, family->policy,
+						extack);
+	else
+		err = nlmsg_validate(info->nlhdr, GENL_HDRLEN + family->hdrsize,
+				     family->maxattr, family->policy, extack);
+
 	if (!err && family->pre_doit)
 		err = family->pre_doit(ops, skb, info);
 
@@ -230,11 +237,15 @@ int backport_genl_register_family(struct genl_family *family)
 	 * memory layout isn't compatible with the old version
 	 */
 	for (i = 0; i < family->n_ops; i++) {
-		ops[i].policy = NULL;
 #if LINUX_VERSION_IS_LESS(4,12,0)
 		if (ops[i].doit)
 			ops[i].doit = extack_doit;
 #endif
+/*
+ * TODO: add dumpit redirect (like extack_doit) that will
+ *       make this code honor !GENL_DONT_VALIDATE_DUMP and
+ *       actually validate in this case ...
+ */
 	}
 	/* keep doit/dumpit NULL - that's invalid */
 	ops[family->n_ops].done = (void *)family;
@@ -248,12 +259,13 @@ int backport_genl_register_family(struct genl_family *family)
 #if LINUX_VERSION_IS_GEQ(3,10,0)
 	COPY(parallel_ops);
 #endif
-	family->family.pre_doit = backport_pre_doit;
-	family->family.post_doit = backport_post_doit;
+	/* The casts are OK - we checked everything is the same offset in genl_ops */
+	family->family.pre_doit = (void *)backport_pre_doit;
+	family->family.post_doit = (void *)backport_post_doit;
 	/* attrbuf is output only */
-	family->copy_ops = ops;
+	family->copy_ops = (void *)ops;
 #if LINUX_VERSION_IS_GEQ(3,13,0)
-	family->family.ops = ops;
+	family->family.ops = (void *)ops;
 	COPY(mcgrps);
 	COPY(n_ops);
 	COPY(n_mcgrps);
-- 
2.20.1

--
To unsubscribe from this list: send the line "unsubscribe backports" in

  parent reply index

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-01 21:48 [PATCH 00/18] backports: Update for kernel 5.2 Hauke Mehrtens
2019-07-01 21:48 ` [PATCH 01/18] patches: update select queue patches Hauke Mehrtens
2019-07-01 21:48 ` [PATCH 02/18] backport: rcupdate: add rcu_head_init and rcu_head_after_call_rcu Hauke Mehrtens
2019-07-01 21:48 ` [PATCH 03/18] backport: check for failure when allocating ops in genetlink Hauke Mehrtens
2019-07-01 21:48 ` [PATCH 04/18] backport-include: add empty lockdep_map structure in lockdep.h Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 05/18] patches: Make patches apply on top of kernel 5.2-rc6 Hauke Mehrtens
2019-07-01 21:49 ` Hauke Mehrtens [this message]
2019-07-01 21:49 ` [PATCH 07/18] header: Add backport-include/net/ipv6_stubs.h Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 08/18] header: Add sdio_retune*() functions Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 09/18] header: Add HRTIMER_MODE_{ABS,REL}_SOFT Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 10/18] patch: Remove usage of DMI_PRODUCT_SKU Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 11/18] header: Make napi_complete_done() return bool Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 12/18] header: add hrtimer_forward() and ns_to_ktime() Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 13/18] dependencies: Add MT7615E dependency Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 14/18] patches: rtw88: Add missing include on kernel 3.18 Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 14/18] " Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 15/18] header: Remove include/net/inet_frag.h Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 16/18] defconfig: update wifi defconfig Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 17/18] header: fix of_get_mac_address() Hauke Mehrtens
2019-07-01 21:49 ` [PATCH 18/18] header: add support for GCC7 and GCC8 Hauke Mehrtens
2019-07-02  7:44   ` Johannes Berg

Reply instructions:

You may reply publically 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=20190701214914.8066-7-hauke@hauke-m.de \
    --to=hauke@hauke-m.de \
    --cc=backports@vger.kernel.org \
    --cc=johannes.berg@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Backports Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/backports/0 backports/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 backports backports/ https://lore.kernel.org/backports \
		backports@vger.kernel.org backports@archiver.kernel.org
	public-inbox-index backports


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.backports


AGPL code for this site: git clone https://public-inbox.org/ public-inbox