tcp: enforce tcp_min_snd_mss in tcp_mtu_probing()
[pandora-kernel.git] / mm / mmap.c
index c7cbb40..69a307b 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -194,7 +194,7 @@ static void __remove_shared_vm_struct(struct vm_area_struct *vma,
        if (vma->vm_flags & VM_DENYWRITE)
                atomic_inc(&file->f_path.dentry->d_inode->i_writecount);
        if (vma->vm_flags & VM_SHARED)
-               mapping->i_mmap_writable--;
+               mapping_unmap_writable(mapping);
 
        flush_dcache_mmap_lock(mapping);
        if (unlikely(vma->vm_flags & VM_NONLINEAR))
@@ -410,7 +410,7 @@ static void __vma_link_file(struct vm_area_struct *vma)
                if (vma->vm_flags & VM_DENYWRITE)
                        atomic_dec(&file->f_path.dentry->d_inode->i_writecount);
                if (vma->vm_flags & VM_SHARED)
-                       mapping->i_mmap_writable++;
+                       atomic_inc(&mapping->i_mmap_writable);
 
                flush_dcache_mmap_lock(mapping);
                if (unlikely(vma->vm_flags & VM_NONLINEAR))
@@ -1049,6 +1049,8 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
 
                        if (!file->f_op || !file->f_op->mmap)
                                return -ENODEV;
+                       if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
+                               return -EINVAL;
                        break;
 
                default:
@@ -1057,6 +1059,8 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
        } else {
                switch (flags & MAP_TYPE) {
                case MAP_SHARED:
+                       if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
+                               return -EINVAL;
                        /*
                         * Ignore pgoff.
                         */
@@ -1201,11 +1205,9 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
 {
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma, *prev;
-       int correct_wcount = 0;
        int error;
        struct rb_node **rb_link, *rb_parent;
        unsigned long charged = 0;
-       struct inode *inode =  file ? file->f_path.dentry->d_inode : NULL;
 
        /* Clear old maps */
        error = -ENOMEM;
@@ -1272,17 +1274,23 @@ munmap_back:
        INIT_LIST_HEAD(&vma->anon_vma_chain);
 
        if (file) {
-               error = -EINVAL;
-               if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
-                       goto free_vma;
                if (vm_flags & VM_DENYWRITE) {
                        error = deny_write_access(file);
                        if (error)
                                goto free_vma;
-                       correct_wcount = 1;
                }
-               vma->vm_file = file;
-               get_file(file);
+               if (vm_flags & VM_SHARED) {
+                       error = mapping_map_writable(file->f_mapping);
+                       if (error)
+                               goto allow_write_and_free_vma;
+               }
+
+               /* ->mmap() can change vma->vm_file, but must guarantee that
+                * vma_link() below can deny write-access if VM_DENYWRITE is set
+                * and map writably if VM_SHARED is set. This usually means the
+                * new file must not have been exposed to user-space, yet.
+                */
+               vma->vm_file = get_file(file);
                error = file->f_op->mmap(file, vma);
                if (error)
                        goto unmap_and_free_vma;
@@ -1319,11 +1327,14 @@ munmap_back:
        }
 
        vma_link(mm, vma, prev, rb_link, rb_parent);
-       file = vma->vm_file;
-
        /* Once vma denies write, undo our temporary denial count */
-       if (correct_wcount)
-               atomic_inc(&inode->i_writecount);
+       if (file) {
+               if (vm_flags & VM_SHARED)
+                       mapping_unmap_writable(file->f_mapping);
+               if (vm_flags & VM_DENYWRITE)
+                       allow_write_access(file);
+       }
+       file = vma->vm_file;
 out:
        perf_event_mmap(vma);
 
@@ -1337,14 +1348,17 @@ out:
        return addr;
 
 unmap_and_free_vma:
-       if (correct_wcount)
-               atomic_inc(&inode->i_writecount);
        vma->vm_file = NULL;
        fput(file);
 
        /* Undo any partial mapping done by a device driver. */
        unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
        charged = 0;
+       if (vm_flags & VM_SHARED)
+               mapping_unmap_writable(file->f_mapping);
+allow_write_and_free_vma:
+       if (vm_flags & VM_DENYWRITE)
+               allow_write_access(file);
 free_vma:
        kmem_cache_free(vm_area_cachep, vma);
 unacct_error:
@@ -1717,7 +1731,7 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
 
        /* Guard against exceeding limits of the address space. */
        address &= PAGE_MASK;
-       if (address >= TASK_SIZE)
+       if (address >= (TASK_SIZE & PAGE_MASK))
                return -ENOMEM;
        address += PAGE_SIZE;
 
@@ -1729,7 +1743,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
                gap_addr = TASK_SIZE;
 
        next = vma->vm_next;
-       if (next && next->vm_start < gap_addr) {
+       if (next && next->vm_start < gap_addr &&
+                       (next->vm_flags & (VM_WRITE|VM_READ|VM_EXEC))) {
                if (!(next->vm_flags & VM_GROWSUP))
                        return -ENOMEM;
                /* Check that both stack segments have the same anon_vma? */
@@ -1788,7 +1803,8 @@ int expand_downwards(struct vm_area_struct *vma,
        if (gap_addr > address)
                return -ENOMEM;
        prev = vma->vm_prev;
-       if (prev && prev->vm_end > gap_addr) {
+       if (prev && prev->vm_end > gap_addr &&
+                       (prev->vm_flags & (VM_WRITE|VM_READ|VM_EXEC))) {
                if (!(prev->vm_flags & VM_GROWSDOWN))
                        return -ENOMEM;
                /* Check that both stack segments have the same anon_vma? */