diff options
-rw-r--r-- | include/net/xfrm.h | 3 | ||||
-rw-r--r-- | net/xfrm/xfrm_compat.c | 26 | ||||
-rw-r--r-- | net/xfrm/xfrm_state.c | 14 |
3 files changed, 43 insertions, 0 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index b75b69b255f5..0f9cdfb77f91 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1837,6 +1837,9 @@ struct xfrm_translator { struct nlmsghdr *(*rcv_msg_compat)(const struct nlmsghdr *nlh, int maxtype, const struct nla_policy *policy); + /* Translate 32-bit user_policy from sockptr */ + int (*xlate_user_policy_sockptr)(u8 **pdata32, int optlen); + struct module *owner; }; diff --git a/net/xfrm/xfrm_compat.c b/net/xfrm/xfrm_compat.c index fa168821c805..7158078a71f1 100644 --- a/net/xfrm/xfrm_compat.c +++ b/net/xfrm/xfrm_compat.c @@ -636,10 +636,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) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 7558ed17dc65..4cbeee279840 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1939,6 +1939,20 @@ int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen if (copy_from_user(data, optval, optlen)) goto out; + if (is_compat_task()) { + 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) { |