Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
xfrm-compat v2-v3 diff
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