Last active
September 21, 2020 14:03
-
-
Save 0x7f454c46/8f68311dfa1f240959fdbe7c77ed2259 to your computer and use it in GitHub Desktop.
xfrm-compat v2-v3 diff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/include/net/xfrm.h b/include/net/xfrm.h | |
index 633c210bd2dd..53618a31634b 100644 | |
--- a/include/net/xfrm.h | |
+++ b/include/net/xfrm.h | |
@@ -2000,35 +2000,36 @@ static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x, | |
return 0; | |
} | |
+extern const int xfrm_msg_min[XFRM_NR_MSGTYPES]; | |
extern const struct nla_policy xfrma_policy[XFRMA_MAX+1]; | |
-#ifdef CONFIG_XFRM_USER_COMPAT | |
-extern int xfrm_alloc_compat(struct sk_buff *skb); | |
-extern int __xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh); | |
-extern const int xfrm_msg_min[XFRM_NR_MSGTYPES]; | |
-extern struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *nlh, | |
+struct xfrm_translator { | |
+ /* Allocate frag_list and put compat translation there */ | |
+ int (*alloc_compat)(struct sk_buff *skb, const struct nlmsghdr *src); | |
+ | |
+ /* Allocate nlmsg with 64-bit translaton of received 32-bit message */ | |
+ struct nlmsghdr *(*rcv_msg_compat)(const struct nlmsghdr *nlh, | |
int maxtype, const struct nla_policy *policy, | |
struct netlink_ext_ack *extack); | |
-extern int xfrm_user_policy_compat(u8 **pdata32, int optlen); | |
+ | |
+ /* Translate 32-bit user_policy from sockptr */ | |
+ int (*xlate_user_policy_sockptr)(u8 **pdata32, int optlen); | |
+ | |
+ struct module *owner; | |
+}; | |
+ | |
+#if IS_ENABLED(CONFIG_XFRM_USER_COMPAT) | |
+extern int xfrm_register_translator(struct xfrm_translator *xtr); | |
+extern int xfrm_unregister_translator(struct xfrm_translator *xtr); | |
+extern struct xfrm_translator *xfrm_get_translator(void); | |
+extern void xfrm_put_translator(struct xfrm_translator *xtr); | |
#else | |
-static inline int xfrm_alloc_compat(struct sk_buff *skb) | |
+static inline struct xfrm_translator *xfrm_get_translator(void) | |
{ | |
- return 0; | |
-} | |
-static inline int __xfrm_alloc_compat(struct sk_buff *skb, | |
- const struct nlmsghdr *nlh) | |
-{ | |
- return 0; | |
-} | |
-static inline struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *nlh, | |
- int maxtype, const struct nla_policy *policy, | |
- struct netlink_ext_ack *extack) | |
-{ | |
- return ERR_PTR(-EOPNOTSUPP); | |
+ return NULL; | |
} | |
-static inline int xfrm_user_policy_compat(u8 **pdata32, int optlen) | |
+static inline void xfrm_put_translator(struct xfrm_translator *xtr) | |
{ | |
- return -EOPNOTSUPP; | |
} | |
#endif | |
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c | |
index 97c61e78103e..de12dd3136f9 100644 | |
--- a/net/netlink/af_netlink.c | |
+++ b/net/netlink/af_netlink.c | |
@@ -2198,8 +2198,7 @@ static int netlink_dump_done(struct netlink_sock *nlk, struct sk_buff *skb, | |
return -ENOBUFS; | |
nl_dump_check_consistent(cb, nlh); | |
- memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, | |
- sizeof(nlk->dump_done_errno)); | |
+ memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, sizeof(nlk->dump_done_errno)); | |
if (extack->_msg && nlk->flags & NETLINK_F_EXT_ACK) { | |
nlh->nlmsg_flags |= NLM_F_ACK_TLVS; | |
diff --git a/net/xfrm/xfrm_compat.c b/net/xfrm/xfrm_compat.c | |
index 990eecfc4c0e..e28f0c9ecd6a 100644 | |
--- a/net/xfrm/xfrm_compat.c | |
+++ b/net/xfrm/xfrm_compat.c | |
@@ -308,7 +308,7 @@ static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src) | |
return 0; | |
} | |
-int __xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src) | |
+static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src) | |
{ | |
u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE; | |
struct sk_buff *new = NULL; | |
@@ -336,13 +336,6 @@ int __xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src) | |
return 0; | |
} | |
-int xfrm_alloc_compat(struct sk_buff *skb) | |
-{ | |
- const struct nlmsghdr *nlh_src = nlmsg_hdr(skb); | |
- | |
- return __xfrm_alloc_compat(skb, nlh_src); | |
-} | |
- | |
/* Calculates len of translated 64-bit message. */ | |
static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src, | |
struct nlattr *attrs[XFRMA_MAX+1]) | |
@@ -518,7 +511,7 @@ static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src, | |
NL_SET_ERR_MSG(extack, "Unsupported message type"); | |
return -EOPNOTSUPP; | |
} | |
- pos += dst->nlmsg_len; | |
+ pos = dst->nlmsg_len; | |
for (i = 1; i < XFRMA_MAX + 1; i++) { | |
int err; | |
@@ -537,7 +530,7 @@ static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src, | |
return 0; | |
} | |
-struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32, | |
+static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32, | |
int maxtype, const struct nla_policy *policy, | |
struct netlink_ext_ack *extack) | |
{ | |
@@ -583,7 +576,7 @@ struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32, | |
return h64; | |
} | |
-int xfrm_user_policy_compat(u8 **pdata32, int optlen) | |
+static int xfrm_user_policy_compat(u8 **pdata32, int optlen) | |
{ | |
struct compat_xfrm_userpolicy_info *p = (void *)*pdata32; | |
u8 *src_templates, *dst_templates; | |
@@ -607,3 +600,26 @@ int xfrm_user_policy_compat(u8 **pdata32, int optlen) | |
*pdata32 = data64; | |
return 0; | |
} | |
+ | |
+static struct xfrm_translator xfrm_translator = { | |
+ .owner = THIS_MODULE, | |
+ .alloc_compat = xfrm_alloc_compat, | |
+ .rcv_msg_compat = xfrm_user_rcv_msg_compat, | |
+ .xlate_user_policy_sockptr = xfrm_user_policy_compat, | |
+}; | |
+ | |
+static int __init xfrm_compat_init(void) | |
+{ | |
+ return xfrm_register_translator(&xfrm_translator); | |
+} | |
+ | |
+static void __exit xfrm_compat_exit(void) | |
+{ | |
+ xfrm_unregister_translator(&xfrm_translator); | |
+} | |
+ | |
+module_init(xfrm_compat_init); | |
+module_exit(xfrm_compat_exit); | |
+MODULE_LICENSE("GPL"); | |
+MODULE_AUTHOR("Dmitry Safonov"); | |
+MODULE_DESCRIPTION("XFRM 32-bit compatibility layer"); | |
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c | |
index 053e6fe6ea7a..f9961884500b 100644 | |
--- a/net/xfrm/xfrm_state.c | |
+++ b/net/xfrm/xfrm_state.c | |
@@ -2264,6 +2264,66 @@ static bool km_is_alive(const struct km_event *c) | |
return is_alive; | |
} | |
+#if IS_ENABLED(CONFIG_XFRM_USER_COMPAT) | |
+static DEFINE_SPINLOCK(xfrm_translator_lock); | |
+static struct xfrm_translator __rcu *xfrm_translator; | |
+ | |
+struct xfrm_translator *xfrm_get_translator(void) | |
+{ | |
+ struct xfrm_translator *xtr; | |
+ | |
+ rcu_read_lock(); | |
+ xtr = rcu_dereference(xfrm_translator); | |
+ if (unlikely(!xtr)) | |
+ goto out; | |
+ if (!try_module_get(xtr->owner)) | |
+ xtr = NULL; | |
+out: | |
+ rcu_read_unlock(); | |
+ return xtr; | |
+} | |
+EXPORT_SYMBOL_GPL(xfrm_get_translator); | |
+ | |
+void xfrm_put_translator(struct xfrm_translator *xtr) | |
+{ | |
+ module_put(xtr->owner); | |
+} | |
+EXPORT_SYMBOL_GPL(xfrm_put_translator); | |
+ | |
+int xfrm_register_translator(struct xfrm_translator *xtr) | |
+{ | |
+ int err = 0; | |
+ | |
+ spin_lock_bh(&xfrm_translator_lock); | |
+ if (unlikely(xfrm_translator != NULL)) | |
+ err = -EEXIST; | |
+ else | |
+ rcu_assign_pointer(xfrm_translator, xtr); | |
+ spin_unlock_bh(&xfrm_translator_lock); | |
+ | |
+ return err; | |
+} | |
+EXPORT_SYMBOL_GPL(xfrm_register_translator); | |
+ | |
+int xfrm_unregister_translator(struct xfrm_translator *xtr) | |
+{ | |
+ int err = 0; | |
+ | |
+ spin_lock_bh(&xfrm_translator_lock); | |
+ if (likely(xfrm_translator != NULL)) { | |
+ if (rcu_access_pointer(xfrm_translator) != xtr) | |
+ err = -EINVAL; | |
+ else | |
+ RCU_INIT_POINTER(xfrm_translator, NULL); | |
+ } | |
+ spin_unlock_bh(&xfrm_translator_lock); | |
+ synchronize_rcu(); | |
+ | |
+ return err; | |
+} | |
+EXPORT_SYMBOL_GPL(xfrm_unregister_translator); | |
+#endif | |
+ | |
int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen) | |
{ | |
int err; | |
@@ -2286,7 +2346,13 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen) | |
return PTR_ERR(data); | |
if (in_compat_syscall()) { | |
- err = xfrm_user_policy_compat(&data, optlen); | |
+ struct xfrm_translator *xtr = xfrm_get_translator(); | |
+ | |
+ if (!xtr) | |
+ return -EOPNOTSUPP; | |
+ | |
+ err = xtr->xlate_user_policy_sockptr(&data, optlen); | |
+ xfrm_put_translator(xtr); | |
if (err) { | |
kfree(data); | |
return err; | |
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c | |
index c00eeb5503bc..d0c32a8fcc4a 100644 | |
--- a/net/xfrm/xfrm_user.c | |
+++ b/net/xfrm/xfrm_user.c | |
@@ -975,6 +975,7 @@ static int dump_one_state(struct xfrm_state *x, int count, void *ptr) | |
struct xfrm_dump_info *sp = ptr; | |
struct sk_buff *in_skb = sp->in_skb; | |
struct sk_buff *skb = sp->out_skb; | |
+ struct xfrm_translator *xtr; | |
struct xfrm_usersa_info *p; | |
struct nlmsghdr *nlh; | |
int err; | |
@@ -993,10 +994,15 @@ static int dump_one_state(struct xfrm_state *x, int count, void *ptr) | |
} | |
nlmsg_end(skb, nlh); | |
- err = __xfrm_alloc_compat(skb, nlh); | |
- if (err) { | |
- nlmsg_cancel(skb, nlh); | |
- return err; | |
+ xtr = xfrm_get_translator(); | |
+ if (xtr) { | |
+ err = xtr->alloc_compat(skb, nlh); | |
+ | |
+ xfrm_put_translator(xtr); | |
+ if (err) { | |
+ nlmsg_cancel(skb, nlh); | |
+ return err; | |
+ } | |
} | |
return 0; | |
@@ -1089,17 +1095,22 @@ static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb, | |
u32 pid, unsigned int group) | |
{ | |
struct sock *nlsk = rcu_dereference(net->xfrm.nlsk); | |
- int err; | |
+ struct xfrm_translator *xtr; | |
if (!nlsk) { | |
kfree_skb(skb); | |
return -EPIPE; | |
} | |
- err = xfrm_alloc_compat(skb); | |
- if (err) { | |
- kfree_skb(skb); | |
- return err; | |
+ xtr = xfrm_get_translator(); | |
+ if (xtr) { | |
+ int err = xtr->alloc_compat(skb, nlmsg_hdr(skb)); | |
+ | |
+ xfrm_put_translator(xtr); | |
+ if (err) { | |
+ kfree_skb(skb); | |
+ return err; | |
+ } | |
} | |
return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC); | |
@@ -1321,6 +1332,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, | |
struct net *net = sock_net(skb->sk); | |
struct xfrm_state *x; | |
struct xfrm_userspi_info *p; | |
+ struct xfrm_translator *xtr; | |
struct sk_buff *resp_skb; | |
xfrm_address_t *daddr; | |
int family; | |
@@ -1371,10 +1383,15 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, | |
goto out; | |
} | |
- err = xfrm_alloc_compat(skb); | |
- if (err) { | |
- kfree_skb(resp_skb); | |
- goto out; | |
+ xtr = xfrm_get_translator(); | |
+ if (xtr) { | |
+ err = xtr->alloc_compat(skb, nlmsg_hdr(skb)); | |
+ | |
+ xfrm_put_translator(xtr); | |
+ if (err) { | |
+ kfree_skb(resp_skb); | |
+ goto out; | |
+ } | |
} | |
err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid); | |
@@ -1783,6 +1800,7 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr | |
struct xfrm_userpolicy_info *p; | |
struct sk_buff *in_skb = sp->in_skb; | |
struct sk_buff *skb = sp->out_skb; | |
+ struct xfrm_translator *xtr; | |
struct nlmsghdr *nlh; | |
int err; | |
@@ -1808,10 +1826,15 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr | |
} | |
nlmsg_end(skb, nlh); | |
- err = __xfrm_alloc_compat(skb, nlh); | |
- if (err) { | |
- nlmsg_cancel(skb, nlh); | |
- return err; | |
+ xtr = xfrm_get_translator(); | |
+ if (xtr) { | |
+ err = xtr->alloc_compat(skb, nlh); | |
+ | |
+ xfrm_put_translator(xtr); | |
+ if (err) { | |
+ nlmsg_cancel(skb, nlh); | |
+ return err; | |
+ } | |
} | |
return 0; | |
@@ -2582,6 +2605,7 @@ const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { | |
[XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32), | |
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), | |
}; | |
+EXPORT_SYMBOL_GPL(xfrm_msg_min); | |
#undef XMSGSIZE | |
@@ -2617,6 +2641,7 @@ const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { | |
[XFRMA_SET_MARK_MASK] = { .type = NLA_U32 }, | |
[XFRMA_IF_ID] = { .type = NLA_U32 }, | |
}; | |
+EXPORT_SYMBOL_GPL(xfrma_policy); | |
static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { | |
[XFRMA_SPD_IPV4_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) }, | |
@@ -2681,8 +2706,14 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |
return -EPERM; | |
if (in_compat_syscall()) { | |
- nlh64 = xfrm_user_rcv_msg_compat(nlh, link->nla_max, | |
- link->nla_pol, extack); | |
+ struct xfrm_translator *xtr = xfrm_get_translator(); | |
+ | |
+ if (!xtr) | |
+ return -EOPNOTSUPP; | |
+ | |
+ nlh64 = xtr->rcv_msg_compat(nlh, link->nla_max, | |
+ link->nla_pol, extack); | |
+ xfrm_put_translator(xtr); | |
if (IS_ERR(nlh64)) | |
return PTR_ERR(nlh64); | |
if (nlh64) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment