Skip to content

Commit e23d415

Browse files
Al Virotorvalds
authored andcommitted
fix fault_in_multipages_...() on architectures with no-op access_ok()
Switching iov_iter fault-in to multipages variants has exposed an old bug in underlying fault_in_multipages_...(); they break if the range passed to them wraps around. Normally access_ok() done by callers will prevent such (and it's a guaranteed EFAULT - ERR_PTR() values fall into such a range and they should not point to any valid objects). However, on architectures where userland and kernel live in different MMU contexts (e.g. s390) access_ok() is a no-op and on those a range with a wraparound can reach fault_in_multipages_...(). Since any wraparound means EFAULT there, the fix is trivial - turn those while (uaddr <= end) ... into if (unlikely(uaddr > end)) return -EFAULT; do ... while (uaddr <= end); Reported-by: Jan Stancek <[email protected]> Tested-by: Jan Stancek <[email protected]> Cc: [email protected] # v3.5+ Signed-off-by: Al Viro <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent df04abf commit e23d415

File tree

1 file changed

+19
-19
lines changed

1 file changed

+19
-19
lines changed

include/linux/pagemap.h

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -571,56 +571,56 @@ static inline int fault_in_pages_readable(const char __user *uaddr, int size)
571571
*/
572572
static inline int fault_in_multipages_writeable(char __user *uaddr, int size)
573573
{
574-
int ret = 0;
575574
char __user *end = uaddr + size - 1;
576575

577576
if (unlikely(size == 0))
578-
return ret;
577+
return 0;
579578

579+
if (unlikely(uaddr > end))
580+
return -EFAULT;
580581
/*
581582
* Writing zeroes into userspace here is OK, because we know that if
582583
* the zero gets there, we'll be overwriting it.
583584
*/
584-
while (uaddr <= end) {
585-
ret = __put_user(0, uaddr);
586-
if (ret != 0)
587-
return ret;
585+
do {
586+
if (unlikely(__put_user(0, uaddr) != 0))
587+
return -EFAULT;
588588
uaddr += PAGE_SIZE;
589-
}
589+
} while (uaddr <= end);
590590

591591
/* Check whether the range spilled into the next page. */
592592
if (((unsigned long)uaddr & PAGE_MASK) ==
593593
((unsigned long)end & PAGE_MASK))
594-
ret = __put_user(0, end);
594+
return __put_user(0, end);
595595

596-
return ret;
596+
return 0;
597597
}
598598

599599
static inline int fault_in_multipages_readable(const char __user *uaddr,
600600
int size)
601601
{
602602
volatile char c;
603-
int ret = 0;
604603
const char __user *end = uaddr + size - 1;
605604

606605
if (unlikely(size == 0))
607-
return ret;
606+
return 0;
608607

609-
while (uaddr <= end) {
610-
ret = __get_user(c, uaddr);
611-
if (ret != 0)
612-
return ret;
608+
if (unlikely(uaddr > end))
609+
return -EFAULT;
610+
611+
do {
612+
if (unlikely(__get_user(c, uaddr) != 0))
613+
return -EFAULT;
613614
uaddr += PAGE_SIZE;
614-
}
615+
} while (uaddr <= end);
615616

616617
/* Check whether the range spilled into the next page. */
617618
if (((unsigned long)uaddr & PAGE_MASK) ==
618619
((unsigned long)end & PAGE_MASK)) {
619-
ret = __get_user(c, end);
620-
(void)c;
620+
return __get_user(c, end);
621621
}
622622

623-
return ret;
623+
return 0;
624624
}
625625

626626
int add_to_page_cache_locked(struct page *page, struct address_space *mapping,

0 commit comments

Comments
 (0)