Fix up sys_move_pages(): 1. Update comments. 2. Make the parameter to sys_move_pages void * 3. Check for boundary conditions on the size of the pm array. 4. Fix order of parameters to kmalloc(). 5. Process parameters before taking mmap_sem and detangle the error states for pages migrated. 6. Add the required call to migrate_prep(). Signed-off-by: Christoph Lameter Index: linux-2.6.17-rc4-mm2/mm/migrate.c =================================================================== --- linux-2.6.17-rc4-mm2.orig/mm/migrate.c 2006-05-20 21:14:37.000000000 -0700 +++ linux-2.6.17-rc4-mm2/mm/migrate.c 2006-05-20 21:29:39.000000000 -0700 @@ -63,9 +63,8 @@ } /* - * migrate_prep() needs to be called after we have compiled the list of pages - * to be migrated using isolate_lru_page() but before we begin a series of calls - * to migrate_pages(). + * migrate_prep() needs to be called before we start compiling a list of pages + * to be migrated using isolate_lru_page(). */ int migrate_prep(void) { @@ -723,6 +722,7 @@ * Move a list of individual pages */ struct page_to_node { + unsigned long addr; struct page *page; int node; int status; @@ -749,7 +749,7 @@ * process. */ asmlinkage long sys_move_pages(int pid, unsigned long nr_pages, - const unsigned long __user *pages, + const void __user * __user *pages, const int __user *nodes, int __user *status, int flags) { @@ -787,98 +787,113 @@ } task_nodes = cpuset_mems_allowed(task); - pm = kmalloc(GFP_KERNEL, (nr_pages + 1) * sizeof(struct page_to_node)); + + /* Limit nr_pages so that the multiplication may not overflow */ + if (nr_pages >= ULONG_MAX / sizeof(struct page_to_node) - 1) { + err = -ENOMEM; + goto out2; + } + + pm = kmalloc((nr_pages + 1) * sizeof(struct page_to_node), + GFP_KERNEL); if (!pm) { err = -ENOMEM; goto out2; } + /* + * Get parameters from user space and initialize the pm + * array. Return various errors if the user did something wrong. + */ + for (i = 0; i < nr_pages; i++) { + const void *p; + + pm[i].page = ZERO_PAGE(0); + + err = -EFAULT; + if (get_user(p, pages + i)) + goto out; + + pm[i].addr = (unsigned long)p; + if (nodes) { + int node; + + if (get_user(node, nodes + i)) + goto out; + + err = -ENOENT; + if (!node_online(node)) + goto out; + + err = -EPERM; + if (!node_isset(node, task_nodes)) + goto out; + + pm[i].node= node; + } + } + pm[nr_pages].page = NULL; + down_read(&mm->mmap_sem); - for(i = 0 ; i < nr_pages; i++) { - unsigned long addr; - int node; + /* + * Build a list of pages to migrate + */ + migrate_prep(); + for (i = 0; i < nr_pages; i++) { struct vm_area_struct *vma; struct page *page; - - pm[i].page = ZERO_PAGE(0); + int node; err = -EFAULT; - if (get_user(addr, pages + i)) - goto putback; - - vma = find_vma(mm, addr); + vma = find_vma(mm, pm[i].addr); if (!vma) goto set_status; - page = follow_page(vma, addr, FOLL_GET); + page = follow_page(vma, pm[i].addr, FOLL_GET); err = -ENOENT; if (!page) goto set_status; pm[i].page = page; - if (!nodes) { - err = page_to_nid(page); - put_page(page); - goto set_status; - } + node = page_to_nid(page); + + if (!nodes || node == pm[i].node) + /* + * Node already in the right place or status req + */ + goto put_and_set; err = -EPERM; if (page_mapcount(page) > 1 && - !(flags & MPOL_MF_MOVE_ALL)) { - put_page(page); - goto set_status; - } - + !(flags & MPOL_MF_MOVE_ALL)) + goto put_and_set; err = isolate_lru_page(page, &pagelist); - __put_page(page); - if (err) - goto remove; - - err = -EFAULT; - if (get_user(node, nodes + i)) - goto remove; - - err = -ENOENT; - if (!node_online(node)) - goto remove; - - err = -EPERM; - if (!node_isset(node, task_nodes)) - goto remove; - - pm[i].node = node; - err = -EAGAIN; - if (node != page_to_nid(page)) - goto set_status; - - err = node; -remove: - list_del(&page->lru); - move_to_lru(page); +put_and_set: + /* + * Either remove the duplicate refcount from + * isolate_lru_page() or drop the page ref if it was + * not isolated. + */ + put_page(page); set_status: pm[i].status = err; } - err = 0; - if (!nodes || list_empty(&pagelist)) - goto out; - - pm[nr_pages].page = NULL; - - err = migrate_pages(&pagelist, new_page_node, (unsigned long)pm); - goto out; -putback: - putback_lru_pages(&pagelist); + err = 0; + if (nodes && !list_empty(&pagelist)) + err = migrate_pages(&pagelist, new_page_node, + (unsigned long)pm); -out: up_read(&mm->mmap_sem); if (err >= 0) /* Return status information */ - for(i = 0; i < nr_pages; i++) - put_user(pm[i].status, status +i); + for (i = 0; i < nr_pages; i++) + if (put_user(pm[i].status, status + i)) + err = -EFAULT; +out: kfree(pm); out2: mmput(mm);