fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / xen / privcmd / privcmd.c
index 501aa5f..0f328ea 100644 (file)
 static struct proc_dir_entry *privcmd_intf;
 static struct proc_dir_entry *capabilities_intf;
 
+#ifndef HAVE_ARCH_PRIVCMD_MMAP
+static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma);
+#endif
+
 static int privcmd_ioctl(struct inode *inode, struct file *file,
                         unsigned int cmd, unsigned long data)
 {
@@ -48,6 +52,8 @@ static int privcmd_ioctl(struct inode *inode, struct file *file,
                        return -EFAULT;
 
 #if defined(__i386__)
+               if (hypercall.op >= (PAGE_SIZE >> 5))
+                       break;
                __asm__ __volatile__ (
                        "pushl %%ebx; pushl %%ecx; pushl %%edx; "
                        "pushl %%esi; pushl %%edi; "
@@ -64,21 +70,21 @@ static int privcmd_ioctl(struct inode *inode, struct file *file,
                        "popl %%ecx; popl %%ebx"
                        : "=a" (ret) : "0" (&hypercall) : "memory" );
 #elif defined (__x86_64__)
-               {
+               if (hypercall.op < (PAGE_SIZE >> 5)) {
                        long ign1, ign2, ign3;
                        __asm__ __volatile__ (
                                "movq %8,%%r10; movq %9,%%r8;"
-                               "shlq $5,%%rax ;"
+                               "shll $5,%%eax ;"
                                "addq $hypercall_page,%%rax ;"
                                "call *%%rax"
                                : "=a" (ret), "=D" (ign1),
                                  "=S" (ign2), "=d" (ign3)
-                               : "0" ((unsigned long)hypercall.op), 
-                               "1" ((unsigned long)hypercall.arg[0]), 
-                               "2" ((unsigned long)hypercall.arg[1]),
-                               "3" ((unsigned long)hypercall.arg[2]), 
-                               "g" ((unsigned long)hypercall.arg[3]),
-                               "g" ((unsigned long)hypercall.arg[4])
+                               : "0" ((unsigned int)hypercall.op),
+                               "1" (hypercall.arg[0]),
+                               "2" (hypercall.arg[1]),
+                               "3" (hypercall.arg[2]),
+                               "g" (hypercall.arg[3]),
+                               "g" (hypercall.arg[4])
                                : "r8", "r10", "memory" );
                }
 #elif defined (__ia64__)
@@ -121,12 +127,10 @@ static int privcmd_ioctl(struct inode *inode, struct file *file,
 
                vma = find_vma(mm, msg.va);
                rc = -EINVAL;
-               if (!vma || (msg.va != vma->vm_start) || vma->vm_private_data)
+               if (!vma || (msg.va != vma->vm_start) ||
+                   !privcmd_enforce_singleshot_mapping(vma))
                        goto mmap_out;
 
-               /* Mapping is a one-shot operation per vma. */
-               vma->vm_private_data = (void *)1;
-
                va = vma->vm_start;
 
                for (i = 0; i < mmapcmd.num; i++) {
@@ -136,7 +140,7 @@ static int privcmd_ioctl(struct inode *inode, struct file *file,
 
                        /* Do not allow range to wrap the address space. */
                        rc = -EINVAL;
-                       if ((msg.npages > (INT_MAX >> PAGE_SHIFT)) ||
+                       if ((msg.npages > (LONG_MAX >> PAGE_SHIFT)) ||
                            ((unsigned long)(msg.npages << PAGE_SHIFT) >= -va))
                                goto mmap_out;
 
@@ -180,7 +184,7 @@ static int privcmd_ioctl(struct inode *inode, struct file *file,
                if (copy_from_user(&m, udata, sizeof(m)))
                        return -EFAULT;
 
-               if ((m.num <= 0) || (m.num > (INT_MAX >> PAGE_SHIFT)))
+               if ((m.num <= 0) || (m.num > (LONG_MAX >> PAGE_SHIFT)))
                        return -EINVAL;
 
                down_read(&mm->mmap_sem);
@@ -188,15 +192,13 @@ static int privcmd_ioctl(struct inode *inode, struct file *file,
                vma = find_vma(mm, m.addr);
                if (!vma ||
                    (m.addr != vma->vm_start) ||
-                   ((m.addr + (m.num<<PAGE_SHIFT)) != vma->vm_end) ||
-                   vma->vm_private_data) {
+                   ((m.addr + ((unsigned long)m.num<<PAGE_SHIFT)) !=
+                    vma->vm_end) ||
+                   !privcmd_enforce_singleshot_mapping(vma)) {
                        up_read(&mm->mmap_sem);
                        return -EINVAL;
                }
 
-               /* Mapping is a one-shot operation per vma. */
-               vma->vm_private_data = (void *)1;
-
                p = m.arr;
                addr = m.addr;
                for (i = 0; i < m.num; i++, addr += PAGE_SIZE, p++) {
@@ -250,6 +252,11 @@ static int privcmd_mmap(struct file * file, struct vm_area_struct * vma)
 
        return 0;
 }
+
+static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma)
+{
+       return (xchg(&vma->vm_private_data, (void *)1) == NULL);
+}
 #endif
 
 static struct file_operations privcmd_file_ops = {