
From: Andi Kleen <ak@muc.de>

This patch fixes the argument length checking in sched_setaffinity.

Previously it would error out when the length passed was smaller than
sizeof(cpumask_t).  And any bits beyond cpumask_s would be silently
ignored.

First this assumes that the user application knows the size of cpumask_t,
which should be kernel internal.  When you increase cpumask_t old
applications break and there is no good way for the application to find out
the cpumask_t size the kernel uses.

This patch changes it to do similar checking to the NUMA API calls: 

- Any length is ok as long as all online CPUs are covered (this could
  still cause application breakage with more CPUs, but there is no good way
  around it) 

- When the user passes more than cpumask_t bytes the excess bytes are
  checked to be zero.

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/kernel/sched.c |   36 +++++++++++++++++++++++++++++++-----
 1 files changed, 31 insertions(+), 5 deletions(-)

diff -puN kernel/sched.c~fix-argument-checking-in-sched_setaffinity kernel/sched.c
--- 25/kernel/sched.c~fix-argument-checking-in-sched_setaffinity	Tue Aug 31 14:35:12 2004
+++ 25-akpm/kernel/sched.c	Tue Aug 31 14:35:12 2004
@@ -3369,6 +3369,34 @@ out_unlock:
 	return retval;
 }
 
+static int get_user_cpu_mask(unsigned long __user *user_mask_ptr, unsigned len,
+			     cpumask_t *new_mask)
+{
+	if (len < sizeof(cpumask_t)) {
+		/* Smaller is ok as long as all online CPUs are covered */
+		int i, max = 0;
+		for_each_online_cpu(i)
+			max = i;
+		if (len < (max + 7)/8)
+			return -EINVAL;
+		memset(new_mask, 0, sizeof(cpumask_t));
+	} else if (len > sizeof(cpumask_t)) {
+		/* Longer is ok as long as all high bits are 0 */
+		int i;
+		if (len > PAGE_SIZE)
+			return -EINVAL;
+		for (i = sizeof(cpumask_t); i < len; i++) {
+			unsigned char val;
+			if (get_user(val, (unsigned char *)user_mask_ptr + i))
+				return -EFAULT;
+			if (val)
+				return -EINVAL;
+		}
+		len = sizeof(cpumask_t);
+	}
+	return copy_from_user(new_mask, user_mask_ptr, len) ? -EFAULT : 0;
+}
+
 /**
  * sys_sched_setaffinity - set the cpu affinity of a process
  * @pid: pid of the process
@@ -3382,11 +3410,9 @@ asmlinkage long sys_sched_setaffinity(pi
 	int retval;
 	task_t *p;
 
-	if (len < sizeof(new_mask))
-		return -EINVAL;
-
-	if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask)))
-		return -EFAULT;
+	retval = get_user_cpu_mask(user_mask_ptr, len, &new_mask);
+	if (retval)
+		return retval;
 
 	lock_cpu_hotplug();
 	read_lock(&tasklist_lock);
_
