summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2017-10-03 02:34:56 +0200
committerRalf Baechle <ralf@linux-mips.org>2017-10-12 15:10:34 +0200
commit9907223b79b0134b89d34415bc8b93a1c80656a4 (patch)
tree7fb62f858f96b27aab668fdd647b7d897b274a3f
parenta119e20eb164f2a80e8a1d2100c766c6d9d87729 (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.c110
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,