Commit 8865c418 authored by David Woodhouse's avatar David Woodhouse Committed by David S. Miller

atm: 32-bit ioctl compatibility

We lack compat ioctl support through most of the ATM code. This patch
deals with most of it, and I can now at least use BR2684 and PPPoATM
with 32-bit userspace.

I haven't added a .compat_ioctl method to struct atm_ioctl, because
AFAICT none of the current users need any conversion -- so we can just
call the ->ioctl() method in every case. I looked at br2684, clip, lec,
mpc, pppoatm and atmtcp.

In svc_compat_ioctl() the only mangling which is needed is to change
COMPAT_ATM_ADDPARTY to ATM_ADDPARTY. Although it's defined as
	_IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf)
it doesn't actually _take_ a struct atm_iobuf as an argument -- it takes
a struct sockaddr_atmsvc, which _is_ the same between 32-bit and 64-bit
code, so doesn't need conversion.

Almost all of vcc_ioctl() would have been identical, so I converted that
into a core do_vcc_ioctl() function with an 'int compat' argument.

I've done the same with atm_dev_ioctl(), where there _are_ a few
differences, but still it's relatively contained and there would
otherwise have been a lot of duplication.

I haven't done any of the actual device-specific ioctls, although I've
added a compat_ioctl method to struct atmdev_ops.
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent dcd39c90
...@@ -236,5 +236,16 @@ struct atmif_sioc { ...@@ -236,5 +236,16 @@ struct atmif_sioc {
void __user *arg; void __user *arg;
}; };
#ifdef __KERNEL__
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
struct compat_atmif_sioc {
int number;
int length;
compat_uptr_t arg;
};
#endif
#endif
typedef unsigned short atm_backend_t; typedef unsigned short atm_backend_t;
#endif #endif
...@@ -100,6 +100,10 @@ struct atm_dev_stats { ...@@ -100,6 +100,10 @@ struct atm_dev_stats {
/* use backend to make new if */ /* use backend to make new if */
#define ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf) #define ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf)
/* add party to p2mp call */ /* add party to p2mp call */
#ifdef CONFIG_COMPAT
/* It actually takes struct sockaddr_atmsvc, not struct atm_iobuf */
#define COMPAT_ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL+4,struct compat_atm_iobuf)
#endif
#define ATM_DROPPARTY _IOW('a', ATMIOC_SPECIAL+5,int) #define ATM_DROPPARTY _IOW('a', ATMIOC_SPECIAL+5,int)
/* drop party from p2mp call */ /* drop party from p2mp call */
...@@ -224,6 +228,13 @@ struct atm_cirange { ...@@ -224,6 +228,13 @@ struct atm_cirange {
extern struct proc_dir_entry *atm_proc_root; extern struct proc_dir_entry *atm_proc_root;
#endif #endif
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
struct compat_atm_iobuf {
int length;
compat_uptr_t buffer;
};
#endif
struct k_atm_aal_stats { struct k_atm_aal_stats {
#define __HANDLE_ITEM(i) atomic_t i #define __HANDLE_ITEM(i) atomic_t i
...@@ -379,6 +390,10 @@ struct atmdev_ops { /* only send is required */ ...@@ -379,6 +390,10 @@ struct atmdev_ops { /* only send is required */
int (*open)(struct atm_vcc *vcc); int (*open)(struct atm_vcc *vcc);
void (*close)(struct atm_vcc *vcc); void (*close)(struct atm_vcc *vcc);
int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void __user *arg); int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void __user *arg);
#ifdef CONFIG_COMPAT
int (*compat_ioctl)(struct atm_dev *dev,unsigned int cmd,
void __user *arg);
#endif
int (*getsockopt)(struct atm_vcc *vcc,int level,int optname, int (*getsockopt)(struct atm_vcc *vcc,int level,int optname,
void __user *optval,int optlen); void __user *optval,int optlen);
int (*setsockopt)(struct atm_vcc *vcc,int level,int optname, int (*setsockopt)(struct atm_vcc *vcc,int level,int optname,
......
...@@ -19,6 +19,7 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, ...@@ -19,6 +19,7 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
size_t total_len); size_t total_len);
unsigned int vcc_poll(struct file *file, struct socket *sock, poll_table *wait); unsigned int vcc_poll(struct file *file, struct socket *sock, poll_table *wait);
int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
int vcc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
int vcc_setsockopt(struct socket *sock, int level, int optname, int vcc_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, int optlen); char __user *optval, int optlen);
int vcc_getsockopt(struct socket *sock, int level, int optname, int vcc_getsockopt(struct socket *sock, int level, int optname,
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/atmlec.h> #include <linux/atmlec.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <asm/ioctls.h> #include <asm/ioctls.h>
#include <net/compat.h>
#include "resources.h" #include "resources.h"
#include "signaling.h" /* for WAITING and sigd_attach */ #include "signaling.h" /* for WAITING and sigd_attach */
...@@ -46,7 +47,7 @@ void deregister_atm_ioctl(struct atm_ioctl *ioctl) ...@@ -46,7 +47,7 @@ void deregister_atm_ioctl(struct atm_ioctl *ioctl)
EXPORT_SYMBOL(register_atm_ioctl); EXPORT_SYMBOL(register_atm_ioctl);
EXPORT_SYMBOL(deregister_atm_ioctl); EXPORT_SYMBOL(deregister_atm_ioctl);
int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg, int compat)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct atm_vcc *vcc; struct atm_vcc *vcc;
...@@ -80,13 +81,25 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -80,13 +81,25 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
goto done; goto done;
} }
case SIOCGSTAMP: /* borrowed from IP */ case SIOCGSTAMP: /* borrowed from IP */
#ifdef CONFIG_COMPAT
if (compat)
error = compat_sock_get_timestamp(sk, argp);
else
#endif
error = sock_get_timestamp(sk, argp); error = sock_get_timestamp(sk, argp);
goto done; goto done;
case SIOCGSTAMPNS: /* borrowed from IP */ case SIOCGSTAMPNS: /* borrowed from IP */
#ifdef CONFIG_COMPAT
if (compat)
error = compat_sock_get_timestampns(sk, argp);
else
#endif
error = sock_get_timestampns(sk, argp); error = sock_get_timestampns(sk, argp);
goto done; goto done;
case ATM_SETSC: case ATM_SETSC:
printk(KERN_WARNING "ATM_SETSC is obsolete\n"); if (net_ratelimit())
printk(KERN_WARNING "ATM_SETSC is obsolete; used by %s:%d\n",
current->comm, task_pid_nr(current));
error = 0; error = 0;
goto done; goto done;
case ATMSIGD_CTRL: case ATMSIGD_CTRL:
...@@ -99,12 +112,23 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -99,12 +112,23 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
* info uses kernel pointers as opaque references, * info uses kernel pointers as opaque references,
* so the holder of the file descriptor can scribble * so the holder of the file descriptor can scribble
* on the kernel... so we should make sure that we * on the kernel... so we should make sure that we
* have the same privledges that /proc/kcore needs * have the same privileges that /proc/kcore needs
*/ */
if (!capable(CAP_SYS_RAWIO)) { if (!capable(CAP_SYS_RAWIO)) {
error = -EPERM; error = -EPERM;
goto done; goto done;
} }
#ifdef CONFIG_COMPAT
/* WTF? I don't even want to _think_ about making this
work for 32-bit userspace. TBH I don't really want
to think about it at all. dwmw2. */
if (compat) {
if (net_ratelimit())
printk(KERN_WARNING "32-bit task cannot be atmsigd\n");
error = -EINVAL;
goto done;
}
#endif
error = sigd_attach(vcc); error = sigd_attach(vcc);
if (!error) if (!error)
sock->state = SS_CONNECTED; sock->state = SS_CONNECTED;
...@@ -155,8 +179,21 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -155,8 +179,21 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
if (error != -ENOIOCTLCMD) if (error != -ENOIOCTLCMD)
goto done; goto done;
error = atm_dev_ioctl(cmd, argp); error = atm_dev_ioctl(cmd, argp, compat);
done: done:
return error; return error;
} }
int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
return do_vcc_ioctl(sock, cmd, arg, 0);
}
#ifdef CONFIG_COMPAT
int vcc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
return do_vcc_ioctl(sock, cmd, arg, 1);
}
#endif
...@@ -113,6 +113,9 @@ static const struct proto_ops pvc_proto_ops = { ...@@ -113,6 +113,9 @@ static const struct proto_ops pvc_proto_ops = {
.getname = pvc_getname, .getname = pvc_getname,
.poll = vcc_poll, .poll = vcc_poll,
.ioctl = vcc_ioctl, .ioctl = vcc_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = vcc_compat_ioctl,
#endif
.listen = sock_no_listen, .listen = sock_no_listen,
.shutdown = pvc_shutdown, .shutdown = pvc_shutdown,
.setsockopt = pvc_setsockopt, .setsockopt = pvc_setsockopt,
......
...@@ -195,20 +195,39 @@ static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg, in ...@@ -195,20 +195,39 @@ static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg, in
} }
int atm_dev_ioctl(unsigned int cmd, void __user *arg) int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat)
{ {
void __user *buf; void __user *buf;
int error, len, number, size = 0; int error, len, number, size = 0;
struct atm_dev *dev; struct atm_dev *dev;
struct list_head *p; struct list_head *p;
int *tmp_buf, *tmp_p; int *tmp_buf, *tmp_p;
struct atm_iobuf __user *iobuf = arg; int __user *sioc_len;
struct atmif_sioc __user *sioc = arg; int __user *iobuf_len;
#ifndef CONFIG_COMPAT
compat = 0; /* Just so the compiler _knows_ */
#endif
switch (cmd) { switch (cmd) {
case ATM_GETNAMES: case ATM_GETNAMES:
if (compat) {
#ifdef CONFIG_COMPAT
struct compat_atm_iobuf __user *ciobuf = arg;
compat_uptr_t cbuf;
iobuf_len = &ciobuf->length;
if (get_user(cbuf, &ciobuf->buffer))
return -EFAULT;
buf = compat_ptr(cbuf);
#endif
} else {
struct atm_iobuf __user *iobuf = arg;
iobuf_len = &iobuf->length;
if (get_user(buf, &iobuf->buffer)) if (get_user(buf, &iobuf->buffer))
return -EFAULT; return -EFAULT;
if (get_user(len, &iobuf->length)) }
if (get_user(len, iobuf_len))
return -EFAULT; return -EFAULT;
mutex_lock(&atm_dev_mutex); mutex_lock(&atm_dev_mutex);
list_for_each(p, &atm_devs) list_for_each(p, &atm_devs)
...@@ -229,7 +248,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg) ...@@ -229,7 +248,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
} }
mutex_unlock(&atm_dev_mutex); mutex_unlock(&atm_dev_mutex);
error = ((copy_to_user(buf, tmp_buf, size)) || error = ((copy_to_user(buf, tmp_buf, size)) ||
put_user(size, &iobuf->length)) put_user(size, iobuf_len))
? -EFAULT : 0; ? -EFAULT : 0;
kfree(tmp_buf); kfree(tmp_buf);
return error; return error;
...@@ -237,13 +256,32 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg) ...@@ -237,13 +256,32 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
break; break;
} }
if (compat) {
#ifdef CONFIG_COMPAT
struct compat_atmif_sioc __user *csioc = arg;
compat_uptr_t carg;
sioc_len = &csioc->length;
if (get_user(carg, &csioc->arg))
return -EFAULT;
buf = compat_ptr(carg);
if (get_user(len, &csioc->length))
return -EFAULT;
if (get_user(number, &csioc->number))
return -EFAULT;
#endif
} else {
struct atmif_sioc __user *sioc = arg;
sioc_len = &sioc->length;
if (get_user(buf, &sioc->arg)) if (get_user(buf, &sioc->arg))
return -EFAULT; return -EFAULT;
if (get_user(len, &sioc->length)) if (get_user(len, &sioc->length))
return -EFAULT; return -EFAULT;
if (get_user(number, &sioc->number)) if (get_user(number, &sioc->number))
return -EFAULT; return -EFAULT;
}
if (!(dev = try_then_request_module(atm_dev_lookup(number), if (!(dev = try_then_request_module(atm_dev_lookup(number),
"atm-device-%d", number))) "atm-device-%d", number)))
return -ENODEV; return -ENODEV;
...@@ -358,7 +396,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg) ...@@ -358,7 +396,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
size = error; size = error;
/* may return 0, but later on size == 0 means "don't /* may return 0, but later on size == 0 means "don't
write the length" */ write the length" */
error = put_user(size, &sioc->length) error = put_user(size, sioc_len)
? -EFAULT : 0; ? -EFAULT : 0;
goto done; goto done;
case ATM_SETLOOP: case ATM_SETLOOP:
...@@ -380,11 +418,21 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg) ...@@ -380,11 +418,21 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
} }
/* fall through */ /* fall through */
default: default:
if (compat) {
#ifdef CONFIG_COMPAT
if (!dev->ops->compat_ioctl) {
error = -EINVAL;
goto done;
}
size = dev->ops->compat_ioctl(dev, cmd, buf);
#endif
} else {
if (!dev->ops->ioctl) { if (!dev->ops->ioctl) {
error = -EINVAL; error = -EINVAL;
goto done; goto done;
} }
size = dev->ops->ioctl(dev, cmd, buf); size = dev->ops->ioctl(dev, cmd, buf);
}
if (size < 0) { if (size < 0) {
error = (size == -ENOIOCTLCMD ? -EINVAL : size); error = (size == -ENOIOCTLCMD ? -EINVAL : size);
goto done; goto done;
...@@ -392,7 +440,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg) ...@@ -392,7 +440,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
} }
if (size) if (size)
error = put_user(size, &sioc->length) error = put_user(size, sioc_len)
? -EFAULT : 0; ? -EFAULT : 0;
else else
error = 0; error = 0;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
extern struct list_head atm_devs; extern struct list_head atm_devs;
extern struct mutex atm_dev_mutex; extern struct mutex atm_dev_mutex;
int atm_dev_ioctl(unsigned int cmd, void __user *arg); int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
......
...@@ -604,6 +604,22 @@ static int svc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -604,6 +604,22 @@ static int svc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return error; return error;
} }
#ifdef CONFIG_COMPAT
static int svc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
/* The definition of ATM_ADDPARTY uses the size of struct atm_iobuf.
But actually it takes a struct sockaddr_atmsvc, which doesn't need
compat handling. So all we have to do is fix up cmd... */
if (cmd == COMPAT_ATM_ADDPARTY)
cmd = ATM_ADDPARTY;
if (cmd == ATM_ADDPARTY || cmd == ATM_DROPPARTY)
return svc_ioctl(sock, cmd, arg);
else
return vcc_compat_ioctl(sock, cmd, arg);
}
#endif /* CONFIG_COMPAT */
static const struct proto_ops svc_proto_ops = { static const struct proto_ops svc_proto_ops = {
.family = PF_ATMSVC, .family = PF_ATMSVC,
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -616,6 +632,9 @@ static const struct proto_ops svc_proto_ops = { ...@@ -616,6 +632,9 @@ static const struct proto_ops svc_proto_ops = {
.getname = svc_getname, .getname = svc_getname,
.poll = vcc_poll, .poll = vcc_poll,
.ioctl = svc_ioctl, .ioctl = svc_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = svc_compat_ioctl,
#endif
.listen = svc_listen, .listen = svc_listen,
.shutdown = svc_shutdown, .shutdown = svc_shutdown,
.setsockopt = svc_setsockopt, .setsockopt = svc_setsockopt,
......
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