diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2017-10-03 02:34:56 +0200 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2017-10-12 15:10:34 +0200 |
commit | 9907223b79b0134b89d34415bc8b93a1c80656a4 (patch) | |
tree | 7fb62f858f96b27aab668fdd647b7d897b274a3f | |
parent | a119e20eb164f2a80e8a1d2100c766c6d9d87729 (diff) |
ROSE: Implement compat ioctl handling.wip/ax25-compat-ioctls-v1
Al reports:
Date: Sun, 1 Oct 2017 23:13:12 +0100
Subject: [RFC] compat SIOCADDRT problems
Message-ID: <20171001221312.GG21978@ZenIV.linux.org.uk>
To: netdev@vger.kernel.org
Cc: Ralf Baechle <ralf@linux-mips.org>
Handling of SIOC{ADD,DEL}RT for 32bit is somewhat odd. AFAICS,
the rules for native ioctl look so:
AF_APPLETALK, AF_INET, AF_IPX, AF_PACKET: take struct rtentry. The last one
doesn't have ->compat_ioctl() and 32bit automatically hits routing_ioctl()
in net/socket.c, the rest have ->compat_ioctl() but it doesn't recognize
SIOC{ADD,DEL}RT, so it ends up handled by the same code.
AF_INET6: takes struct in6_rtmsg. Hits routing_ioctl(), which recognizes
ipv6 and does the right thing.
AF_X25: takes x25_route_struct. Layout is apparently identical for 32bit
and 64bit. Has ->compat_ioctl(), which does the same thing as ->ioctl() on
those two.
AF_AX25: takes struct ax25_routes_struct. Again, identical layout on 32bit
and 64bit. Unfortunately, there's no ->compat_ioctl() in this one, so we
end up hitting routing_ioctl() and get screwed.
AF_NETROM: same as previous, except that it takes struct nr_route_struct.
Apparently broken.
AF_ROSE: ditto, with struct rose_route_struct.
AF_QIPCRTR: explicitly recognizes and fails with -EINVAL. Odd (other
protocol families without SIOCADDRT support fail with -ENOTTY), but clearly
not an issue for compat code.
Everything else: fails with -ENOTTY.
Are AF_{AX25,NETROM,ROSE} really broken for 32bit processes on
biarch hosts, or am I missing something subtle in there?
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Reported-by: Al Viro <viro@ZenIV.linux.org.uk>
References: 20171001221312.GG21978@ZenIV.linux.org.uk
-rw-r--r-- | net/rose/af_rose.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 4a9729257023..881ffa1379a7 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -11,6 +11,7 @@ */ #include <linux/capability.h> +#include <linux/compat.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -29,6 +30,7 @@ #include <linux/stat.h> #include <net/net_namespace.h> #include <net/ax25.h> +#include <net/compat.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/if_arp.h> @@ -1374,6 +1376,111 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return 0; } +#ifdef CONFIG_COMPAT +static int compat_rose_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + struct sock *sk = sock->sk; + struct rose_sock *rose = rose_sk(sk); + void __user *argp = compat_ptr(arg); + + switch (cmd) { + case TIOCOUTQ: { + long amount; + + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); + if (amount < 0) + amount = 0; + return put_user(amount, (unsigned int __user *) argp); + } + + case TIOCINQ: { + struct sk_buff *skb; + long amount = 0L; + /* These two are safe on a single CPU system as only user tasks fiddle here */ + if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) + amount = skb->len; + return put_user(amount, (unsigned int __user *) argp); + } + + case SIOCGSTAMP: + return compat_sock_get_timestamp(sk, + (struct timeval __user *) argp); + + case SIOCGSTAMPNS: + return compat_sock_get_timestampns(sk, + (struct timespec __user *) argp); + + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + return -EINVAL; + + case SIOCADDRT: + case SIOCDELRT: + case SIOCRSCLRRT: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + return rose_rt_ioctl(cmd, argp); + + case SIOCRSGCAUSE: { + struct rose_cause_struct rose_cause; + rose_cause.cause = rose->cause; + rose_cause.diagnostic = rose->diagnostic; + return copy_to_user(argp, &rose_cause, sizeof(struct rose_cause_struct)) ? -EFAULT : 0; + } + + case SIOCRSSCAUSE: { + struct rose_cause_struct rose_cause; + if (copy_from_user(&rose_cause, argp, sizeof(struct rose_cause_struct))) + return -EFAULT; + rose->cause = rose_cause.cause; + rose->diagnostic = rose_cause.diagnostic; + return 0; + } + + case SIOCRSSL2CALL: + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) + ax25_listen_release(&rose_callsign, NULL); + if (copy_from_user(&rose_callsign, argp, sizeof(ax25_address))) + return -EFAULT; + if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) + return ax25_listen_register(&rose_callsign, NULL); + + return 0; + + case SIOCRSGL2CALL: + return copy_to_user(argp, &rose_callsign, sizeof(ax25_address)) ? -EFAULT : 0; + + case SIOCRSACCEPT: + if (rose->state == ROSE_STATE_5) { + rose_write_internal(sk, ROSE_CALL_ACCEPTED); + rose_start_idletimer(sk); + rose->condition = 0x00; + rose->vs = 0; + rose->va = 0; + rose->vr = 0; + rose->vl = 0; + rose->state = ROSE_STATE_3; + } + return 0; + + default: + return -ENOIOCTLCMD; + } + + return 0; +} +#endif /* CONFIG_COMPAT */ + #ifdef CONFIG_PROC_FS static void *rose_info_start(struct seq_file *seq, loff_t *pos) __acquires(rose_list_lock) @@ -1485,6 +1592,9 @@ static const struct proto_ops rose_proto_ops = { .getname = rose_getname, .poll = datagram_poll, .ioctl = rose_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_rose_ioctl, +#endif .listen = rose_listen, .shutdown = sock_no_shutdown, .setsockopt = rose_setsockopt, |