Commit ac4d63da authored by Marcel Holtmann's avatar Marcel Holtmann Committed by Adrian Bunk

[Bluetooth] Fix compat ioctl for BNEP, CMTP and HIDP

There exists no attempt do deal with the fact that a structure with
a uint32_t followed by a pointer is going to be different for 32-bit
and 64-bit userspace. Any 32-bit process trying to use it will be
failing with -EFAULT if it's lucky; suffering from having data dumped
at a random address if it's not.
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarAdrian Bunk <bunk@stusta.de>
parent ecfad2cc
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/compat.h>
#include <net/sock.h> #include <net/sock.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -147,24 +148,56 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long ...@@ -147,24 +148,56 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
return 0; return 0;
} }
#ifdef CONFIG_COMPAT
static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
if (cmd == BNEPGETCONNLIST) {
struct bnep_connlist_req cl;
uint32_t uci;
int err;
if (get_user(cl.cnum, (uint32_t __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
cl.ci = compat_ptr(uci);
if (cl.cnum <= 0)
return -EINVAL;
err = bnep_get_connlist(&cl);
if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
err = -EFAULT;
return err;
}
return bnep_sock_ioctl(sock, cmd, arg);
}
#endif
static const struct proto_ops bnep_sock_ops = { static const struct proto_ops bnep_sock_ops = {
.family = PF_BLUETOOTH, .family = PF_BLUETOOTH,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.release = bnep_sock_release, .release = bnep_sock_release,
.ioctl = bnep_sock_ioctl, .ioctl = bnep_sock_ioctl,
.bind = sock_no_bind, #ifdef CONFIG_COMPAT
.getname = sock_no_getname, .compat_ioctl = bnep_sock_compat_ioctl,
.sendmsg = sock_no_sendmsg, #endif
.recvmsg = sock_no_recvmsg, .bind = sock_no_bind,
.poll = sock_no_poll, .getname = sock_no_getname,
.listen = sock_no_listen, .sendmsg = sock_no_sendmsg,
.shutdown = sock_no_shutdown, .recvmsg = sock_no_recvmsg,
.setsockopt = sock_no_setsockopt, .poll = sock_no_poll,
.getsockopt = sock_no_getsockopt, .listen = sock_no_listen,
.connect = sock_no_connect, .shutdown = sock_no_shutdown,
.socketpair = sock_no_socketpair, .setsockopt = sock_no_setsockopt,
.accept = sock_no_accept, .getsockopt = sock_no_getsockopt,
.mmap = sock_no_mmap .connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.mmap = sock_no_mmap
}; };
static struct proto bnep_proto = { static struct proto bnep_proto = {
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/compat.h>
#include <net/sock.h> #include <net/sock.h>
#include <linux/isdn/capilli.h> #include <linux/isdn/capilli.h>
...@@ -138,11 +139,43 @@ static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long ...@@ -138,11 +139,43 @@ static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
return -EINVAL; return -EINVAL;
} }
#ifdef CONFIG_COMPAT
static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
if (cmd == CMTPGETCONNLIST) {
struct cmtp_connlist_req cl;
uint32_t uci;
int err;
if (get_user(cl.cnum, (uint32_t __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
cl.ci = compat_ptr(uci);
if (cl.cnum <= 0)
return -EINVAL;
err = cmtp_get_connlist(&cl);
if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
err = -EFAULT;
return err;
}
return cmtp_sock_ioctl(sock, cmd, arg);
}
#endif
static const struct proto_ops cmtp_sock_ops = { static const struct proto_ops cmtp_sock_ops = {
.family = PF_BLUETOOTH, .family = PF_BLUETOOTH,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.release = cmtp_sock_release, .release = cmtp_sock_release,
.ioctl = cmtp_sock_ioctl, .ioctl = cmtp_sock_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = cmtp_sock_compat_ioctl,
#endif
.bind = sock_no_bind, .bind = sock_no_bind,
.getname = sock_no_getname, .getname = sock_no_getname,
.sendmsg = sock_no_sendmsg, .sendmsg = sock_no_sendmsg,
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/compat.h>
#include <net/sock.h> #include <net/sock.h>
#include "hidp.h" #include "hidp.h"
...@@ -144,11 +145,88 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long ...@@ -144,11 +145,88 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
return -EINVAL; return -EINVAL;
} }
#ifdef CONFIG_COMPAT
struct compat_hidp_connadd_req {
int ctrl_sock; // Connected control socket
int intr_sock; // Connteted interrupt socket
__u16 parser;
__u16 rd_size;
compat_uptr_t rd_data;
__u8 country;
__u8 subclass;
__u16 vendor;
__u16 product;
__u16 version;
__u32 flags;
__u32 idle_to;
char name[128];
};
static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
if (cmd == HIDPGETCONNLIST) {
struct hidp_connlist_req cl;
uint32_t uci;
int err;
if (get_user(cl.cnum, (uint32_t __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
cl.ci = compat_ptr(uci);
if (cl.cnum <= 0)
return -EINVAL;
err = hidp_get_connlist(&cl);
if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
err = -EFAULT;
return err;
} else if (cmd == HIDPCONNADD) {
struct compat_hidp_connadd_req ca;
struct hidp_connadd_req __user *uca;
uca = compat_alloc_user_space(sizeof(*uca));
if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
return -EFAULT;
if (put_user(ca.ctrl_sock, &uca->ctrl_sock) ||
put_user(ca.intr_sock, &uca->intr_sock) ||
put_user(ca.parser, &uca->parser) ||
put_user(ca.rd_size, &uca->parser) ||
put_user(compat_ptr(ca.rd_data), &uca->rd_data) ||
put_user(ca.country, &uca->country) ||
put_user(ca.subclass, &uca->subclass) ||
put_user(ca.vendor, &uca->vendor) ||
put_user(ca.product, &uca->product) ||
put_user(ca.version, &uca->version) ||
put_user(ca.flags, &uca->flags) ||
put_user(ca.idle_to, &uca->idle_to) ||
copy_to_user(&uca->name[0], &ca.name[0], 128))
return -EFAULT;
arg = (unsigned long) uca;
/* Fall through. We don't actually write back any _changes_
to the structure anyway, so there's no need to copy back
into the original compat version */
}
return hidp_sock_ioctl(sock, cmd, arg);
}
#endif
static const struct proto_ops hidp_sock_ops = { static const struct proto_ops hidp_sock_ops = {
.family = PF_BLUETOOTH, .family = PF_BLUETOOTH,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.release = hidp_sock_release, .release = hidp_sock_release,
.ioctl = hidp_sock_ioctl, .ioctl = hidp_sock_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = hidp_sock_compat_ioctl,
#endif
.bind = sock_no_bind, .bind = sock_no_bind,
.getname = sock_no_getname, .getname = sock_no_getname,
.sendmsg = sock_no_sendmsg, .sendmsg = sock_no_sendmsg,
......
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