Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9c603e5

Browse files
committed
mtdchar: fix offset overflow detection
Sasha Levin has been running trinity in a KVM tools guest, and was able to trigger the BUG_ON() at arch/x86/mm/pat.c:279 (verifying the range of the memory type). The call trace showed that it was mtdchar_mmap() that created an invalid remap_pfn_range(). The problem is that mtdchar_mmap() does various really odd and subtle things with the vma page offset etc, and uses the wrong types (and the wrong overflow) detection for it. For example, the page offset may well be 32-bit on a 32-bit architecture, but after shifting it up by PAGE_SHIFT, we need to use a potentially 64-bit resource_size_t to correctly hold the full value. Also, we need to check that the vma length plus offset doesn't overflow before we check that it is smaller than the length of the mtdmap region. This fixes things up and tries to make the code a bit easier to read. Reported-and-tested-by: Sasha Levin <[email protected]> Acked-by: Suresh Siddha <[email protected]> Acked-by: Artem Bityutskiy <[email protected]> Cc: David Woodhouse <[email protected]> Cc: [email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent 6672d90 commit 9c603e5

File tree

1 file changed

+42
-6
lines changed

1 file changed

+42
-6
lines changed

drivers/mtd/mtdchar.c

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,33 @@ static unsigned long mtdchar_get_unmapped_area(struct file *file,
11231123
}
11241124
#endif
11251125

1126+
static inline unsigned long get_vm_size(struct vm_area_struct *vma)
1127+
{
1128+
return vma->vm_end - vma->vm_start;
1129+
}
1130+
1131+
static inline resource_size_t get_vm_offset(struct vm_area_struct *vma)
1132+
{
1133+
return (resource_size_t) vma->vm_pgoff << PAGE_SHIFT;
1134+
}
1135+
1136+
/*
1137+
* Set a new vm offset.
1138+
*
1139+
* Verify that the incoming offset really works as a page offset,
1140+
* and that the offset and size fit in a resource_size_t.
1141+
*/
1142+
static inline int set_vm_offset(struct vm_area_struct *vma, resource_size_t off)
1143+
{
1144+
pgoff_t pgoff = off >> PAGE_SHIFT;
1145+
if (off != (resource_size_t) pgoff << PAGE_SHIFT)
1146+
return -EINVAL;
1147+
if (off + get_vm_size(vma) - 1 < off)
1148+
return -EINVAL;
1149+
vma->vm_pgoff = pgoff;
1150+
return 0;
1151+
}
1152+
11261153
/*
11271154
* set up a mapping for shared memory segments
11281155
*/
@@ -1132,20 +1159,29 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
11321159
struct mtd_file_info *mfi = file->private_data;
11331160
struct mtd_info *mtd = mfi->mtd;
11341161
struct map_info *map = mtd->priv;
1135-
unsigned long start;
1136-
unsigned long off;
1137-
u32 len;
1162+
resource_size_t start, off;
1163+
unsigned long len, vma_len;
11381164

11391165
if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) {
1140-
off = vma->vm_pgoff << PAGE_SHIFT;
1166+
off = get_vm_offset(vma);
11411167
start = map->phys;
11421168
len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
11431169
start &= PAGE_MASK;
1144-
if ((vma->vm_end - vma->vm_start + off) > len)
1170+
vma_len = get_vm_size(vma);
1171+
1172+
/* Overflow in off+len? */
1173+
if (vma_len + off < off)
1174+
return -EINVAL;
1175+
/* Does it fit in the mapping? */
1176+
if (vma_len + off > len)
11451177
return -EINVAL;
11461178

11471179
off += start;
1148-
vma->vm_pgoff = off >> PAGE_SHIFT;
1180+
/* Did that overflow? */
1181+
if (off < start)
1182+
return -EINVAL;
1183+
if (set_vm_offset(vma, off) < 0)
1184+
return -EINVAL;
11491185
vma->vm_flags |= VM_IO | VM_RESERVED;
11501186

11511187
#ifdef pgprot_noncached

0 commit comments

Comments
 (0)