xfrm/compat: Translate 32-bit user_policy from sockptr
authorDmitry Safonov <dima@arista.com>
Mon, 21 Sep 2020 14:36:56 +0000 (15:36 +0100)
committerSteffen Klassert <steffen.klassert@secunet.com>
Thu, 24 Sep 2020 06:53:04 +0000 (08:53 +0200)
Provide compat_xfrm_userpolicy_info translation for xfrm setsocketopt().
Reallocate buffer and put the missing padding for 64-bit message.

Signed-off-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
include/net/xfrm.h
net/xfrm/xfrm_compat.c
net/xfrm/xfrm_state.c

index fa18cb6bb3f7c0d5ed06145db90a561d6a9e294d..53618a31634b7e17c7c2cdeee9a622fd562aaf64 100644 (file)
@@ -2012,6 +2012,9 @@ struct xfrm_translator {
                        int maxtype, const struct nla_policy *policy,
                        struct netlink_ext_ack *extack);
 
+       /* Translate 32-bit user_policy from sockptr */
+       int (*xlate_user_policy_sockptr)(u8 **pdata32, int optlen);
+
        struct module *owner;
 };
 
index b1b5f972538d07ed20d4065a8bfe029af3c697ae..e28f0c9ecd6af9b56c834817797e454e46672d9f 100644 (file)
@@ -576,10 +576,36 @@ static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32,
        return h64;
 }
 
+static int xfrm_user_policy_compat(u8 **pdata32, int optlen)
+{
+       struct compat_xfrm_userpolicy_info *p = (void *)*pdata32;
+       u8 *src_templates, *dst_templates;
+       u8 *data64;
+
+       if (optlen < sizeof(*p))
+               return -EINVAL;
+
+       data64 = kmalloc_track_caller(optlen + 4, GFP_USER | __GFP_NOWARN);
+       if (!data64)
+               return -ENOMEM;
+
+       memcpy(data64, *pdata32, sizeof(*p));
+       memset(data64 + sizeof(*p), 0, 4);
+
+       src_templates = *pdata32 + sizeof(*p);
+       dst_templates = data64 + sizeof(*p) + 4;
+       memcpy(dst_templates, src_templates, optlen - sizeof(*p));
+
+       kfree(*pdata32);
+       *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)
index cc206ca3df789b0b302a5dd350d66177c3799585..f9961884500b41c0c82fd13cdd09da126872d638 100644 (file)
@@ -2331,9 +2331,6 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
        struct xfrm_mgr *km;
        struct xfrm_policy *pol = NULL;
 
-       if (in_compat_syscall())
-               return -EOPNOTSUPP;
-
        if (sockptr_is_null(optval) && !optlen) {
                xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL);
                xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL);
@@ -2348,6 +2345,20 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
        if (IS_ERR(data))
                return PTR_ERR(data);
 
+       if (in_compat_syscall()) {
+               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;
+               }
+       }
+
        err = -EINVAL;
        rcu_read_lock();
        list_for_each_entry_rcu(km, &xfrm_km_list, list) {