Commit a4855831 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fixes in 32 bit ioctl emulation code

From: Raghavan <raghav@in.ibm.com>,
      me

I am submitting a patch that fixes 2 race conditions in the 32 bit ioctl
emulation code.(fs/compat.c) Since the search is not locked; when a
ioctl_trans structure is deleted, corruption can occur.

The following scenarios discuss the race conditions:

1) When the search is hapenning, if any ioctl_trans structure gets
   deleted; then rather than searching the hash table, the code will start
   searching the free list.

while (t && t->cmd != cmd)
        -
parent ed258d80
...@@ -282,53 +282,36 @@ static int __init init_sys32_ioctl(void) ...@@ -282,53 +282,36 @@ static int __init init_sys32_ioctl(void)
__initcall(init_sys32_ioctl); __initcall(init_sys32_ioctl);
static struct ioctl_trans *ioctl_free_list; int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int,
unsigned int, unsigned long, struct file *))
/* Never free them really. This avoids SMP races. With a Read-Copy-Update
enabled kernel we could just use the RCU infrastructure for this. */
static void free_ioctl(struct ioctl_trans *t)
{
t->cmd = 0;
mb();
t->next = ioctl_free_list;
ioctl_free_list = t;
}
int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *))
{ {
struct ioctl_trans *t; struct ioctl_trans *t;
struct ioctl_trans *new_t;
unsigned long hash = ioctl32_hash(cmd); unsigned long hash = ioctl32_hash(cmd);
new_t = kmalloc(sizeof(*new_t), GFP_KERNEL);
if (!new_t)
return -ENOMEM;
lock_kernel(); lock_kernel();
for (t = (struct ioctl_trans *)ioctl32_hash_table[hash]; for (t = ioctl32_hash_table[hash]; t; t = t->next) {
t;
t = t->next) {
if (t->cmd == cmd) { if (t->cmd == cmd) {
printk("Trying to register duplicated ioctl32 handler %x\n", cmd); printk(KERN_ERR "Trying to register duplicated ioctl32 "
"handler %x\n", cmd);
unlock_kernel(); unlock_kernel();
kfree(new_t);
return -EINVAL; return -EINVAL;
} }
}
if (ioctl_free_list) {
t = ioctl_free_list;
ioctl_free_list = t->next;
} else {
t = kmalloc(sizeof(struct ioctl_trans), GFP_KERNEL);
if (!t) {
unlock_kernel();
return -ENOMEM;
}
} }
new_t->next = NULL;
t->next = NULL; new_t->cmd = cmd;
t->cmd = cmd; new_t->handler = handler;
t->handler = handler; ioctl32_insert_translation(new_t);
ioctl32_insert_translation(t);
unlock_kernel(); unlock_kernel();
return 0; return 0;
} }
EXPORT_SYMBOL(register_ioctl32_conversion);
static inline int builtin_ioctl(struct ioctl_trans *t) static inline int builtin_ioctl(struct ioctl_trans *t)
{ {
...@@ -347,7 +330,7 @@ int unregister_ioctl32_conversion(unsigned int cmd) ...@@ -347,7 +330,7 @@ int unregister_ioctl32_conversion(unsigned int cmd)
lock_kernel(); lock_kernel();
t = (struct ioctl_trans *)ioctl32_hash_table[hash]; t = ioctl32_hash_table[hash];
if (!t) { if (!t) {
unlock_kernel(); unlock_kernel();
return -EINVAL; return -EINVAL;
...@@ -358,38 +341,39 @@ int unregister_ioctl32_conversion(unsigned int cmd) ...@@ -358,38 +341,39 @@ int unregister_ioctl32_conversion(unsigned int cmd)
printk("%p tried to unregister builtin ioctl %x\n", printk("%p tried to unregister builtin ioctl %x\n",
__builtin_return_address(0), cmd); __builtin_return_address(0), cmd);
} else { } else {
ioctl32_hash_table[hash] = t->next; ioctl32_hash_table[hash] = t->next;
free_ioctl(t);
unlock_kernel(); unlock_kernel();
return 0; kfree(t);
return 0;
} }
} }
while (t->next) { while (t->next) {
t1 = (struct ioctl_trans *)(long)t->next; t1 = t->next;
if (t1->cmd == cmd) { if (t1->cmd == cmd) {
if (builtin_ioctl(t1)) { if (builtin_ioctl(t1)) {
printk("%p tried to unregister builtin ioctl %x\n", printk("%p tried to unregister builtin "
__builtin_return_address(0), cmd); "ioctl %x\n",
__builtin_return_address(0), cmd);
goto out; goto out;
} else { } else {
t->next = t1->next; t->next = t1->next;
free_ioctl(t1);
unlock_kernel(); unlock_kernel();
return 0; kfree(t1);
return 0;
} }
} }
t = t1; t = t1;
} }
printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n", cmd); printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n",
out: cmd);
out:
unlock_kernel(); unlock_kernel();
return -EINVAL; return -EINVAL;
} }
EXPORT_SYMBOL(register_ioctl32_conversion);
EXPORT_SYMBOL(unregister_ioctl32_conversion); EXPORT_SYMBOL(unregister_ioctl32_conversion);
asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
unsigned long arg)
{ {
struct file * filp; struct file * filp;
int error = -EBADF; int error = -EBADF;
...@@ -404,43 +388,53 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, unsigned lon ...@@ -404,43 +388,53 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, unsigned lon
goto out; goto out;
} }
t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)]; lock_kernel();
t = ioctl32_hash_table[ioctl32_hash (cmd)];
while (t && t->cmd != cmd) while (t && t->cmd != cmd)
t = (struct ioctl_trans *)t->next; t = t->next;
if (t) { if (t) {
if (t->handler) { if (t->handler) {
lock_kernel();
error = t->handler(fd, cmd, arg, filp); error = t->handler(fd, cmd, arg, filp);
unlock_kernel(); unlock_kernel();
} else } else {
unlock_kernel();
error = sys_ioctl(fd, cmd, arg); error = sys_ioctl(fd, cmd, arg);
} else if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) { }
error = siocdevprivate_ioctl(fd, cmd, arg);
} else { } else {
static int count; unlock_kernel();
if (++count <= 50) { if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
char buf[10]; error = siocdevprivate_ioctl(fd, cmd, arg);
char *path = (char *)__get_free_page(GFP_KERNEL), *fn = "?"; } else {
static int count;
/* find the name of the device. */ if (++count <= 50) {
if (path) { char buf[10];
fn = d_path(filp->f_dentry, filp->f_vfsmnt, char *fn = "?";
path, PAGE_SIZE); char *path;
path = (char *)__get_free_page(GFP_KERNEL);
/* find the name of the device. */
if (path) {
fn = d_path(filp->f_dentry,
filp->f_vfsmnt, path,
PAGE_SIZE);
}
sprintf(buf,"'%c'", (cmd>>24) & 0x3f);
if (!isprint(buf[1]))
sprintf(buf, "%02x", buf[1]);
printk("ioctl32(%s:%d): Unknown cmd fd(%d) "
"cmd(%08x){%s} arg(%08x) on %s\n",
current->comm, current->pid,
(int)fd, (unsigned int)cmd, buf,
(unsigned int)arg, fn);
if (path)
free_page((unsigned long)path);
} }
error = -EINVAL;
sprintf(buf,"'%c'", (cmd>>24) & 0x3f);
if (!isprint(buf[1]))
sprintf(buf, "%02x", buf[1]);
printk("ioctl32(%s:%d): Unknown cmd fd(%d) "
"cmd(%08x){%s} arg(%08x) on %s\n",
current->comm, current->pid,
(int)fd, (unsigned int)cmd, buf, (unsigned int)arg,
fn);
if (path)
free_page((unsigned long)path);
} }
error = -EINVAL;
} }
out: out:
fput(filp); fput(filp);
......
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