Commit 07996783 authored by David S. Miller's avatar David S. Miller

Merge branch 'gtp-fix-several-bugs'

Taehee Yoo says:

====================
gtp: fix several bugs

This patch series fixes several bugs in the gtp module.

First patch fixes suspicious RCU usage.
The problem is to use rcu_dereference_sk_user_data() outside of
RCU read critical section.

Second patch fixes use-after-free.
gtp_encap_destroy() is called twice.
gtp_encap_destroy() use both gtp->sk0 and gtp->sk1u.
these pointers can be freed in gtp_encap_destroy().
So, gtp_encap_destroy() should avoid using freed sk pointer.

Third patch removes duplicate code in gtp_dellink().
gtp_dellink() calls gtp_encap_disable() twice.
So, remove one of them.

Fourth patch fixes usage of GFP_KERNEL.
GFP_KERNEL can not be used in RCU read critical section.
This patch make ipv4_pdp_add() to use GFP_ATOMIC instead of GFP_KERNEL.

Fifth patch fixes use-after-free in gtp_newlink().
gtp_newlink() uses gtp_net which would be destroyed by the __exit_net
routine.
So, gtp_newlink should not be called after the __exit_net routine.

Sixth patch adds missing error handling routine in gtp_encap_enable().
gtp_encap_enable() will fail, if invalid role value is sent from
user-space. if so, gtp_encap_enable() should execute error handling
routine.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ccd1479e e30155fd
...@@ -285,16 +285,29 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) ...@@ -285,16 +285,29 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
return gtp_rx(pctx, skb, hdrlen, gtp->role); return gtp_rx(pctx, skb, hdrlen, gtp->role);
} }
static void gtp_encap_destroy(struct sock *sk) static void __gtp_encap_destroy(struct sock *sk)
{ {
struct gtp_dev *gtp; struct gtp_dev *gtp;
gtp = rcu_dereference_sk_user_data(sk); lock_sock(sk);
gtp = sk->sk_user_data;
if (gtp) { if (gtp) {
if (gtp->sk0 == sk)
gtp->sk0 = NULL;
else
gtp->sk1u = NULL;
udp_sk(sk)->encap_type = 0; udp_sk(sk)->encap_type = 0;
rcu_assign_sk_user_data(sk, NULL); rcu_assign_sk_user_data(sk, NULL);
sock_put(sk); sock_put(sk);
} }
release_sock(sk);
}
static void gtp_encap_destroy(struct sock *sk)
{
rtnl_lock();
__gtp_encap_destroy(sk);
rtnl_unlock();
} }
static void gtp_encap_disable_sock(struct sock *sk) static void gtp_encap_disable_sock(struct sock *sk)
...@@ -302,7 +315,7 @@ static void gtp_encap_disable_sock(struct sock *sk) ...@@ -302,7 +315,7 @@ static void gtp_encap_disable_sock(struct sock *sk)
if (!sk) if (!sk)
return; return;
gtp_encap_destroy(sk); __gtp_encap_destroy(sk);
} }
static void gtp_encap_disable(struct gtp_dev *gtp) static void gtp_encap_disable(struct gtp_dev *gtp)
...@@ -681,7 +694,6 @@ static void gtp_dellink(struct net_device *dev, struct list_head *head) ...@@ -681,7 +694,6 @@ static void gtp_dellink(struct net_device *dev, struct list_head *head)
{ {
struct gtp_dev *gtp = netdev_priv(dev); struct gtp_dev *gtp = netdev_priv(dev);
gtp_encap_disable(gtp);
gtp_hashtable_free(gtp); gtp_hashtable_free(gtp);
list_del_rcu(&gtp->list); list_del_rcu(&gtp->list);
unregister_netdevice_queue(dev, head); unregister_netdevice_queue(dev, head);
...@@ -796,7 +808,8 @@ static struct sock *gtp_encap_enable_socket(int fd, int type, ...@@ -796,7 +808,8 @@ static struct sock *gtp_encap_enable_socket(int fd, int type,
goto out_sock; goto out_sock;
} }
if (rcu_dereference_sk_user_data(sock->sk)) { lock_sock(sock->sk);
if (sock->sk->sk_user_data) {
sk = ERR_PTR(-EBUSY); sk = ERR_PTR(-EBUSY);
goto out_sock; goto out_sock;
} }
...@@ -812,6 +825,7 @@ static struct sock *gtp_encap_enable_socket(int fd, int type, ...@@ -812,6 +825,7 @@ static struct sock *gtp_encap_enable_socket(int fd, int type,
setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg); setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg);
out_sock: out_sock:
release_sock(sock->sk);
sockfd_put(sock); sockfd_put(sock);
return sk; return sk;
} }
...@@ -843,8 +857,13 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) ...@@ -843,8 +857,13 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
if (data[IFLA_GTP_ROLE]) { if (data[IFLA_GTP_ROLE]) {
role = nla_get_u32(data[IFLA_GTP_ROLE]); role = nla_get_u32(data[IFLA_GTP_ROLE]);
if (role > GTP_ROLE_SGSN) if (role > GTP_ROLE_SGSN) {
if (sk0)
gtp_encap_disable_sock(sk0);
if (sk1u)
gtp_encap_disable_sock(sk1u);
return -EINVAL; return -EINVAL;
}
} }
gtp->sk0 = sk0; gtp->sk0 = sk0;
...@@ -945,7 +964,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk, ...@@ -945,7 +964,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk,
} }
pctx = kmalloc(sizeof(struct pdp_ctx), GFP_KERNEL); pctx = kmalloc(sizeof(*pctx), GFP_ATOMIC);
if (pctx == NULL) if (pctx == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -1034,6 +1053,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) ...@@ -1034,6 +1053,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
return -EINVAL; return -EINVAL;
} }
rtnl_lock();
rcu_read_lock(); rcu_read_lock();
gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); gtp = gtp_find_dev(sock_net(skb->sk), info->attrs);
...@@ -1058,6 +1078,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) ...@@ -1058,6 +1078,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
rtnl_unlock();
return err; return err;
} }
...@@ -1360,9 +1381,9 @@ late_initcall(gtp_init); ...@@ -1360,9 +1381,9 @@ late_initcall(gtp_init);
static void __exit gtp_fini(void) static void __exit gtp_fini(void)
{ {
unregister_pernet_subsys(&gtp_net_ops);
genl_unregister_family(&gtp_genl_family); genl_unregister_family(&gtp_genl_family);
rtnl_link_unregister(&gtp_link_ops); rtnl_link_unregister(&gtp_link_ops);
unregister_pernet_subsys(&gtp_net_ops);
pr_info("GTP module unloaded\n"); pr_info("GTP module unloaded\n");
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment