diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-10 15:07:51 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-10 15:07:51 -0400 |
commit | b34d8915c413acb51d837a45fb8747b61f65c020 (patch) | |
tree | ced5fac166324634653d84b1afe2b958b3904f4d /kernel | |
parent | e8a89cebdbaab14caaa26debdb4ffd493b8831af (diff) | |
parent | f33ebbe9da2c3c24664a0ad4f8fd83f293547e63 (diff) |
Merge branch 'writable_limits' of git://decibel.fi.muni.cz/~xslaby/linux
* 'writable_limits' of git://decibel.fi.muni.cz/~xslaby/linux:
unistd: add __NR_prlimit64 syscall numbers
rlimits: implement prlimit64 syscall
rlimits: switch more rlimit syscalls to do_prlimit
rlimits: redo do_setrlimit to more generic do_prlimit
rlimits: add rlimit64 structure
rlimits: do security check under task_lock
rlimits: allow setrlimit to non-current tasks
rlimits: split sys_setrlimit
rlimits: selinux, do rlimits changes under task_lock
rlimits: make sure ->rlim_max never grows in sys_setrlimit
rlimits: add task_struct to update_rlimit_cpu
rlimits: security, add task_struct to setrlimit
Fix up various system call number conflicts. We not only added fanotify
system calls in the meantime, but asm-generic/unistd.h added a wait4
along with a range of reserved per-architecture system calls.
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/compat.c | 17 | ||||
-rw-r--r-- | kernel/posix-cpu-timers.c | 8 | ||||
-rw-r--r-- | kernel/sys.c | 202 |
3 files changed, 165 insertions, 62 deletions
diff --git a/kernel/compat.c b/kernel/compat.c index 5adab05a3172..e167efce8423 100644 --- a/kernel/compat.c +++ b/kernel/compat.c | |||
@@ -279,11 +279,6 @@ asmlinkage long compat_sys_setrlimit(unsigned int resource, | |||
279 | struct compat_rlimit __user *rlim) | 279 | struct compat_rlimit __user *rlim) |
280 | { | 280 | { |
281 | struct rlimit r; | 281 | struct rlimit r; |
282 | int ret; | ||
283 | mm_segment_t old_fs = get_fs (); | ||
284 | |||
285 | if (resource >= RLIM_NLIMITS) | ||
286 | return -EINVAL; | ||
287 | 282 | ||
288 | if (!access_ok(VERIFY_READ, rlim, sizeof(*rlim)) || | 283 | if (!access_ok(VERIFY_READ, rlim, sizeof(*rlim)) || |
289 | __get_user(r.rlim_cur, &rlim->rlim_cur) || | 284 | __get_user(r.rlim_cur, &rlim->rlim_cur) || |
@@ -294,10 +289,7 @@ asmlinkage long compat_sys_setrlimit(unsigned int resource, | |||
294 | r.rlim_cur = RLIM_INFINITY; | 289 | r.rlim_cur = RLIM_INFINITY; |
295 | if (r.rlim_max == COMPAT_RLIM_INFINITY) | 290 | if (r.rlim_max == COMPAT_RLIM_INFINITY) |
296 | r.rlim_max = RLIM_INFINITY; | 291 | r.rlim_max = RLIM_INFINITY; |
297 | set_fs(KERNEL_DS); | 292 | return do_prlimit(current, resource, &r, NULL); |
298 | ret = sys_setrlimit(resource, (struct rlimit __user *) &r); | ||
299 | set_fs(old_fs); | ||
300 | return ret; | ||
301 | } | 293 | } |
302 | 294 | ||
303 | #ifdef COMPAT_RLIM_OLD_INFINITY | 295 | #ifdef COMPAT_RLIM_OLD_INFINITY |
@@ -329,16 +321,13 @@ asmlinkage long compat_sys_old_getrlimit(unsigned int resource, | |||
329 | 321 | ||
330 | #endif | 322 | #endif |
331 | 323 | ||
332 | asmlinkage long compat_sys_getrlimit (unsigned int resource, | 324 | asmlinkage long compat_sys_getrlimit(unsigned int resource, |
333 | struct compat_rlimit __user *rlim) | 325 | struct compat_rlimit __user *rlim) |
334 | { | 326 | { |
335 | struct rlimit r; | 327 | struct rlimit r; |
336 | int ret; | 328 | int ret; |
337 | mm_segment_t old_fs = get_fs(); | ||
338 | 329 | ||
339 | set_fs(KERNEL_DS); | 330 | ret = do_prlimit(current, resource, NULL, &r); |
340 | ret = sys_getrlimit(resource, (struct rlimit __user *) &r); | ||
341 | set_fs(old_fs); | ||
342 | if (!ret) { | 331 | if (!ret) { |
343 | if (r.rlim_cur > COMPAT_RLIM_INFINITY) | 332 | if (r.rlim_cur > COMPAT_RLIM_INFINITY) |
344 | r.rlim_cur = COMPAT_RLIM_INFINITY; | 333 | r.rlim_cur = COMPAT_RLIM_INFINITY; |
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index f66bdd33a6c6..6842eeba5879 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c | |||
@@ -16,13 +16,13 @@ | |||
16 | * siglock protection since other code may update expiration cache as | 16 | * siglock protection since other code may update expiration cache as |
17 | * well. | 17 | * well. |
18 | */ | 18 | */ |
19 | void update_rlimit_cpu(unsigned long rlim_new) | 19 | void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new) |
20 | { | 20 | { |
21 | cputime_t cputime = secs_to_cputime(rlim_new); | 21 | cputime_t cputime = secs_to_cputime(rlim_new); |
22 | 22 | ||
23 | spin_lock_irq(¤t->sighand->siglock); | 23 | spin_lock_irq(&task->sighand->siglock); |
24 | set_process_cpu_timer(current, CPUCLOCK_PROF, &cputime, NULL); | 24 | set_process_cpu_timer(task, CPUCLOCK_PROF, &cputime, NULL); |
25 | spin_unlock_irq(¤t->sighand->siglock); | 25 | spin_unlock_irq(&task->sighand->siglock); |
26 | } | 26 | } |
27 | 27 | ||
28 | static int check_clock(const clockid_t which_clock) | 28 | static int check_clock(const clockid_t which_clock) |
diff --git a/kernel/sys.c b/kernel/sys.c index e83ddbbaf89d..e9ad44489828 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -1236,15 +1236,14 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len) | |||
1236 | 1236 | ||
1237 | SYSCALL_DEFINE2(getrlimit, unsigned int, resource, struct rlimit __user *, rlim) | 1237 | SYSCALL_DEFINE2(getrlimit, unsigned int, resource, struct rlimit __user *, rlim) |
1238 | { | 1238 | { |
1239 | if (resource >= RLIM_NLIMITS) | 1239 | struct rlimit value; |
1240 | return -EINVAL; | 1240 | int ret; |
1241 | else { | 1241 | |
1242 | struct rlimit value; | 1242 | ret = do_prlimit(current, resource, NULL, &value); |
1243 | task_lock(current->group_leader); | 1243 | if (!ret) |
1244 | value = current->signal->rlim[resource]; | 1244 | ret = copy_to_user(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0; |
1245 | task_unlock(current->group_leader); | 1245 | |
1246 | return copy_to_user(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0; | 1246 | return ret; |
1247 | } | ||
1248 | } | 1247 | } |
1249 | 1248 | ||
1250 | #ifdef __ARCH_WANT_SYS_OLD_GETRLIMIT | 1249 | #ifdef __ARCH_WANT_SYS_OLD_GETRLIMIT |
@@ -1272,44 +1271,89 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource, | |||
1272 | 1271 | ||
1273 | #endif | 1272 | #endif |
1274 | 1273 | ||
1275 | SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) | 1274 | static inline bool rlim64_is_infinity(__u64 rlim64) |
1276 | { | 1275 | { |
1277 | struct rlimit new_rlim, *old_rlim; | 1276 | #if BITS_PER_LONG < 64 |
1278 | int retval; | 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 | |||
1307 | /* make sure you are allowed to change @tsk limits before calling this */ | ||
1308 | int do_prlimit(struct task_struct *tsk, unsigned int resource, | ||
1309 | struct rlimit *new_rlim, struct rlimit *old_rlim) | ||
1310 | { | ||
1311 | struct rlimit *rlim; | ||
1312 | int retval = 0; | ||
1279 | 1313 | ||
1280 | if (resource >= RLIM_NLIMITS) | 1314 | if (resource >= RLIM_NLIMITS) |
1281 | return -EINVAL; | 1315 | return -EINVAL; |
1282 | if (copy_from_user(&new_rlim, rlim, sizeof(*rlim))) | 1316 | if (new_rlim) { |
1283 | return -EFAULT; | 1317 | if (new_rlim->rlim_cur > new_rlim->rlim_max) |
1284 | if (new_rlim.rlim_cur > new_rlim.rlim_max) | 1318 | return -EINVAL; |
1285 | return -EINVAL; | 1319 | if (resource == RLIMIT_NOFILE && |
1286 | old_rlim = current->signal->rlim + resource; | 1320 | new_rlim->rlim_max > sysctl_nr_open) |
1287 | if ((new_rlim.rlim_max > old_rlim->rlim_max) && | 1321 | return -EPERM; |
1288 | !capable(CAP_SYS_RESOURCE)) | ||
1289 | return -EPERM; | ||
1290 | if (resource == RLIMIT_NOFILE && new_rlim.rlim_max > sysctl_nr_open) | ||
1291 | return -EPERM; | ||
1292 | |||
1293 | retval = security_task_setrlimit(resource, &new_rlim); | ||
1294 | if (retval) | ||
1295 | return retval; | ||
1296 | |||
1297 | if (resource == RLIMIT_CPU && new_rlim.rlim_cur == 0) { | ||
1298 | /* | ||
1299 | * The caller is asking for an immediate RLIMIT_CPU | ||
1300 | * expiry. But we use the zero value to mean "it was | ||
1301 | * never set". So let's cheat and make it one second | ||
1302 | * instead | ||
1303 | */ | ||
1304 | new_rlim.rlim_cur = 1; | ||
1305 | } | 1322 | } |
1306 | 1323 | ||
1307 | task_lock(current->group_leader); | 1324 | /* protect tsk->signal and tsk->sighand from disappearing */ |
1308 | *old_rlim = new_rlim; | 1325 | read_lock(&tasklist_lock); |
1309 | task_unlock(current->group_leader); | 1326 | if (!tsk->sighand) { |
1310 | 1327 | retval = -ESRCH; | |
1311 | if (resource != RLIMIT_CPU) | ||
1312 | goto out; | 1328 | goto out; |
1329 | } | ||
1330 | |||
1331 | rlim = tsk->signal->rlim + resource; | ||
1332 | task_lock(tsk->group_leader); | ||
1333 | if (new_rlim) { | ||
1334 | if (new_rlim->rlim_max > rlim->rlim_max && | ||
1335 | !capable(CAP_SYS_RESOURCE)) | ||
1336 | retval = -EPERM; | ||
1337 | if (!retval) | ||
1338 | retval = security_task_setrlimit(tsk->group_leader, | ||
1339 | resource, new_rlim); | ||
1340 | if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) { | ||
1341 | /* | ||
1342 | * The caller is asking for an immediate RLIMIT_CPU | ||
1343 | * expiry. But we use the zero value to mean "it was | ||
1344 | * never set". So let's cheat and make it one second | ||
1345 | * instead | ||
1346 | */ | ||
1347 | new_rlim->rlim_cur = 1; | ||
1348 | } | ||
1349 | } | ||
1350 | if (!retval) { | ||
1351 | if (old_rlim) | ||
1352 | *old_rlim = *rlim; | ||
1353 | if (new_rlim) | ||
1354 | *rlim = *new_rlim; | ||
1355 | } | ||
1356 | task_unlock(tsk->group_leader); | ||
1313 | 1357 | ||
1314 | /* | 1358 | /* |
1315 | * RLIMIT_CPU handling. Note that the kernel fails to return an error | 1359 | * RLIMIT_CPU handling. Note that the kernel fails to return an error |
@@ -1317,14 +1361,84 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) | |||
1317 | * very long-standing error, and fixing it now risks breakage of | 1361 | * very long-standing error, and fixing it now risks breakage of |
1318 | * applications, so we live with it | 1362 | * applications, so we live with it |
1319 | */ | 1363 | */ |
1320 | if (new_rlim.rlim_cur == RLIM_INFINITY) | 1364 | if (!retval && new_rlim && resource == RLIMIT_CPU && |
1321 | goto out; | 1365 | new_rlim->rlim_cur != RLIM_INFINITY) |
1322 | 1366 | update_rlimit_cpu(tsk, new_rlim->rlim_cur); | |
1323 | update_rlimit_cpu(new_rlim.rlim_cur); | ||
1324 | out: | 1367 | out: |
1368 | read_unlock(&tasklist_lock); | ||
1369 | return retval; | ||
1370 | } | ||
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 | |||
1325 | return 0; | 1388 | return 0; |
1326 | } | 1389 | } |
1327 | 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 | |||
1433 | SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) | ||
1434 | { | ||
1435 | struct rlimit new_rlim; | ||
1436 | |||
1437 | if (copy_from_user(&new_rlim, rlim, sizeof(*rlim))) | ||
1438 | return -EFAULT; | ||
1439 | return do_prlimit(current, resource, &new_rlim, NULL); | ||
1440 | } | ||
1441 | |||
1328 | /* | 1442 | /* |
1329 | * It would make sense to put struct rusage in the task_struct, | 1443 | * It would make sense to put struct rusage in the task_struct, |
1330 | * except that would make the task_struct be *really big*. After | 1444 | * except that would make the task_struct be *really big*. After |