Commit fa9dc265ace9774e62f0e31108e5f47911124bda
Committed by
Linus Torvalds
1 parent
cf77e988dd
Exists in
master
and in
20 other branches
cpumask: fix compat getaffinity
Commit a45185d2d "cpumask: convert kernel/compat.c" broke libnuma, which abuses sched_getaffinity to find out NR_CPUS in order to parse /sys/devices/system/node/node*/cpumap. On NUMA systems with less than 32 possibly CPUs, the current compat_sys_sched_getaffinity now returns '4' instead of the actual NR_CPUS/8, which makes libnuma bail out when parsing the cpumap. The libnuma call sched_getaffinity(0, bitmap, 4096) at first. It mean the libnuma expect the return value of sched_getaffinity() is either len argument or NR_CPUS. But it doesn't expect to return nr_cpu_ids. Strictly speaking, userland requirement are 1) Glibc assume the return value mean the lengh of initialized of mask argument. E.g. if sched_getaffinity(1024) return 128, glibc make zero fill rest 896 byte. 2) Libnuma assume the return value can be used to guess NR_CPUS in kernel. It assume len-arg<NR_CPUS makes -EINVAL. But it try len=4096 at first and 4096 is always bigger than NR_CPUS. Then, if we remove strange min_length normalization, we never hit -EINVAL case. sched_getaffinity() already solved this issue. This patch adapts compat_sys_sched_getaffinity() to match the non-compat case. Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Acked-by: Rusty Russell <rusty@rustcorp.com.au> Acked-by: Arnd Bergmann <arnd@arndb.de> Reported-by: Ken Werner <ken.werner@web.de> Cc: stable@kernel.org Cc: Andi Kleen <andi@firstfloor.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 1 changed file with 11 additions and 14 deletions Side-by-side Diff
kernel/compat.c
... | ... | @@ -495,29 +495,26 @@ |
495 | 495 | { |
496 | 496 | int ret; |
497 | 497 | cpumask_var_t mask; |
498 | - unsigned long *k; | |
499 | - unsigned int min_length = cpumask_size(); | |
500 | 498 | |
501 | - if (nr_cpu_ids <= BITS_PER_COMPAT_LONG) | |
502 | - min_length = sizeof(compat_ulong_t); | |
503 | - | |
504 | - if (len < min_length) | |
499 | + if ((len * BITS_PER_BYTE) < nr_cpu_ids) | |
505 | 500 | return -EINVAL; |
501 | + if (len & (sizeof(compat_ulong_t)-1)) | |
502 | + return -EINVAL; | |
506 | 503 | |
507 | 504 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) |
508 | 505 | return -ENOMEM; |
509 | 506 | |
510 | 507 | ret = sched_getaffinity(pid, mask); |
511 | - if (ret < 0) | |
512 | - goto out; | |
508 | + if (ret == 0) { | |
509 | + size_t retlen = min_t(size_t, len, cpumask_size()); | |
513 | 510 | |
514 | - k = cpumask_bits(mask); | |
515 | - ret = compat_put_bitmap(user_mask_ptr, k, min_length * 8); | |
516 | - if (ret == 0) | |
517 | - ret = min_length; | |
518 | - | |
519 | -out: | |
511 | + if (compat_put_bitmap(user_mask_ptr, cpumask_bits(mask), retlen * 8)) | |
512 | + ret = -EFAULT; | |
513 | + else | |
514 | + ret = retlen; | |
515 | + } | |
520 | 516 | free_cpumask_var(mask); |
517 | + | |
521 | 518 | return ret; |
522 | 519 | } |
523 | 520 |