compat.c 15.7 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 *  linux/kernel/compat.c
 *
 *  Kernel compatibililty routines for e.g. 32 bit syscall support
 *  on 64 bit kernels.
 *
 *  Copyright (C) 2002-2003 Stephen Rothwell, IBM Corporation
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 */

#include <linux/linkage.h>
#include <linux/compat.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/signal.h>
#include <linux/sched.h>	/* for MAX_SCHEDULE_TIMEOUT */
#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/security.h>
23
#include <linux/timex.h>
24
#include <linux/export.h>
25
#include <linux/migrate.h>
26
#include <linux/posix-timers.h>
27
#include <linux/times.h>
28
#include <linux/ptrace.h>
29
#include <linux/gfp.h>
Linus Torvalds's avatar
Linus Torvalds committed
30

31
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
32

33 34 35 36 37
int compat_get_timex(struct timex *txc, const struct compat_timex __user *utp)
{
	struct compat_timex tx32;

	if (copy_from_user(&tx32, utp, sizeof(struct compat_timex)))
38 39
		return -EFAULT;

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
	txc->modes = tx32.modes;
	txc->offset = tx32.offset;
	txc->freq = tx32.freq;
	txc->maxerror = tx32.maxerror;
	txc->esterror = tx32.esterror;
	txc->status = tx32.status;
	txc->constant = tx32.constant;
	txc->precision = tx32.precision;
	txc->tolerance = tx32.tolerance;
	txc->time.tv_sec = tx32.time.tv_sec;
	txc->time.tv_usec = tx32.time.tv_usec;
	txc->tick = tx32.tick;
	txc->ppsfreq = tx32.ppsfreq;
	txc->jitter = tx32.jitter;
	txc->shift = tx32.shift;
	txc->stabil = tx32.stabil;
	txc->jitcnt = tx32.jitcnt;
	txc->calcnt = tx32.calcnt;
	txc->errcnt = tx32.errcnt;
	txc->stbcnt = tx32.stbcnt;

61 62 63
	return 0;
}

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
int compat_put_timex(struct compat_timex __user *utp, const struct timex *txc)
{
	struct compat_timex tx32;

	memset(&tx32, 0, sizeof(struct compat_timex));
	tx32.modes = txc->modes;
	tx32.offset = txc->offset;
	tx32.freq = txc->freq;
	tx32.maxerror = txc->maxerror;
	tx32.esterror = txc->esterror;
	tx32.status = txc->status;
	tx32.constant = txc->constant;
	tx32.precision = txc->precision;
	tx32.tolerance = txc->tolerance;
	tx32.time.tv_sec = txc->time.tv_sec;
	tx32.time.tv_usec = txc->time.tv_usec;
	tx32.tick = txc->tick;
	tx32.ppsfreq = txc->ppsfreq;
	tx32.jitter = txc->jitter;
	tx32.shift = txc->shift;
	tx32.stabil = txc->stabil;
	tx32.jitcnt = txc->jitcnt;
	tx32.calcnt = txc->calcnt;
	tx32.errcnt = txc->errcnt;
	tx32.stbcnt = txc->stbcnt;
	tx32.tai = txc->tai;
	if (copy_to_user(utp, &tx32, sizeof(struct compat_timex)))
91 92 93 94
		return -EFAULT;
	return 0;
}

95
static int __compat_get_timeval(struct timeval *tv, const struct compat_timeval __user *ctv)
96 97 98 99 100 101
{
	return (!access_ok(VERIFY_READ, ctv, sizeof(*ctv)) ||
			__get_user(tv->tv_sec, &ctv->tv_sec) ||
			__get_user(tv->tv_usec, &ctv->tv_usec)) ? -EFAULT : 0;
}

102
static int __compat_put_timeval(const struct timeval *tv, struct compat_timeval __user *ctv)
103 104 105 106 107 108
{
	return (!access_ok(VERIFY_WRITE, ctv, sizeof(*ctv)) ||
			__put_user(tv->tv_sec, &ctv->tv_sec) ||
			__put_user(tv->tv_usec, &ctv->tv_usec)) ? -EFAULT : 0;
}

109
static int __compat_get_timespec(struct timespec *ts, const struct compat_timespec __user *cts)
Linus Torvalds's avatar
Linus Torvalds committed
110 111 112 113 114 115
{
	return (!access_ok(VERIFY_READ, cts, sizeof(*cts)) ||
			__get_user(ts->tv_sec, &cts->tv_sec) ||
			__get_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0;
}

116
static int __compat_put_timespec(const struct timespec *ts, struct compat_timespec __user *cts)
Linus Torvalds's avatar
Linus Torvalds committed
117 118 119 120 121 122
{
	return (!access_ok(VERIFY_WRITE, cts, sizeof(*cts)) ||
			__put_user(ts->tv_sec, &cts->tv_sec) ||
			__put_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0;
}

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
static int __compat_get_timespec64(struct timespec64 *ts64,
				   const struct compat_timespec __user *cts)
{
	struct compat_timespec ts;
	int ret;

	ret = copy_from_user(&ts, cts, sizeof(ts));
	if (ret)
		return -EFAULT;

	ts64->tv_sec = ts.tv_sec;
	ts64->tv_nsec = ts.tv_nsec;

	return 0;
}

static int __compat_put_timespec64(const struct timespec64 *ts64,
				   struct compat_timespec __user *cts)
{
	struct compat_timespec ts = {
		.tv_sec = ts64->tv_sec,
		.tv_nsec = ts64->tv_nsec
	};
	return copy_to_user(cts, &ts, sizeof(ts)) ? -EFAULT : 0;
}

int compat_get_timespec64(struct timespec64 *ts, const void __user *uts)
{
	if (COMPAT_USE_64BIT_TIME)
		return copy_from_user(ts, uts, sizeof(*ts)) ? -EFAULT : 0;
	else
		return __compat_get_timespec64(ts, uts);
}
EXPORT_SYMBOL_GPL(compat_get_timespec64);

int compat_put_timespec64(const struct timespec64 *ts, void __user *uts)
{
	if (COMPAT_USE_64BIT_TIME)
		return copy_to_user(uts, ts, sizeof(*ts)) ? -EFAULT : 0;
	else
		return __compat_put_timespec64(ts, uts);
}
EXPORT_SYMBOL_GPL(compat_put_timespec64);

167 168 169
int compat_get_timeval(struct timeval *tv, const void __user *utv)
{
	if (COMPAT_USE_64BIT_TIME)
170
		return copy_from_user(tv, utv, sizeof(*tv)) ? -EFAULT : 0;
171
	else
172
		return __compat_get_timeval(tv, utv);
173 174 175 176 177 178
}
EXPORT_SYMBOL_GPL(compat_get_timeval);

int compat_put_timeval(const struct timeval *tv, void __user *utv)
{
	if (COMPAT_USE_64BIT_TIME)
179
		return copy_to_user(utv, tv, sizeof(*tv)) ? -EFAULT : 0;
180
	else
181
		return __compat_put_timeval(tv, utv);
182 183 184 185 186 187
}
EXPORT_SYMBOL_GPL(compat_put_timeval);

int compat_get_timespec(struct timespec *ts, const void __user *uts)
{
	if (COMPAT_USE_64BIT_TIME)
188
		return copy_from_user(ts, uts, sizeof(*ts)) ? -EFAULT : 0;
189
	else
190
		return __compat_get_timespec(ts, uts);
191 192 193 194 195 196
}
EXPORT_SYMBOL_GPL(compat_get_timespec);

int compat_put_timespec(const struct timespec *ts, void __user *uts)
{
	if (COMPAT_USE_64BIT_TIME)
197
		return copy_to_user(uts, ts, sizeof(*ts)) ? -EFAULT : 0;
198
	else
199
		return __compat_put_timespec(ts, uts);
200 201 202
}
EXPORT_SYMBOL_GPL(compat_put_timespec);

203
int get_compat_itimerval(struct itimerval *o, const struct compat_itimerval __user *i)
Linus Torvalds's avatar
Linus Torvalds committed
204
{
205
	struct compat_itimerval v32;
206

207 208 209 210 211 212 213
	if (copy_from_user(&v32, i, sizeof(struct compat_itimerval)))
		return -EFAULT;
	o->it_interval.tv_sec = v32.it_interval.tv_sec;
	o->it_interval.tv_usec = v32.it_interval.tv_usec;
	o->it_value.tv_sec = v32.it_value.tv_sec;
	o->it_value.tv_usec = v32.it_value.tv_usec;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
214 215
}

216
int put_compat_itimerval(struct compat_itimerval __user *o, const struct itimerval *i)
Linus Torvalds's avatar
Linus Torvalds committed
217
{
218
	struct compat_itimerval v32;
Linus Torvalds's avatar
Linus Torvalds committed
219

220 221 222 223 224
	v32.it_interval.tv_sec = i->it_interval.tv_sec;
	v32.it_interval.tv_usec = i->it_interval.tv_usec;
	v32.it_value.tv_sec = i->it_value.tv_sec;
	v32.it_value.tv_usec = i->it_value.tv_usec;
	return copy_to_user(o, &v32, sizeof(struct compat_itimerval)) ? -EFAULT : 0;
Linus Torvalds's avatar
Linus Torvalds committed
225 226
}

227 228
#ifdef __ARCH_WANT_SYS_SIGPROCMASK

229 230 231 232 233
/*
 * sys_sigprocmask SIG_SETMASK sets the first (compat) word of the
 * blocked set of signals to the supplied signal set
 */
static inline void compat_sig_setmask(sigset_t *blocked, compat_sigset_word set)
Linus Torvalds's avatar
Linus Torvalds committed
234
{
235 236
	memcpy(blocked->sig, &set, sizeof(set));
}
Linus Torvalds's avatar
Linus Torvalds committed
237

238 239 240
COMPAT_SYSCALL_DEFINE3(sigprocmask, int, how,
		       compat_old_sigset_t __user *, nset,
		       compat_old_sigset_t __user *, oset)
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
{
	old_sigset_t old_set, new_set;
	sigset_t new_blocked;

	old_set = current->blocked.sig[0];

	if (nset) {
		if (get_user(new_set, nset))
			return -EFAULT;
		new_set &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP));

		new_blocked = current->blocked;

		switch (how) {
		case SIG_BLOCK:
			sigaddsetmask(&new_blocked, new_set);
			break;
		case SIG_UNBLOCK:
			sigdelsetmask(&new_blocked, new_set);
			break;
		case SIG_SETMASK:
			compat_sig_setmask(&new_blocked, new_set);
			break;
		default:
			return -EINVAL;
		}

		set_current_blocked(&new_blocked);
	}

	if (oset) {
		if (put_user(old_set, oset))
			return -EFAULT;
	}

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
277 278
}

279 280
#endif

Linus Torvalds's avatar
Linus Torvalds committed
281 282
int put_compat_rusage(const struct rusage *r, struct compat_rusage __user *ru)
{
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
	struct compat_rusage r32;
	memset(&r32, 0, sizeof(r32));
	r32.ru_utime.tv_sec = r->ru_utime.tv_sec;
	r32.ru_utime.tv_usec = r->ru_utime.tv_usec;
	r32.ru_stime.tv_sec = r->ru_stime.tv_sec;
	r32.ru_stime.tv_usec = r->ru_stime.tv_usec;
	r32.ru_maxrss = r->ru_maxrss;
	r32.ru_ixrss = r->ru_ixrss;
	r32.ru_idrss = r->ru_idrss;
	r32.ru_isrss = r->ru_isrss;
	r32.ru_minflt = r->ru_minflt;
	r32.ru_majflt = r->ru_majflt;
	r32.ru_nswap = r->ru_nswap;
	r32.ru_inblock = r->ru_inblock;
	r32.ru_oublock = r->ru_oublock;
	r32.ru_msgsnd = r->ru_msgsnd;
	r32.ru_msgrcv = r->ru_msgrcv;
	r32.ru_nsignals = r->ru_nsignals;
	r32.ru_nvcsw = r->ru_nvcsw;
	r32.ru_nivcsw = r->ru_nivcsw;
	if (copy_to_user(ru, &r32, sizeof(r32)))
Linus Torvalds's avatar
Linus Torvalds committed
304 305 306 307 308
		return -EFAULT;
	return 0;
}

static int compat_get_user_cpu_mask(compat_ulong_t __user *user_mask_ptr,
309
				    unsigned len, struct cpumask *new_mask)
Linus Torvalds's avatar
Linus Torvalds committed
310 311 312
{
	unsigned long *k;

313 314 315 316
	if (len < cpumask_size())
		memset(new_mask, 0, cpumask_size());
	else if (len > cpumask_size())
		len = cpumask_size();
Linus Torvalds's avatar
Linus Torvalds committed
317

318
	k = cpumask_bits(new_mask);
Linus Torvalds's avatar
Linus Torvalds committed
319 320 321
	return compat_get_bitmap(k, user_mask_ptr, len * 8);
}

322 323 324
COMPAT_SYSCALL_DEFINE3(sched_setaffinity, compat_pid_t, pid,
		       unsigned int, len,
		       compat_ulong_t __user *, user_mask_ptr)
Linus Torvalds's avatar
Linus Torvalds committed
325
{
326
	cpumask_var_t new_mask;
Linus Torvalds's avatar
Linus Torvalds committed
327 328
	int retval;

329 330 331 332
	if (!alloc_cpumask_var(&new_mask, GFP_KERNEL))
		return -ENOMEM;

	retval = compat_get_user_cpu_mask(user_mask_ptr, len, new_mask);
Linus Torvalds's avatar
Linus Torvalds committed
333
	if (retval)
334
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
335

336 337 338 339
	retval = sched_setaffinity(pid, new_mask);
out:
	free_cpumask_var(new_mask);
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
340 341
}

342 343
COMPAT_SYSCALL_DEFINE3(sched_getaffinity, compat_pid_t,  pid, unsigned int, len,
		       compat_ulong_t __user *, user_mask_ptr)
Linus Torvalds's avatar
Linus Torvalds committed
344 345
{
	int ret;
346
	cpumask_var_t mask;
Linus Torvalds's avatar
Linus Torvalds committed
347

348 349 350
	if ((len * BITS_PER_BYTE) < nr_cpu_ids)
		return -EINVAL;
	if (len & (sizeof(compat_ulong_t)-1))
Linus Torvalds's avatar
Linus Torvalds committed
351 352
		return -EINVAL;

353 354 355 356
	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
		return -ENOMEM;

	ret = sched_getaffinity(pid, mask);
357
	if (ret == 0) {
358
		unsigned int retlen = min(len, cpumask_size());
Linus Torvalds's avatar
Linus Torvalds committed
359

360 361 362 363 364
		if (compat_put_bitmap(user_mask_ptr, cpumask_bits(mask), retlen * 8))
			ret = -EFAULT;
		else
			ret = retlen;
	}
365
	free_cpumask_var(mask);
366

367
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
368 369
}

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
int get_compat_itimerspec64(struct itimerspec64 *its,
			const struct compat_itimerspec __user *uits)
{

	if (__compat_get_timespec64(&its->it_interval, &uits->it_interval) ||
	    __compat_get_timespec64(&its->it_value, &uits->it_value))
		return -EFAULT;
	return 0;
}
EXPORT_SYMBOL_GPL(get_compat_itimerspec64);

int put_compat_itimerspec64(const struct itimerspec64 *its,
			struct compat_itimerspec __user *uits)
{
	if (__compat_put_timespec64(&its->it_interval, &uits->it_interval) ||
	    __compat_put_timespec64(&its->it_value, &uits->it_value))
		return -EFAULT;
	return 0;
}
EXPORT_SYMBOL_GPL(put_compat_itimerspec64);

Linus Torvalds's avatar
Linus Torvalds committed
391 392 393 394 395 396 397 398 399 400
/*
 * We currently only need the following fields from the sigevent
 * structure: sigev_value, sigev_signo, sig_notify and (sometimes
 * sigev_notify_thread_id).  The others are handled in user mode.
 * We also assume that copying sigev_value.sival_int is sufficient
 * to keep all the bits of sigev_value.sival_ptr intact.
 */
int get_compat_sigevent(struct sigevent *event,
		const struct compat_sigevent __user *u_event)
{
401
	memset(event, 0, sizeof(*event));
Linus Torvalds's avatar
Linus Torvalds committed
402 403 404 405 406 407 408 409 410 411
	return (!access_ok(VERIFY_READ, u_event, sizeof(*u_event)) ||
		__get_user(event->sigev_value.sival_int,
			&u_event->sigev_value.sival_int) ||
		__get_user(event->sigev_signo, &u_event->sigev_signo) ||
		__get_user(event->sigev_notify, &u_event->sigev_notify) ||
		__get_user(event->sigev_notify_thread_id,
			&u_event->sigev_notify_thread_id))
		? -EFAULT : 0;
}

412
long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
Linus Torvalds's avatar
Linus Torvalds committed
413 414 415 416 417 418
		       unsigned long bitmap_size)
{
	unsigned long nr_compat_longs;

	/* align bitmap up to nearest compat_long_t boundary */
	bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
419
	nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
Linus Torvalds's avatar
Linus Torvalds committed
420 421 422 423

	if (!access_ok(VERIFY_READ, umask, bitmap_size / 8))
		return -EFAULT;

424 425 426 427 428 429 430
	user_access_begin();
	while (nr_compat_longs > 1) {
		compat_ulong_t l1, l2;
		unsafe_get_user(l1, umask++, Efault);
		unsafe_get_user(l2, umask++, Efault);
		*mask++ = ((unsigned long)l2 << BITS_PER_COMPAT_LONG) | l1;
		nr_compat_longs -= 2;
Linus Torvalds's avatar
Linus Torvalds committed
431
	}
432 433 434
	if (nr_compat_longs)
		unsafe_get_user(*mask, umask++, Efault);
	user_access_end();
Linus Torvalds's avatar
Linus Torvalds committed
435
	return 0;
436 437 438 439

Efault:
	user_access_end();
	return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
440 441 442 443 444 445 446 447 448
}

long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
		       unsigned long bitmap_size)
{
	unsigned long nr_compat_longs;

	/* align bitmap up to nearest compat_long_t boundary */
	bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
449
	nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
Linus Torvalds's avatar
Linus Torvalds committed
450 451 452 453

	if (!access_ok(VERIFY_WRITE, umask, bitmap_size / 8))
		return -EFAULT;

454 455 456 457 458 459
	user_access_begin();
	while (nr_compat_longs > 1) {
		unsigned long m = *mask++;
		unsafe_put_user((compat_ulong_t)m, umask++, Efault);
		unsafe_put_user(m >> BITS_PER_COMPAT_LONG, umask++, Efault);
		nr_compat_longs -= 2;
Linus Torvalds's avatar
Linus Torvalds committed
460
	}
461 462 463
	if (nr_compat_longs)
		unsafe_put_user((compat_ulong_t)*mask, umask++, Efault);
	user_access_end();
Linus Torvalds's avatar
Linus Torvalds committed
464
	return 0;
465 466 467
Efault:
	user_access_end();
	return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
468 469
}

Al Viro's avatar
Al Viro committed
470 471
int
get_compat_sigset(sigset_t *set, const compat_sigset_t __user *compat)
Linus Torvalds's avatar
Linus Torvalds committed
472
{
Al Viro's avatar
Al Viro committed
473 474 475 476
#ifdef __BIG_ENDIAN
	compat_sigset_t v;
	if (copy_from_user(&v, compat, sizeof(compat_sigset_t)))
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
477
	switch (_NSIG_WORDS) {
Al Viro's avatar
Al Viro committed
478 479 480 481
	case 4: set->sig[3] = v.sig[6] | (((long)v.sig[7]) << 32 );
	case 3: set->sig[2] = v.sig[4] | (((long)v.sig[5]) << 32 );
	case 2: set->sig[1] = v.sig[2] | (((long)v.sig[3]) << 32 );
	case 1: set->sig[0] = v.sig[0] | (((long)v.sig[1]) << 32 );
Linus Torvalds's avatar
Linus Torvalds committed
482
	}
Al Viro's avatar
Al Viro committed
483 484 485 486 487
#else
	if (copy_from_user(set, compat, sizeof(compat_sigset_t)))
		return -EFAULT;
#endif
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
488
}
Al Viro's avatar
Al Viro committed
489
EXPORT_SYMBOL_GPL(get_compat_sigset);
Linus Torvalds's avatar
Linus Torvalds committed
490

491 492 493
int
put_compat_sigset(compat_sigset_t __user *compat, const sigset_t *set,
		  unsigned int size)
494
{
495 496 497
	/* size <= sizeof(compat_sigset_t) <= sizeof(sigset_t) */
#ifdef __BIG_ENDIAN
	compat_sigset_t v;
498
	switch (_NSIG_WORDS) {
499 500 501 502
	case 4: v.sig[7] = (set->sig[3] >> 32); v.sig[6] = set->sig[3];
	case 3: v.sig[5] = (set->sig[2] >> 32); v.sig[4] = set->sig[2];
	case 2: v.sig[3] = (set->sig[1] >> 32); v.sig[2] = set->sig[1];
	case 1: v.sig[1] = (set->sig[0] >> 32); v.sig[0] = set->sig[0];
503
	}
504 505 506 507
	return copy_to_user(compat, &v, size) ? -EFAULT : 0;
#else
	return copy_to_user(compat, set, size) ? -EFAULT : 0;
#endif
508 509
}

510
#ifdef CONFIG_NUMA
511 512 513 514 515
COMPAT_SYSCALL_DEFINE6(move_pages, pid_t, pid, compat_ulong_t, nr_pages,
		       compat_uptr_t __user *, pages32,
		       const int __user *, nodes,
		       int __user *, status,
		       int, flags)
516 517 518 519 520 521 522 523
{
	const void __user * __user *pages;
	int i;

	pages = compat_alloc_user_space(nr_pages * sizeof(void *));
	for (i = 0; i < nr_pages; i++) {
		compat_uptr_t p;

524
		if (get_user(p, pages32 + i) ||
525 526 527 528 529
			put_user(compat_ptr(p), pages + i))
			return -EFAULT;
	}
	return sys_move_pages(pid, nr_pages, pages, nodes, status, flags);
}
530

531 532 533 534
COMPAT_SYSCALL_DEFINE4(migrate_pages, compat_pid_t, pid,
		       compat_ulong_t, maxnode,
		       const compat_ulong_t __user *, old_nodes,
		       const compat_ulong_t __user *, new_nodes)
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
{
	unsigned long __user *old = NULL;
	unsigned long __user *new = NULL;
	nodemask_t tmp_mask;
	unsigned long nr_bits;
	unsigned long size;

	nr_bits = min_t(unsigned long, maxnode - 1, MAX_NUMNODES);
	size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
	if (old_nodes) {
		if (compat_get_bitmap(nodes_addr(tmp_mask), old_nodes, nr_bits))
			return -EFAULT;
		old = compat_alloc_user_space(new_nodes ? size * 2 : size);
		if (new_nodes)
			new = old + size / sizeof(unsigned long);
		if (copy_to_user(old, nodes_addr(tmp_mask), size))
			return -EFAULT;
	}
	if (new_nodes) {
		if (compat_get_bitmap(nodes_addr(tmp_mask), new_nodes, nr_bits))
			return -EFAULT;
		if (new == NULL)
			new = compat_alloc_user_space(size);
		if (copy_to_user(new, nodes_addr(tmp_mask), size))
			return -EFAULT;
	}
	return sys_migrate_pages(pid, nr_bits + 1, old, new);
}
563
#endif
564

565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
/*
 * Allocate user-space memory for the duration of a single system call,
 * in order to marshall parameters inside a compat thunk.
 */
void __user *compat_alloc_user_space(unsigned long len)
{
	void __user *ptr;

	/* If len would occupy more than half of the entire compat space... */
	if (unlikely(len > (((compat_uptr_t)~0) >> 1)))
		return NULL;

	ptr = arch_compat_alloc_user_space(len);

	if (unlikely(!access_ok(VERIFY_WRITE, ptr, len)))
		return NULL;

	return ptr;
}
EXPORT_SYMBOL_GPL(compat_alloc_user_space);