aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/syscalls.h4
-rw-r--r--kernel/sys.c94
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;
35struct old_utsname; 35struct old_utsname;
36struct pollfd; 36struct pollfd;
37struct rlimit; 37struct rlimit;
38struct rlimit64;
38struct rusage; 39struct rusage;
39struct sched_param; 40struct sched_param;
40struct sel_arg_struct; 41struct sel_arg_struct;
@@ -644,6 +645,9 @@ asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit __user *r
644#endif 645#endif
645asmlinkage long sys_setrlimit(unsigned int resource, 646asmlinkage long sys_setrlimit(unsigned int resource,
646 struct rlimit __user *rlim); 647 struct rlimit __user *rlim);
648asmlinkage long sys_prlimit64(pid_t pid, unsigned int resource,
649 const struct rlimit64 __user *new_rlim,
650 struct rlimit64 __user *old_rlim);
647asmlinkage long sys_getrusage(int who, struct rusage __user *ru); 651asmlinkage long sys_getrusage(int who, struct rusage __user *ru);
648asmlinkage long sys_umask(int mask); 652asmlinkage 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
1274static 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
1283static 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
1295static 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 */
1275int do_prlimit(struct task_struct *tsk, unsigned int resource, 1308int 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 */
1373static 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
1391SYSCALL_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
1339SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) 1433SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim)
1340{ 1434{
1341 struct rlimit new_rlim; 1435 struct rlimit new_rlim;