[PATCH 04/18] net/xfrm: Add _packed types for compat users

From: Dmitry Safonov
Date: Wed Jul 25 2018 - 22:32:07 EST


xfrm_usersa_info and xfrm_userpolicy_info structures differ in size
between 64-bit and 32-bit ABI. In 64-bit ABI there is additional
4-byte padding in the end of the structure:

32-bit:
sizeof(xfrm_usersa_info) = 220
sizeof(xfrm_userpolicy_info) = 164
64-bit:
sizeof(xfrm_usersa_info) = 224
sizeof(xfrm_userpolicy_info) = 168

In preparation to add compat support to xfrm it's needed to add _packed
verstion for those types.

Parse xfrm_usersa_info and xfrm_userpolicy_info netlink messages
sent by userspace using _packed structures (as we don't care about
parsing padding).
Sending _packed notification messages back to userspace will be done
with following patches (in copy_to_user_state() and
copy_to_user_policy()).

Cc: "David S. Miller" <davem@xxxxxxxxxxxxx>
Cc: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx>
Cc: Steffen Klassert <steffen.klassert@xxxxxxxxxxx>
Cc: netdev@xxxxxxxxxxxxxxx
Signed-off-by: Dmitry Safonov <dima@xxxxxxxxxx>
---
net/xfrm/xfrm_user.c | 89 ++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 65 insertions(+), 24 deletions(-)

diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 2677cb55b7a8..b382cdd3bef6 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -33,6 +33,34 @@
#endif
#include <asm/unaligned.h>

+struct xfrm_usersa_info_packed {
+ struct xfrm_selector sel;
+ struct xfrm_id id;
+ xfrm_address_t saddr;
+ struct xfrm_lifetime_cfg lft;
+ struct xfrm_lifetime_cur curlft;
+ struct xfrm_stats stats;
+ __u32 seq;
+ __u32 reqid;
+ __u16 family;
+ __u8 mode; /* XFRM_MODE_xxx */
+ __u8 replay_window;
+ __u8 flags;
+ __u8 __pad[3];
+} __packed;
+
+struct xfrm_userpolicy_info_packed {
+ struct xfrm_selector sel;
+ struct xfrm_lifetime_cfg lft;
+ struct xfrm_lifetime_cur curlft;
+ __u32 priority;
+ __u32 index;
+ __u8 dir;
+ __u8 action;
+ __u8 flags;
+ __u8 share;
+} __packed;
+
static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
{
struct nlattr *rt = attrs[type];
@@ -115,7 +143,7 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs)
return 0;
}

-static inline int verify_replay(struct xfrm_usersa_info *p,
+static inline int verify_replay(struct xfrm_usersa_info_packed *p,
struct nlattr **attrs)
{
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
@@ -143,7 +171,7 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
return 0;
}

-static int verify_newsa_info(struct xfrm_usersa_info *p,
+static int verify_newsa_info(struct xfrm_usersa_info_packed *p,
struct nlattr **attrs)
{
int err;
@@ -464,7 +492,8 @@ static inline unsigned int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx)
return len;
}

-static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
+static void copy_from_user_state(struct xfrm_state *x,
+ struct xfrm_usersa_info_packed *p)
{
memcpy(&x->id, &p->id, sizeof(x->id));
memcpy(&x->sel, &p->sel, sizeof(x->sel));
@@ -528,9 +557,8 @@ static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
}

static struct xfrm_state *xfrm_state_construct(struct net *net,
- struct xfrm_usersa_info *p,
- struct nlattr **attrs,
- int *errp)
+ struct xfrm_usersa_info_packed *p,
+ struct nlattr **attrs, int *errp)
{
struct xfrm_state *x = xfrm_state_alloc(net);
int err = -ENOMEM;
@@ -630,7 +658,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
struct net *net = sock_net(skb->sk);
- struct xfrm_usersa_info *p = nlmsg_data(nlh);
+ struct xfrm_usersa_info_packed *p = nlmsg_data(nlh);
struct xfrm_state *x;
int err;
struct km_event c;
@@ -1331,7 +1359,7 @@ static int verify_policy_type(u8 type)
return 0;
}

-static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)
+static int verify_newpolicy_info(struct xfrm_userpolicy_info_packed *p)
{
int ret;

@@ -1513,7 +1541,8 @@ static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs)
return 0;
}

-static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p)
+static void copy_from_user_policy(struct xfrm_policy *xp,
+ struct xfrm_userpolicy_info_packed *p)
{
xp->priority = p->priority;
xp->index = p->index;
@@ -1540,7 +1569,9 @@ static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_i
p->share = XFRM_SHARE_ANY; /* XXX xp->share */
}

-static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp)
+static struct xfrm_policy *xfrm_policy_construct(struct net *net,
+ struct xfrm_userpolicy_info_packed *p,
+ struct nlattr **attrs, int *errp)
{
struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL);
int err;
@@ -1575,7 +1606,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
struct net *net = sock_net(skb->sk);
- struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
+ struct xfrm_userpolicy_info_packed *p = nlmsg_data(nlh);
struct xfrm_policy *xp;
struct km_event c;
int err;
@@ -2079,7 +2110,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct net *net = sock_net(skb->sk);
struct xfrm_policy *xp;
struct xfrm_user_polexpire *up = nlmsg_data(nlh);
- struct xfrm_userpolicy_info *p = &up->pol;
+ struct xfrm_userpolicy_info_packed *p = (void *)&up->pol;
u8 type = XFRM_POLICY_TYPE_MAIN;
int err = -ENOENT;
struct xfrm_mark m;
@@ -2140,7 +2171,7 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct xfrm_state *x;
int err;
struct xfrm_user_expire *ue = nlmsg_data(nlh);
- struct xfrm_usersa_info *p = &ue->state;
+ struct xfrm_usersa_info_packed *p = (struct xfrm_usersa_info_packed *)&ue->state;
struct xfrm_mark m;
u32 mark = xfrm_mark_get(attrs, &m);

@@ -2178,6 +2209,7 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct xfrm_mark mark;

struct xfrm_user_acquire *ua = nlmsg_data(nlh);
+ struct xfrm_userpolicy_info_packed *upi = (void *)&ua->policy;
struct xfrm_state *x = xfrm_state_alloc(net);
int err = -ENOMEM;

@@ -2186,12 +2218,12 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,

xfrm_mark_get(attrs, &mark);

- err = verify_newpolicy_info(&ua->policy);
+ err = verify_newpolicy_info(upi);
if (err)
goto free_state;

/* build an XP */
- xp = xfrm_policy_construct(net, &ua->policy, attrs, &err);
+ xp = xfrm_policy_construct(net, upi, attrs, &err);
if (!xp)
goto free_state;

@@ -2881,11 +2913,21 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
u8 *data, int len, int *dir)
{
struct net *net = sock_net(sk);
- struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data;
- struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1);
+ struct xfrm_userpolicy_info *upi = (void *)data;
+ struct xfrm_userpolicy_info_packed *_upi = (void *)data;
+ size_t policy_size;
+ struct xfrm_user_tmpl *ut;
struct xfrm_policy *xp;
int nr;

+ if (in_compat_syscall()) {
+ ut = (struct xfrm_user_tmpl *)(_upi + 1);
+ policy_size = sizeof(*_upi);
+ } else {
+ ut = (struct xfrm_user_tmpl *)(upi + 1);
+ policy_size = sizeof(*upi);
+ }
+
switch (sk->sk_family) {
case AF_INET:
if (opt != IP_XFRM_POLICY) {
@@ -2908,15 +2950,14 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,

*dir = -EINVAL;

- if (len < sizeof(*p) ||
- verify_newpolicy_info(p))
+ if (len < policy_size || verify_newpolicy_info(_upi))
return NULL;

- nr = ((len - sizeof(*p)) / sizeof(*ut));
- if (validate_tmpl(nr, ut, p->sel.family))
+ nr = ((len - policy_size) / sizeof(*ut));
+ if (validate_tmpl(nr, ut, _upi->sel.family))
return NULL;

- if (p->dir > XFRM_POLICY_OUT)
+ if (_upi->dir > XFRM_POLICY_OUT)
return NULL;

xp = xfrm_policy_alloc(net, GFP_ATOMIC);
@@ -2925,11 +2966,11 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
return NULL;
}

- copy_from_user_policy(xp, p);
+ copy_from_user_policy(xp, _upi);
xp->type = XFRM_POLICY_TYPE_MAIN;
copy_templates(xp, ut, nr);

- *dir = p->dir;
+ *dir = _upi->dir;

return xp;
}
--
2.13.6