Commit 645d83c5db970a1c57225e155113b4aa2451e920
Committed by
Linus Torvalds
1 parent
c775197d59
NOMMU: Fix MAP_PRIVATE mmap() of objects where the data can be mapped directly
Fix MAP_PRIVATE mmap() of files and devices where the data in the backing store might be mapped directly. Use the BDI_CAP_MAP_DIRECT capability flag to govern whether or not we should be trying to map a file directly. This can be used to determine whether or not a region has been filled in at the point where we call do_mmap_shared() or do_mmap_private(). The BDI_CAP_MAP_DIRECT capability flag is cleared by validate_mmap_request() if there's any reason we can't use it. It's also cleared in do_mmap_pgoff() if f_op->get_unmapped_area() fails. Without this fix, attempting to run a program from a RomFS image on a non-mappable MTD partition results in a BUG as the kernel attempts XIP, and this can be caught in gdb: Program received signal SIGABRT, Aborted. 0xc005dce8 in add_nommu_region (region=<value optimized out>) at mm/nommu.c:547 (gdb) bt #0 0xc005dce8 in add_nommu_region (region=<value optimized out>) at mm/nommu.c:547 #1 0xc005f168 in do_mmap_pgoff (file=0xc31a6620, addr=<value optimized out>, len=3808, prot=3, flags=6146, pgoff=0) at mm/nommu.c:1373 #2 0xc00a96b8 in elf_fdpic_map_file (params=0xc33fbbec, file=0xc31a6620, mm=0xc31bef60, what=0xc0213144 "executable") at mm.h:1145 #3 0xc00aa8b4 in load_elf_fdpic_binary (bprm=0xc316cb00, regs=<value optimized out>) at fs/binfmt_elf_fdpic.c:343 #4 0xc006b588 in search_binary_handler (bprm=0x6, regs=0xc33fbce0) at fs/exec.c:1234 #5 0xc006c648 in do_execve (filename=<value optimized out>, argv=0xc3ad14cc, envp=0xc3ad1460, regs=0xc33fbce0) at fs/exec.c:1356 #6 0xc0008cf0 in sys_execve (name=<value optimized out>, argv=0xc3ad14cc, envp=0xc3ad1460) at arch/frv/kernel/process.c:263 #7 0xc00075dc in __syscall_call () at arch/frv/kernel/entry.S:897 Note that this fix does the following commit differently: commit a190887b58c32d19c2eee007c5eb8faa970a69ba Author: David Howells <dhowells@redhat.com> Date: Sat Sep 5 11:17:07 2009 -0700 nommu: fix error handling in do_mmap_pgoff() Reported-by: Graff Yang <graff.yang@gmail.com> Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: Pekka Enberg <penberg@cs.helsinki.fi> Cc: Paul Mundt <lethal@linux-sh.org> Cc: Mel Gorman <mel@csn.ul.ie> Cc: Greg Ungerer <gerg@snapgear.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 1 changed file with 12 additions and 22 deletions Side-by-side Diff
mm/nommu.c
... | ... | @@ -1034,7 +1034,7 @@ |
1034 | 1034 | ret = vma->vm_file->f_op->mmap(vma->vm_file, vma); |
1035 | 1035 | if (ret == 0) { |
1036 | 1036 | vma->vm_region->vm_top = vma->vm_region->vm_end; |
1037 | - return ret; | |
1037 | + return 0; | |
1038 | 1038 | } |
1039 | 1039 | if (ret != -ENOSYS) |
1040 | 1040 | return ret; |
... | ... | @@ -1051,7 +1051,8 @@ |
1051 | 1051 | */ |
1052 | 1052 | static int do_mmap_private(struct vm_area_struct *vma, |
1053 | 1053 | struct vm_region *region, |
1054 | - unsigned long len) | |
1054 | + unsigned long len, | |
1055 | + unsigned long capabilities) | |
1055 | 1056 | { |
1056 | 1057 | struct page *pages; |
1057 | 1058 | unsigned long total, point, n, rlen; |
1058 | 1059 | |
... | ... | @@ -1062,13 +1063,13 @@ |
1062 | 1063 | * shared mappings on devices or memory |
1063 | 1064 | * - VM_MAYSHARE will be set if it may attempt to share |
1064 | 1065 | */ |
1065 | - if (vma->vm_file) { | |
1066 | + if (capabilities & BDI_CAP_MAP_DIRECT) { | |
1066 | 1067 | ret = vma->vm_file->f_op->mmap(vma->vm_file, vma); |
1067 | 1068 | if (ret == 0) { |
1068 | 1069 | /* shouldn't return success if we're not sharing */ |
1069 | 1070 | BUG_ON(!(vma->vm_flags & VM_MAYSHARE)); |
1070 | 1071 | vma->vm_region->vm_top = vma->vm_region->vm_end; |
1071 | - return ret; | |
1072 | + return 0; | |
1072 | 1073 | } |
1073 | 1074 | if (ret != -ENOSYS) |
1074 | 1075 | return ret; |
... | ... | @@ -1306,7 +1307,7 @@ |
1306 | 1307 | * - this is the hook for quasi-memory character devices to |
1307 | 1308 | * tell us the location of a shared mapping |
1308 | 1309 | */ |
1309 | - if (file && file->f_op->get_unmapped_area) { | |
1310 | + if (capabilities & BDI_CAP_MAP_DIRECT) { | |
1310 | 1311 | addr = file->f_op->get_unmapped_area(file, addr, len, |
1311 | 1312 | pgoff, flags); |
1312 | 1313 | if (IS_ERR((void *) addr)) { |
1313 | 1314 | |
1314 | 1315 | |
1315 | 1316 | |
... | ... | @@ -1330,15 +1331,17 @@ |
1330 | 1331 | } |
1331 | 1332 | |
1332 | 1333 | vma->vm_region = region; |
1333 | - add_nommu_region(region); | |
1334 | 1334 | |
1335 | - /* set up the mapping */ | |
1335 | + /* set up the mapping | |
1336 | + * - the region is filled in if BDI_CAP_MAP_DIRECT is still set | |
1337 | + */ | |
1336 | 1338 | if (file && vma->vm_flags & VM_SHARED) |
1337 | 1339 | ret = do_mmap_shared_file(vma); |
1338 | 1340 | else |
1339 | - ret = do_mmap_private(vma, region, len); | |
1341 | + ret = do_mmap_private(vma, region, len, capabilities); | |
1340 | 1342 | if (ret < 0) |
1341 | - goto error_put_region; | |
1343 | + goto error_just_free; | |
1344 | + add_nommu_region(region); | |
1342 | 1345 | |
1343 | 1346 | /* okay... we have a mapping; now we have to register it */ |
1344 | 1347 | result = vma->vm_start; |
... | ... | @@ -1355,19 +1358,6 @@ |
1355 | 1358 | |
1356 | 1359 | kleave(" = %lx", result); |
1357 | 1360 | return result; |
1358 | - | |
1359 | -error_put_region: | |
1360 | - __put_nommu_region(region); | |
1361 | - if (vma) { | |
1362 | - if (vma->vm_file) { | |
1363 | - fput(vma->vm_file); | |
1364 | - if (vma->vm_flags & VM_EXECUTABLE) | |
1365 | - removed_exe_file_vma(vma->vm_mm); | |
1366 | - } | |
1367 | - kmem_cache_free(vm_area_cachep, vma); | |
1368 | - } | |
1369 | - kleave(" = %d [pr]", ret); | |
1370 | - return ret; | |
1371 | 1361 | |
1372 | 1362 | error_just_free: |
1373 | 1363 | up_write(&nommu_region_sem); |