CVSROOT: /home/cvs
Module name: linux
Changes by: ralf@ftp.linux-mips.org 04/02/25 22:09:29
Modified files:
arch/mips/mm : ioremap.c
Log message:
Fix for off by one bug from OSDL's buzilla:
http://bugme.osdl.org/show_bug.cgi?id=2188
Summary: __ioremap does not map entirely the size specified (if
size = 1 + multiple of PAGE_SIZE)
Kernel Version: 2.6.x , 2.4.x
Status: NEW
Severity: normal
Owner: io_other@kernel-bugs.osdl.org
Submitter: phil@equator.com
Distribution: redHat , snapgear
Hardware Environment:
Software Environment: linux2.6.x linux2.4.x linux2.2.x
Problem Description:__ioremap does remap properly if the size specified
is a
(mulptiple of PAGE_SIZE) + 1 and if the physical address passed happens
to be
already page aligned. In this particular case, the last byte is not
covered and
an access to it will create a Segmentation Fault.
Steps to reproduce: ioremap a piece of PCI memory of size PAGE_SIZE +
1 and
write to the last byte : Segmentation Fault occurs.
I checked in several architectures (ARM, x86 ) and different kernel
versions
2.4.x, 2.6.x, the __ioremap implementations are similar and contain the
bug
The bug is quite simple to fix :
For example, in snpagear distribution for ARM architecture
void * __ioremap(unsigned long phys_addr, size_t size, unsigned long
flags)
{
void * addr;
struct vm_struct * area;
unsigned long offset, last_addr;
/* Don't allow wraparound or zero size */
last_addr = phys_addr + size - 1; <----- last address is inside
if (!size || last_addr < phys_addr)
return NULL;
/*
* Mappings have to be page-aligned
*/
offset = phys_addr & ~PAGE_MASK;
phys_addr &= PAGE_MASK;
//size = PAGE_ALIGN(last_addr ) - phys_addr;
size = PAGE_ALIGN(last_addr + 1) - phys_addr; <--- THE FIX
/*
* Ok, go for it..
*/
area = get_vm_area(size, VM_IOREMAP);
if (!area)
return NULL;
addr = area->addr;
if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr >> PAGE_SHIFT,
size, flags)) {
vfree(addr);
return NULL;
}
return (void *) (offset + (char *)addr);
}
|