Commit fa9dc265ace9774e62f0e31108e5f47911124bda

Authored by KOSAKI Motohiro
Committed by Linus Torvalds
1 parent cf77e988dd

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

... ... @@ -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