Skip to content

Commit e130242

Browse files
arndbtorvalds
authored andcommitted
mm: simplify compat numa syscalls
The compat implementations for mbind, get_mempolicy, set_mempolicy and migrate_pages are just there to handle the subtly different layout of bitmaps on 32-bit hosts. The compat implementation however lacks some of the checks that are present in the native one, in particular for checking that the extra bits are all zero when user space has a larger mask size than the kernel. Worse, those extra bits do not get cleared when copying in or out of the kernel, which can lead to incorrect data as well. Unify the implementation to handle the compat bitmap layout directly in the get_nodes() and copy_nodes_to_user() helpers. Splitting out the get_bitmap() helper from get_nodes() also helps readability of the native case. On x86, two additional problems are addressed by this: compat tasks can pass a bitmap at the end of a mapping, causing a fault when reading across the page boundary for a 64-bit word. x32 tasks might also run into problems with get_mempolicy corrupting data when an odd number of 32-bit words gets passed. On parisc the migrate_pages() system call apparently had the wrong calling convention, as big-endian architectures expect the words inside of a bitmap to be swapped. This is not a problem though since parisc has no NUMA support. [[email protected]: fix mempolicy crash] Link: https://lkml.kernel.org/r/[email protected] Link: https://lore.kernel.org/lkml/YQPLG20V3dmOfq3a@osiris/ Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Arnd Bergmann <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Cc: Al Viro <[email protected]> Cc: Benjamin Herrenschmidt <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Christian Borntraeger <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: "David S. Miller" <[email protected]> Cc: Eric Biederman <[email protected]> Cc: Feng Tang <[email protected]> Cc: Heiko Carstens <[email protected]> Cc: Helge Deller <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: "James E.J. Bottomley" <[email protected]> Cc: Michael Ellerman <[email protected]> Cc: Paul Mackerras <[email protected]> Cc: Thomas Bogendoerfer <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Vasily Gorbik <[email protected]> Cc: Will Deacon <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 5b1b561 commit e130242

File tree

2 files changed

+64
-129
lines changed

2 files changed

+64
-129
lines changed

include/linux/compat.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -395,14 +395,6 @@ struct compat_kexec_segment;
395395
struct compat_mq_attr;
396396
struct compat_msgbuf;
397397

398-
#define BITS_PER_COMPAT_LONG (8*sizeof(compat_long_t))
399-
400-
#define BITS_TO_COMPAT_LONGS(bits) DIV_ROUND_UP(bits, BITS_PER_COMPAT_LONG)
401-
402-
long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
403-
unsigned long bitmap_size);
404-
long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
405-
unsigned long bitmap_size);
406398
void copy_siginfo_to_external32(struct compat_siginfo *to,
407399
const struct kernel_siginfo *from);
408400
int copy_siginfo_from_user32(kernel_siginfo_t *to,
@@ -976,6 +968,15 @@ static inline bool in_compat_syscall(void) { return false; }
976968

977969
#endif /* CONFIG_COMPAT */
978970

971+
#define BITS_PER_COMPAT_LONG (8*sizeof(compat_long_t))
972+
973+
#define BITS_TO_COMPAT_LONGS(bits) DIV_ROUND_UP(bits, BITS_PER_COMPAT_LONG)
974+
975+
long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
976+
unsigned long bitmap_size);
977+
long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
978+
unsigned long bitmap_size);
979+
979980
/*
980981
* Some legacy ABIs like the i386 one use less than natural alignment for 64-bit
981982
* types, and will need special compat treatment for that. Most architectures

mm/mempolicy.c

Lines changed: 55 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,66 +1362,63 @@ static long do_mbind(unsigned long start, unsigned long len,
13621362
/*
13631363
* User space interface with variable sized bitmaps for nodelists.
13641364
*/
1365+
static int get_bitmap(unsigned long *mask, const unsigned long __user *nmask,
1366+
unsigned long maxnode)
1367+
{
1368+
unsigned long nlongs = BITS_TO_LONGS(maxnode);
1369+
int ret;
1370+
1371+
if (in_compat_syscall())
1372+
ret = compat_get_bitmap(mask,
1373+
(const compat_ulong_t __user *)nmask,
1374+
maxnode);
1375+
else
1376+
ret = copy_from_user(mask, nmask,
1377+
nlongs * sizeof(unsigned long));
1378+
1379+
if (ret)
1380+
return -EFAULT;
1381+
1382+
if (maxnode % BITS_PER_LONG)
1383+
mask[nlongs - 1] &= (1UL << (maxnode % BITS_PER_LONG)) - 1;
1384+
1385+
return 0;
1386+
}
13651387

13661388
/* Copy a node mask from user space. */
13671389
static int get_nodes(nodemask_t *nodes, const unsigned long __user *nmask,
13681390
unsigned long maxnode)
13691391
{
1370-
unsigned long k;
1371-
unsigned long t;
1372-
unsigned long nlongs;
1373-
unsigned long endmask;
1374-
13751392
--maxnode;
13761393
nodes_clear(*nodes);
13771394
if (maxnode == 0 || !nmask)
13781395
return 0;
13791396
if (maxnode > PAGE_SIZE*BITS_PER_BYTE)
13801397
return -EINVAL;
13811398

1382-
nlongs = BITS_TO_LONGS(maxnode);
1383-
if ((maxnode % BITS_PER_LONG) == 0)
1384-
endmask = ~0UL;
1385-
else
1386-
endmask = (1UL << (maxnode % BITS_PER_LONG)) - 1;
1387-
13881399
/*
13891400
* When the user specified more nodes than supported just check
1390-
* if the non supported part is all zero.
1391-
*
1392-
* If maxnode have more longs than MAX_NUMNODES, check
1393-
* the bits in that area first. And then go through to
1394-
* check the rest bits which equal or bigger than MAX_NUMNODES.
1395-
* Otherwise, just check bits [MAX_NUMNODES, maxnode).
1401+
* if the non supported part is all zero, one word at a time,
1402+
* starting at the end.
13961403
*/
1397-
if (nlongs > BITS_TO_LONGS(MAX_NUMNODES)) {
1398-
for (k = BITS_TO_LONGS(MAX_NUMNODES); k < nlongs; k++) {
1399-
if (get_user(t, nmask + k))
1400-
return -EFAULT;
1401-
if (k == nlongs - 1) {
1402-
if (t & endmask)
1403-
return -EINVAL;
1404-
} else if (t)
1405-
return -EINVAL;
1406-
}
1407-
nlongs = BITS_TO_LONGS(MAX_NUMNODES);
1408-
endmask = ~0UL;
1409-
}
1404+
while (maxnode > MAX_NUMNODES) {
1405+
unsigned long bits = min_t(unsigned long, maxnode, BITS_PER_LONG);
1406+
unsigned long t;
14101407

1411-
if (maxnode > MAX_NUMNODES && MAX_NUMNODES % BITS_PER_LONG != 0) {
1412-
unsigned long valid_mask = endmask;
1413-
1414-
valid_mask &= ~((1UL << (MAX_NUMNODES % BITS_PER_LONG)) - 1);
1415-
if (get_user(t, nmask + nlongs - 1))
1408+
if (get_bitmap(&t, &nmask[maxnode / BITS_PER_LONG], bits))
14161409
return -EFAULT;
1417-
if (t & valid_mask)
1410+
1411+
if (maxnode - bits >= MAX_NUMNODES) {
1412+
maxnode -= bits;
1413+
} else {
1414+
maxnode = MAX_NUMNODES;
1415+
t &= ~((1UL << (MAX_NUMNODES % BITS_PER_LONG)) - 1);
1416+
}
1417+
if (t)
14181418
return -EINVAL;
14191419
}
14201420

1421-
if (copy_from_user(nodes_addr(*nodes), nmask, nlongs*sizeof(unsigned long)))
1422-
return -EFAULT;
1423-
nodes_addr(*nodes)[nlongs-1] &= endmask;
1424-
return 0;
1421+
return get_bitmap(nodes_addr(*nodes), nmask, maxnode);
14251422
}
14261423

14271424
/* Copy a kernel node mask to user space */
@@ -1430,14 +1427,24 @@ static int copy_nodes_to_user(unsigned long __user *mask, unsigned long maxnode,
14301427
{
14311428
unsigned long copy = ALIGN(maxnode-1, 64) / 8;
14321429
unsigned int nbytes = BITS_TO_LONGS(nr_node_ids) * sizeof(long);
1430+
bool compat = in_compat_syscall();
1431+
1432+
if (compat)
1433+
nbytes = BITS_TO_COMPAT_LONGS(nr_node_ids) * sizeof(compat_long_t);
14331434

14341435
if (copy > nbytes) {
14351436
if (copy > PAGE_SIZE)
14361437
return -EINVAL;
14371438
if (clear_user((char __user *)mask + nbytes, copy - nbytes))
14381439
return -EFAULT;
14391440
copy = nbytes;
1441+
maxnode = nr_node_ids;
14401442
}
1443+
1444+
if (compat)
1445+
return compat_put_bitmap((compat_ulong_t __user *)mask,
1446+
nodes_addr(*nodes), maxnode);
1447+
14411448
return copy_to_user(mask, nodes_addr(*nodes), copy) ? -EFAULT : 0;
14421449
}
14431450

@@ -1649,105 +1656,32 @@ COMPAT_SYSCALL_DEFINE5(get_mempolicy, int __user *, policy,
16491656
compat_ulong_t, maxnode,
16501657
compat_ulong_t, addr, compat_ulong_t, flags)
16511658
{
1652-
long err;
1653-
unsigned long __user *nm = NULL;
1654-
unsigned long nr_bits, alloc_size;
1655-
DECLARE_BITMAP(bm, MAX_NUMNODES);
1656-
1657-
nr_bits = min_t(unsigned long, maxnode-1, nr_node_ids);
1658-
alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
1659-
1660-
if (nmask)
1661-
nm = compat_alloc_user_space(alloc_size);
1662-
1663-
err = kernel_get_mempolicy(policy, nm, nr_bits+1, addr, flags);
1664-
1665-
if (!err && nmask) {
1666-
unsigned long copy_size;
1667-
copy_size = min_t(unsigned long, sizeof(bm), alloc_size);
1668-
err = copy_from_user(bm, nm, copy_size);
1669-
/* ensure entire bitmap is zeroed */
1670-
err |= clear_user(nmask, ALIGN(maxnode-1, 8) / 8);
1671-
err |= compat_put_bitmap(nmask, bm, nr_bits);
1672-
}
1673-
1674-
return err;
1659+
return kernel_get_mempolicy(policy, (unsigned long __user *)nmask,
1660+
maxnode, addr, flags);
16751661
}
16761662

16771663
COMPAT_SYSCALL_DEFINE3(set_mempolicy, int, mode, compat_ulong_t __user *, nmask,
16781664
compat_ulong_t, maxnode)
16791665
{
1680-
unsigned long __user *nm = NULL;
1681-
unsigned long nr_bits, alloc_size;
1682-
DECLARE_BITMAP(bm, MAX_NUMNODES);
1683-
1684-
nr_bits = min_t(unsigned long, maxnode-1, MAX_NUMNODES);
1685-
alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
1686-
1687-
if (nmask) {
1688-
if (compat_get_bitmap(bm, nmask, nr_bits))
1689-
return -EFAULT;
1690-
nm = compat_alloc_user_space(alloc_size);
1691-
if (copy_to_user(nm, bm, alloc_size))
1692-
return -EFAULT;
1693-
}
1694-
1695-
return kernel_set_mempolicy(mode, nm, nr_bits+1);
1666+
return kernel_set_mempolicy(mode, (unsigned long __user *)nmask, maxnode);
16961667
}
16971668

16981669
COMPAT_SYSCALL_DEFINE6(mbind, compat_ulong_t, start, compat_ulong_t, len,
16991670
compat_ulong_t, mode, compat_ulong_t __user *, nmask,
17001671
compat_ulong_t, maxnode, compat_ulong_t, flags)
17011672
{
1702-
unsigned long __user *nm = NULL;
1703-
unsigned long nr_bits, alloc_size;
1704-
nodemask_t bm;
1705-
1706-
nr_bits = min_t(unsigned long, maxnode-1, MAX_NUMNODES);
1707-
alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
1708-
1709-
if (nmask) {
1710-
if (compat_get_bitmap(nodes_addr(bm), nmask, nr_bits))
1711-
return -EFAULT;
1712-
nm = compat_alloc_user_space(alloc_size);
1713-
if (copy_to_user(nm, nodes_addr(bm), alloc_size))
1714-
return -EFAULT;
1715-
}
1716-
1717-
return kernel_mbind(start, len, mode, nm, nr_bits+1, flags);
1673+
return kernel_mbind(start, len, mode, (unsigned long __user *)nmask,
1674+
maxnode, flags);
17181675
}
17191676

17201677
COMPAT_SYSCALL_DEFINE4(migrate_pages, compat_pid_t, pid,
17211678
compat_ulong_t, maxnode,
17221679
const compat_ulong_t __user *, old_nodes,
17231680
const compat_ulong_t __user *, new_nodes)
17241681
{
1725-
unsigned long __user *old = NULL;
1726-
unsigned long __user *new = NULL;
1727-
nodemask_t tmp_mask;
1728-
unsigned long nr_bits;
1729-
unsigned long size;
1730-
1731-
nr_bits = min_t(unsigned long, maxnode - 1, MAX_NUMNODES);
1732-
size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
1733-
if (old_nodes) {
1734-
if (compat_get_bitmap(nodes_addr(tmp_mask), old_nodes, nr_bits))
1735-
return -EFAULT;
1736-
old = compat_alloc_user_space(new_nodes ? size * 2 : size);
1737-
if (new_nodes)
1738-
new = old + size / sizeof(unsigned long);
1739-
if (copy_to_user(old, nodes_addr(tmp_mask), size))
1740-
return -EFAULT;
1741-
}
1742-
if (new_nodes) {
1743-
if (compat_get_bitmap(nodes_addr(tmp_mask), new_nodes, nr_bits))
1744-
return -EFAULT;
1745-
if (new == NULL)
1746-
new = compat_alloc_user_space(size);
1747-
if (copy_to_user(new, nodes_addr(tmp_mask), size))
1748-
return -EFAULT;
1749-
}
1750-
return kernel_migrate_pages(pid, nr_bits + 1, old, new);
1682+
return kernel_migrate_pages(pid, maxnode,
1683+
(const unsigned long __user *)old_nodes,
1684+
(const unsigned long __user *)new_nodes);
17511685
}
17521686

17531687
#endif /* CONFIG_COMPAT */

0 commit comments

Comments
 (0)