mirror of
https://github.com/torvalds/linux.git
synced 2026-06-03 12:03:54 +02:00
mm/migrate: fix do_pages_stat in compat mode
For arrays with more than 16 entries, the old code would incorrectly
advance the pages pointer by 16 words instead of 16 compat_uptr_t. Fix by
doing the pointer arithmetic inside get_compat_pages_array where pages32
is already a correctly-typed pointer.
Discovered while working on PostgreSQL 18's new NUMA introspection code.
Link: https://lkml.kernel.org/r/aGREU0XTB48w9CwN@msg.df7cb.de
Fixes: 5b1b561ba7 ("mm: simplify compat_sys_move_pages")
Signed-off-by: Christoph Berg <myon@debian.org>
Acked-by: David Hildenbrand <david@redhat.com>
Suggested-by: David Hildenbrand <david@redhat.com>
Reported-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reported-by: Tomas Vondra <tomas@vondra.me>
Closes: https://www.postgresql.org/message-id/flat/6342f601-77de-4ee0-8c2a-3deb50ceac5b%40vondra.me#86402e3d80c031788f5f55b42c459471
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Byungchul Park <byungchul@sk.com>
Cc: Gregory Price <gourry@gourry.net>
Cc: "Huang, Ying" <ying.huang@linux.alibaba.com>
Cc: Joshua Hahn <joshua.hahnjy@gmail.com>
Cc: Mathew Brost <matthew.brost@intel.com>
Cc: Rakie Kim <rakie.kim@sk.com>
Cc: Zi Yan <ziy@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
bb1b5929b4
commit
10d04c26ab
14
mm/migrate.c
14
mm/migrate.c
|
|
@ -2399,6 +2399,7 @@ static void do_pages_stat_array(struct mm_struct *mm, unsigned long nr_pages,
|
|||
|
||||
static int get_compat_pages_array(const void __user *chunk_pages[],
|
||||
const void __user * __user *pages,
|
||||
unsigned long chunk_offset,
|
||||
unsigned long chunk_nr)
|
||||
{
|
||||
compat_uptr_t __user *pages32 = (compat_uptr_t __user *)pages;
|
||||
|
|
@ -2406,7 +2407,7 @@ static int get_compat_pages_array(const void __user *chunk_pages[],
|
|||
int i;
|
||||
|
||||
for (i = 0; i < chunk_nr; i++) {
|
||||
if (get_user(p, pages32 + i))
|
||||
if (get_user(p, pages32 + chunk_offset + i))
|
||||
return -EFAULT;
|
||||
chunk_pages[i] = compat_ptr(p);
|
||||
}
|
||||
|
|
@ -2425,27 +2426,28 @@ static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages,
|
|||
#define DO_PAGES_STAT_CHUNK_NR 16UL
|
||||
const void __user *chunk_pages[DO_PAGES_STAT_CHUNK_NR];
|
||||
int chunk_status[DO_PAGES_STAT_CHUNK_NR];
|
||||
unsigned long chunk_offset = 0;
|
||||
|
||||
while (nr_pages) {
|
||||
unsigned long chunk_nr = min(nr_pages, DO_PAGES_STAT_CHUNK_NR);
|
||||
|
||||
if (in_compat_syscall()) {
|
||||
if (get_compat_pages_array(chunk_pages, pages,
|
||||
chunk_nr))
|
||||
chunk_offset, chunk_nr))
|
||||
break;
|
||||
} else {
|
||||
if (copy_from_user(chunk_pages, pages,
|
||||
if (copy_from_user(chunk_pages, pages + chunk_offset,
|
||||
chunk_nr * sizeof(*chunk_pages)))
|
||||
break;
|
||||
}
|
||||
|
||||
do_pages_stat_array(mm, chunk_nr, chunk_pages, chunk_status);
|
||||
|
||||
if (copy_to_user(status, chunk_status, chunk_nr * sizeof(*status)))
|
||||
if (copy_to_user(status + chunk_offset, chunk_status,
|
||||
chunk_nr * sizeof(*status)))
|
||||
break;
|
||||
|
||||
pages += chunk_nr;
|
||||
status += chunk_nr;
|
||||
chunk_offset += chunk_nr;
|
||||
nr_pages -= chunk_nr;
|
||||
}
|
||||
return nr_pages ? -EFAULT : 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user