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 | |
| 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.
| -rw-r--r-- | arch/x86/ia32/ia32entry.S | 1 | ||||
| -rw-r--r-- | arch/x86/include/asm/unistd_32.h | 3 | ||||
| -rw-r--r-- | arch/x86/include/asm/unistd_64.h | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/syscall_table_32.S | 1 | ||||
| -rw-r--r-- | include/asm-generic/unistd.h | 4 | ||||
| -rw-r--r-- | include/linux/posix-timers.h | 2 | ||||
| -rw-r--r-- | include/linux/resource.h | 9 | ||||
| -rw-r--r-- | include/linux/security.h | 9 | ||||
| -rw-r--r-- | include/linux/syscalls.h | 4 | ||||
| -rw-r--r-- | kernel/compat.c | 17 | ||||
| -rw-r--r-- | kernel/posix-cpu-timers.c | 8 | ||||
| -rw-r--r-- | kernel/sys.c | 202 | ||||
| -rw-r--r-- | security/capability.c | 3 | ||||
| -rw-r--r-- | security/security.c | 5 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 12 |
15 files changed, 207 insertions, 75 deletions
diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 17cf65c94804..91dc4bb13032 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S | |||
| @@ -844,4 +844,5 @@ ia32_sys_call_table: | |||
| 844 | .quad compat_sys_recvmmsg | 844 | .quad compat_sys_recvmmsg |
| 845 | .quad sys_fanotify_init | 845 | .quad sys_fanotify_init |
| 846 | .quad sys32_fanotify_mark | 846 | .quad sys32_fanotify_mark |
| 847 | .quad sys_prlimit64 | ||
| 847 | ia32_syscall_end: | 848 | ia32_syscall_end: |
diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h index 80b799cd74f7..b766a5e8ba0e 100644 --- a/arch/x86/include/asm/unistd_32.h +++ b/arch/x86/include/asm/unistd_32.h | |||
| @@ -345,10 +345,11 @@ | |||
| 345 | #define __NR_recvmmsg 337 | 345 | #define __NR_recvmmsg 337 |
| 346 | #define __NR_fanotify_init 338 | 346 | #define __NR_fanotify_init 338 |
| 347 | #define __NR_fanotify_mark 339 | 347 | #define __NR_fanotify_mark 339 |
| 348 | #define __NR_prlimit64 340 | ||
| 348 | 349 | ||
| 349 | #ifdef __KERNEL__ | 350 | #ifdef __KERNEL__ |
| 350 | 351 | ||
| 351 | #define NR_syscalls 340 | 352 | #define NR_syscalls 341 |
| 352 | 353 | ||
| 353 | #define __ARCH_WANT_IPC_PARSE_VERSION | 354 | #define __ARCH_WANT_IPC_PARSE_VERSION |
| 354 | #define __ARCH_WANT_OLD_READDIR | 355 | #define __ARCH_WANT_OLD_READDIR |
diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h index 5b7b1d585616..363e9b8a715b 100644 --- a/arch/x86/include/asm/unistd_64.h +++ b/arch/x86/include/asm/unistd_64.h | |||
| @@ -667,6 +667,8 @@ __SYSCALL(__NR_recvmmsg, sys_recvmmsg) | |||
| 667 | __SYSCALL(__NR_fanotify_init, sys_fanotify_init) | 667 | __SYSCALL(__NR_fanotify_init, sys_fanotify_init) |
| 668 | #define __NR_fanotify_mark 301 | 668 | #define __NR_fanotify_mark 301 |
| 669 | __SYSCALL(__NR_fanotify_mark, sys_fanotify_mark) | 669 | __SYSCALL(__NR_fanotify_mark, sys_fanotify_mark) |
| 670 | #define __NR_prlimit64 302 | ||
| 671 | __SYSCALL(__NR_prlimit64, sys_prlimit64) | ||
| 670 | 672 | ||
| 671 | #ifndef __NO_STUBS | 673 | #ifndef __NO_STUBS |
| 672 | #define __ARCH_WANT_OLD_READDIR | 674 | #define __ARCH_WANT_OLD_READDIR |
diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index 07ad5eb7cc5c..4802accb9d8d 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S | |||
| @@ -339,3 +339,4 @@ ENTRY(sys_call_table) | |||
| 339 | .long sys_recvmmsg | 339 | .long sys_recvmmsg |
| 340 | .long sys_fanotify_init | 340 | .long sys_fanotify_init |
| 341 | .long sys_fanotify_mark | 341 | .long sys_fanotify_mark |
| 342 | .long sys_prlimit64 | ||
diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h index c17cebc49952..e1898090f22c 100644 --- a/include/asm-generic/unistd.h +++ b/include/asm-generic/unistd.h | |||
| @@ -640,9 +640,11 @@ __SYSCALL(__NR_recvmmsg, sys_recvmmsg) | |||
| 640 | 640 | ||
| 641 | #define __NR_wait4 260 | 641 | #define __NR_wait4 260 |
| 642 | __SYSCALL(__NR_wait4, sys_wait4) | 642 | __SYSCALL(__NR_wait4, sys_wait4) |
| 643 | #define __NR_prlimit64 261 | ||
| 644 | __SYSCALL(__NR_prlimit64, sys_prlimit64) | ||
| 643 | 645 | ||
| 644 | #undef __NR_syscalls | 646 | #undef __NR_syscalls |
| 645 | #define __NR_syscalls 261 | 647 | #define __NR_syscalls 262 |
| 646 | 648 | ||
| 647 | /* | 649 | /* |
| 648 | * All syscalls below here should go away really, | 650 | * All syscalls below here should go away really, |
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 4f71bf4e628c..3e23844a6990 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h | |||
| @@ -117,6 +117,6 @@ void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx, | |||
| 117 | 117 | ||
| 118 | long clock_nanosleep_restart(struct restart_block *restart_block); | 118 | long clock_nanosleep_restart(struct restart_block *restart_block); |
| 119 | 119 | ||
| 120 | void update_rlimit_cpu(unsigned long rlim_new); | 120 | void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new); |
| 121 | 121 | ||
| 122 | #endif | 122 | #endif |
diff --git a/include/linux/resource.h b/include/linux/resource.h index f1e914eefeab..88d36f9145ba 100644 --- a/include/linux/resource.h +++ b/include/linux/resource.h | |||
| @@ -43,6 +43,13 @@ struct rlimit { | |||
| 43 | unsigned long rlim_max; | 43 | unsigned long rlim_max; |
| 44 | }; | 44 | }; |
| 45 | 45 | ||
| 46 | #define RLIM64_INFINITY (~0ULL) | ||
| 47 | |||
| 48 | struct rlimit64 { | ||
| 49 | __u64 rlim_cur; | ||
| 50 | __u64 rlim_max; | ||
| 51 | }; | ||
| 52 | |||
| 46 | #define PRIO_MIN (-20) | 53 | #define PRIO_MIN (-20) |
| 47 | #define PRIO_MAX 20 | 54 | #define PRIO_MAX 20 |
| 48 | 55 | ||
| @@ -73,6 +80,8 @@ struct rlimit { | |||
| 73 | struct task_struct; | 80 | struct task_struct; |
| 74 | 81 | ||
| 75 | int getrusage(struct task_struct *p, int who, struct rusage __user *ru); | 82 | int getrusage(struct task_struct *p, int who, struct rusage __user *ru); |
| 83 | int do_prlimit(struct task_struct *tsk, unsigned int resource, | ||
| 84 | struct rlimit *new_rlim, struct rlimit *old_rlim); | ||
| 76 | 85 | ||
| 77 | #endif /* __KERNEL__ */ | 86 | #endif /* __KERNEL__ */ |
| 78 | 87 | ||
diff --git a/include/linux/security.h b/include/linux/security.h index 5bcb395a49d4..a22219afff09 100644 --- a/include/linux/security.h +++ b/include/linux/security.h | |||
| @@ -1499,7 +1499,8 @@ struct security_operations { | |||
| 1499 | int (*task_setnice) (struct task_struct *p, int nice); | 1499 | int (*task_setnice) (struct task_struct *p, int nice); |
| 1500 | int (*task_setioprio) (struct task_struct *p, int ioprio); | 1500 | int (*task_setioprio) (struct task_struct *p, int ioprio); |
| 1501 | int (*task_getioprio) (struct task_struct *p); | 1501 | int (*task_getioprio) (struct task_struct *p); |
| 1502 | int (*task_setrlimit) (unsigned int resource, struct rlimit *new_rlim); | 1502 | int (*task_setrlimit) (struct task_struct *p, unsigned int resource, |
| 1503 | struct rlimit *new_rlim); | ||
| 1503 | int (*task_setscheduler) (struct task_struct *p, int policy, | 1504 | int (*task_setscheduler) (struct task_struct *p, int policy, |
| 1504 | struct sched_param *lp); | 1505 | struct sched_param *lp); |
| 1505 | int (*task_getscheduler) (struct task_struct *p); | 1506 | int (*task_getscheduler) (struct task_struct *p); |
| @@ -1749,7 +1750,8 @@ void security_task_getsecid(struct task_struct *p, u32 *secid); | |||
| 1749 | int security_task_setnice(struct task_struct *p, int nice); | 1750 | int security_task_setnice(struct task_struct *p, int nice); |
| 1750 | int security_task_setioprio(struct task_struct *p, int ioprio); | 1751 | int security_task_setioprio(struct task_struct *p, int ioprio); |
| 1751 | int security_task_getioprio(struct task_struct *p); | 1752 | int security_task_getioprio(struct task_struct *p); |
| 1752 | int security_task_setrlimit(unsigned int resource, struct rlimit *new_rlim); | 1753 | int security_task_setrlimit(struct task_struct *p, unsigned int resource, |
| 1754 | struct rlimit *new_rlim); | ||
| 1753 | int security_task_setscheduler(struct task_struct *p, | 1755 | int security_task_setscheduler(struct task_struct *p, |
| 1754 | int policy, struct sched_param *lp); | 1756 | int policy, struct sched_param *lp); |
| 1755 | int security_task_getscheduler(struct task_struct *p); | 1757 | int security_task_getscheduler(struct task_struct *p); |
| @@ -2311,7 +2313,8 @@ static inline int security_task_getioprio(struct task_struct *p) | |||
| 2311 | return 0; | 2313 | return 0; |
| 2312 | } | 2314 | } |
| 2313 | 2315 | ||
| 2314 | static inline int security_task_setrlimit(unsigned int resource, | 2316 | static inline int security_task_setrlimit(struct task_struct *p, |
| 2317 | unsigned int resource, | ||
| 2315 | struct rlimit *new_rlim) | 2318 | struct rlimit *new_rlim) |
| 2316 | { | 2319 | { |
| 2317 | return 0; | 2320 | return 0; |
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 2ab198a1e38d..1b67bd333b5e 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/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 |
diff --git a/security/capability.c b/security/capability.c index a0bbf30fb6dc..95a6599a37bb 100644 --- a/security/capability.c +++ b/security/capability.c | |||
| @@ -411,7 +411,8 @@ static int cap_task_getioprio(struct task_struct *p) | |||
| 411 | return 0; | 411 | return 0; |
| 412 | } | 412 | } |
| 413 | 413 | ||
| 414 | static int cap_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) | 414 | static int cap_task_setrlimit(struct task_struct *p, unsigned int resource, |
| 415 | struct rlimit *new_rlim) | ||
| 415 | { | 416 | { |
| 416 | return 0; | 417 | return 0; |
| 417 | } | 418 | } |
diff --git a/security/security.c b/security/security.c index 7461b1bc296c..c53949f17d9e 100644 --- a/security/security.c +++ b/security/security.c | |||
| @@ -780,9 +780,10 @@ int security_task_getioprio(struct task_struct *p) | |||
| 780 | return security_ops->task_getioprio(p); | 780 | return security_ops->task_getioprio(p); |
| 781 | } | 781 | } |
| 782 | 782 | ||
| 783 | int security_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) | 783 | int security_task_setrlimit(struct task_struct *p, unsigned int resource, |
| 784 | struct rlimit *new_rlim) | ||
| 784 | { | 785 | { |
| 785 | return security_ops->task_setrlimit(resource, new_rlim); | 786 | return security_ops->task_setrlimit(p, resource, new_rlim); |
| 786 | } | 787 | } |
| 787 | 788 | ||
| 788 | int security_task_setscheduler(struct task_struct *p, | 789 | int security_task_setscheduler(struct task_struct *p, |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9b40f4c0ac70..42043f96e54f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -2284,12 +2284,15 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm) | |||
| 2284 | rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS, | 2284 | rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS, |
| 2285 | PROCESS__RLIMITINH, NULL); | 2285 | PROCESS__RLIMITINH, NULL); |
| 2286 | if (rc) { | 2286 | if (rc) { |
| 2287 | /* protect against do_prlimit() */ | ||
| 2288 | task_lock(current); | ||
| 2287 | for (i = 0; i < RLIM_NLIMITS; i++) { | 2289 | for (i = 0; i < RLIM_NLIMITS; i++) { |
| 2288 | rlim = current->signal->rlim + i; | 2290 | rlim = current->signal->rlim + i; |
| 2289 | initrlim = init_task.signal->rlim + i; | 2291 | initrlim = init_task.signal->rlim + i; |
| 2290 | rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur); | 2292 | rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur); |
| 2291 | } | 2293 | } |
| 2292 | update_rlimit_cpu(current->signal->rlim[RLIMIT_CPU].rlim_cur); | 2294 | task_unlock(current); |
| 2295 | update_rlimit_cpu(current, rlimit(RLIMIT_CPU)); | ||
| 2293 | } | 2296 | } |
| 2294 | } | 2297 | } |
| 2295 | 2298 | ||
| @@ -3333,16 +3336,17 @@ static int selinux_task_getioprio(struct task_struct *p) | |||
| 3333 | return current_has_perm(p, PROCESS__GETSCHED); | 3336 | return current_has_perm(p, PROCESS__GETSCHED); |
| 3334 | } | 3337 | } |
| 3335 | 3338 | ||
| 3336 | static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) | 3339 | static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, |
| 3340 | struct rlimit *new_rlim) | ||
| 3337 | { | 3341 | { |
| 3338 | struct rlimit *old_rlim = current->signal->rlim + resource; | 3342 | struct rlimit *old_rlim = p->signal->rlim + resource; |
| 3339 | 3343 | ||
| 3340 | /* Control the ability to change the hard limit (whether | 3344 | /* Control the ability to change the hard limit (whether |
| 3341 | lowering or raising it), so that the hard limit can | 3345 | lowering or raising it), so that the hard limit can |
| 3342 | later be used as a safe reset point for the soft limit | 3346 | later be used as a safe reset point for the soft limit |
| 3343 | upon context transitions. See selinux_bprm_committing_creds. */ | 3347 | upon context transitions. See selinux_bprm_committing_creds. */ |
| 3344 | if (old_rlim->rlim_max != new_rlim->rlim_max) | 3348 | if (old_rlim->rlim_max != new_rlim->rlim_max) |
| 3345 | return current_has_perm(current, PROCESS__SETRLIMIT); | 3349 | return current_has_perm(p, PROCESS__SETRLIMIT); |
| 3346 | 3350 | ||
| 3347 | return 0; | 3351 | return 0; |
| 3348 | } | 3352 | } |
