parisc: fix mmap(MAP_FIXED|MAP_SHARED) to already mmapped address
locale-gen on Debian showed a strange problem on parisc: mmap2(NULL, 536870912, PROT_NONE, MAP_SHARED, 3, 0) = 0x42a54000 mmap2(0x42a54000, 103860, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, 3, 0) = -1 EINVAL (Invalid argument) Basically it was just trying to re-mmap() a file at the same address which it was given by a previous mmap() call. But this remapping failed with EINVAL. The problem is, that when MAP_FIXED and MAP_SHARED flags were used, we didn't included the mapping-based offset when we verified the alignment of the given fixed address against the offset which we calculated it in the previous call. Signed-off-by: Helge Deller <deller@gmx.de> Cc: <stable@vger.kernel.org> # 3.10+
This commit is contained in:
		
					parent
					
						
							
								dc1ccc4815
							
						
					
				
			
			
				commit
				
					
						0576da2c08
					
				
			
		
					 1 changed files with 15 additions and 10 deletions
				
			
		| 
						 | 
					@ -61,8 +61,15 @@ static int get_offset(struct address_space *mapping)
 | 
				
			||||||
	return (unsigned long) mapping >> 8;
 | 
						return (unsigned long) mapping >> 8;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static unsigned long get_shared_area(struct address_space *mapping,
 | 
					static unsigned long shared_align_offset(struct file *filp, unsigned long pgoff)
 | 
				
			||||||
		unsigned long addr, unsigned long len, unsigned long pgoff)
 | 
					{
 | 
				
			||||||
 | 
						struct address_space *mapping = filp ? filp->f_mapping : NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (get_offset(mapping) + pgoff) << PAGE_SHIFT;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned long get_shared_area(struct file *filp, unsigned long addr,
 | 
				
			||||||
 | 
							unsigned long len, unsigned long pgoff)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct vm_unmapped_area_info info;
 | 
						struct vm_unmapped_area_info info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,7 +78,7 @@ static unsigned long get_shared_area(struct address_space *mapping,
 | 
				
			||||||
	info.low_limit = PAGE_ALIGN(addr);
 | 
						info.low_limit = PAGE_ALIGN(addr);
 | 
				
			||||||
	info.high_limit = TASK_SIZE;
 | 
						info.high_limit = TASK_SIZE;
 | 
				
			||||||
	info.align_mask = PAGE_MASK & (SHMLBA - 1);
 | 
						info.align_mask = PAGE_MASK & (SHMLBA - 1);
 | 
				
			||||||
	info.align_offset = (get_offset(mapping) + pgoff) << PAGE_SHIFT;
 | 
						info.align_offset = shared_align_offset(filp, pgoff);
 | 
				
			||||||
	return vm_unmapped_area(&info);
 | 
						return vm_unmapped_area(&info);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,20 +89,18 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	if (flags & MAP_FIXED) {
 | 
						if (flags & MAP_FIXED) {
 | 
				
			||||||
		if ((flags & MAP_SHARED) &&
 | 
							if ((flags & MAP_SHARED) &&
 | 
				
			||||||
		    (addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1))
 | 
							    (addr - shared_align_offset(filp, pgoff)) & (SHMLBA - 1))
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		return addr;
 | 
							return addr;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!addr)
 | 
						if (!addr)
 | 
				
			||||||
		addr = TASK_UNMAPPED_BASE;
 | 
							addr = TASK_UNMAPPED_BASE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (filp) {
 | 
						if (filp || (flags & MAP_SHARED))
 | 
				
			||||||
		addr = get_shared_area(filp->f_mapping, addr, len, pgoff);
 | 
							addr = get_shared_area(filp, addr, len, pgoff);
 | 
				
			||||||
	} else if(flags & MAP_SHARED) {
 | 
						else
 | 
				
			||||||
		addr = get_shared_area(NULL, addr, len, pgoff);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		addr = get_unshared_area(addr, len);
 | 
							addr = get_unshared_area(addr, len);
 | 
				
			||||||
	}
 | 
					
 | 
				
			||||||
	return addr;
 | 
						return addr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue