@@ -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);
@@ -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)
@@ -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
@@ -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
similarity index 58%
rename from backport/compat/backport-4.20.c
rename to 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);
@@ -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);