diff options
| -rw-r--r-- | include/linux/syscalls.h | 4 | ||||
| -rw-r--r-- | kernel/sys.c | 94 |
2 files changed, 98 insertions, 0 deletions
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 7f614ce274a9..a60943be4270 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h | |||
| @@ -35,6 +35,7 @@ struct oldold_utsname; | |||
| 35 | struct old_utsname; | 35 | struct old_utsname; |
| 36 | struct pollfd; | 36 | struct pollfd; |
| 37 | struct rlimit; | 37 | struct rlimit; |
| 38 | struct rlimit64; | ||
| 38 | struct rusage; | 39 | struct rusage; |
| 39 | struct sched_param; | 40 | struct sched_param; |
| 40 | struct sel_arg_struct; | 41 | struct sel_arg_struct; |
| @@ -644,6 +645,9 @@ asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit __user *r | |||
| 644 | #endif | 645 | #endif |
| 645 | asmlinkage long sys_setrlimit(unsigned int resource, | 646 | asmlinkage long sys_setrlimit(unsigned int resource, |
| 646 | struct rlimit __user *rlim); | 647 | struct rlimit __user *rlim); |
| 648 | asmlinkage long sys_prlimit64(pid_t pid, unsigned int resource, | ||
| 649 | const struct rlimit64 __user *new_rlim, | ||
| 650 | struct rlimit64 __user *old_rlim); | ||
| 647 | asmlinkage long sys_getrusage(int who, struct rusage __user *ru); | 651 | asmlinkage long sys_getrusage(int who, struct rusage __user *ru); |
| 648 | asmlinkage long sys_umask(int mask); | 652 | asmlinkage long sys_umask(int mask); |
| 649 | 653 | ||
diff --git a/kernel/sys.c b/kernel/sys.c index 9da98dd47276..e9ad44489828 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
| @@ -1271,6 +1271,39 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource, | |||
| 1271 | 1271 | ||
| 1272 | #endif | 1272 | #endif |
| 1273 | 1273 | ||
| 1274 | static inline bool rlim64_is_infinity(__u64 rlim64) | ||
| 1275 | { | ||
| 1276 | #if BITS_PER_LONG < 64 | ||
| 1277 | return rlim64 >= ULONG_MAX; | ||
| 1278 | #else | ||
| 1279 | return rlim64 == RLIM64_INFINITY; | ||
| 1280 | #endif | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | static void rlim_to_rlim64(const struct rlimit *rlim, struct rlimit64 *rlim64) | ||
| 1284 | { | ||
| 1285 | if (rlim->rlim_cur == RLIM_INFINITY) | ||
| 1286 | rlim64->rlim_cur = RLIM64_INFINITY; | ||
| 1287 | else | ||
| 1288 | rlim64->rlim_cur = rlim->rlim_cur; | ||
| 1289 | if (rlim->rlim_max == RLIM_INFINITY) | ||
| 1290 | rlim64->rlim_max = RLIM64_INFINITY; | ||
| 1291 | else | ||
| 1292 | rlim64->rlim_max = rlim->rlim_max; | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | static void rlim64_to_rlim(const struct rlimit64 *rlim64, struct rlimit *rlim) | ||
| 1296 | { | ||
| 1297 | if (rlim64_is_infinity(rlim64->rlim_cur)) | ||
| 1298 | rlim->rlim_cur = RLIM_INFINITY; | ||
| 1299 | else | ||
| 1300 | rlim->rlim_cur = (unsigned long)rlim64->rlim_cur; | ||
| 1301 | if (rlim64_is_infinity(rlim64->rlim_max)) | ||
| 1302 | rlim->rlim_max = RLIM_INFINITY; | ||
| 1303 | else | ||
| 1304 | rlim->rlim_max = (unsigned long)rlim64->rlim_max; | ||
| 1305 | } | ||
| 1306 | |||
| 1274 | /* make sure you are allowed to change @tsk limits before calling this */ | 1307 | /* make sure you are allowed to change @tsk limits before calling this */ |
| 1275 | int do_prlimit(struct task_struct *tsk, unsigned int resource, | 1308 | int do_prlimit(struct task_struct *tsk, unsigned int resource, |
| 1276 | struct rlimit *new_rlim, struct rlimit *old_rlim) | 1309 | struct rlimit *new_rlim, struct rlimit *old_rlim) |
| @@ -1336,6 +1369,67 @@ out: | |||
| 1336 | return retval; | 1369 | return retval; |
| 1337 | } | 1370 | } |
| 1338 | 1371 | ||
| 1372 | /* rcu lock must be held */ | ||
| 1373 | static int check_prlimit_permission(struct task_struct *task) | ||
| 1374 | { | ||
| 1375 | const struct cred *cred = current_cred(), *tcred; | ||
| 1376 | |||
| 1377 | tcred = __task_cred(task); | ||
| 1378 | if ((cred->uid != tcred->euid || | ||
| 1379 | cred->uid != tcred->suid || | ||
| 1380 | cred->uid != tcred->uid || | ||
| 1381 | cred->gid != tcred->egid || | ||
| 1382 | cred->gid != tcred->sgid || | ||
| 1383 | cred->gid != tcred->gid) && | ||
| 1384 | !capable(CAP_SYS_RESOURCE)) { | ||
| 1385 | return -EPERM; | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | return 0; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource, | ||
| 1392 | const struct rlimit64 __user *, new_rlim, | ||
| 1393 | struct rlimit64 __user *, old_rlim) | ||
| 1394 | { | ||
| 1395 | struct rlimit64 old64, new64; | ||
| 1396 | struct rlimit old, new; | ||
| 1397 | struct task_struct *tsk; | ||
| 1398 | int ret; | ||
| 1399 | |||
| 1400 | if (new_rlim) { | ||
| 1401 | if (copy_from_user(&new64, new_rlim, sizeof(new64))) | ||
| 1402 | return -EFAULT; | ||
| 1403 | rlim64_to_rlim(&new64, &new); | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | rcu_read_lock(); | ||
| 1407 | tsk = pid ? find_task_by_vpid(pid) : current; | ||
| 1408 | if (!tsk) { | ||
| 1409 | rcu_read_unlock(); | ||
| 1410 | return -ESRCH; | ||
| 1411 | } | ||
| 1412 | ret = check_prlimit_permission(tsk); | ||
| 1413 | if (ret) { | ||
| 1414 | rcu_read_unlock(); | ||
| 1415 | return ret; | ||
| 1416 | } | ||
| 1417 | get_task_struct(tsk); | ||
| 1418 | rcu_read_unlock(); | ||
| 1419 | |||
| 1420 | ret = do_prlimit(tsk, resource, new_rlim ? &new : NULL, | ||
| 1421 | old_rlim ? &old : NULL); | ||
| 1422 | |||
| 1423 | if (!ret && old_rlim) { | ||
| 1424 | rlim_to_rlim64(&old, &old64); | ||
| 1425 | if (copy_to_user(old_rlim, &old64, sizeof(old64))) | ||
| 1426 | ret = -EFAULT; | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | put_task_struct(tsk); | ||
| 1430 | return ret; | ||
| 1431 | } | ||
| 1432 | |||
| 1339 | SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) | 1433 | SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) |
| 1340 | { | 1434 | { |
| 1341 | struct rlimit new_rlim; | 1435 | struct rlimit new_rlim; |
