X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=kernel%2Fsys.c;h=f91e7c447373bb31efef5262e156755c57fc0074;hb=bdeeeef70891d1ba02473622925d94cea9cb9560;hp=2ce1f171783c4a6e20cb85fa276a4b48b466a6ba;hpb=ec9397bab20a628530ce3051167d3d0fcc2c1af7;p=linux-2.6.git diff --git a/kernel/sys.c b/kernel/sys.c index 2ce1f1717..f91e7c447 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -5,7 +5,6 @@ */ #include -#include #include #include #include @@ -21,15 +20,24 @@ #include #include #include +#include #include +#include #include +#include #include #include #include -#include +#include +#include +#include #include #include +#include +#include +#include + #include #include #include @@ -91,7 +99,7 @@ int cad_pid = 1; */ static struct notifier_block *reboot_notifier_list; -rwlock_t notifier_lock = RW_LOCK_UNLOCKED; +static DEFINE_RWLOCK(notifier_lock); /** * notifier_chain_register - Add notifier to a notifier chain @@ -165,7 +173,7 @@ EXPORT_SYMBOL(notifier_chain_unregister); * of the last notifier function called. */ -int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v) +int __kprobes notifier_call_chain(struct notifier_block **n, unsigned long val, void *v) { int ret=NOTIFY_DONE; struct notifier_block *nb = *n; @@ -219,87 +227,30 @@ int unregister_reboot_notifier(struct notifier_block * nb) EXPORT_SYMBOL(unregister_reboot_notifier); -asmlinkage long sys_ni_syscall(void) +#ifndef CONFIG_SECURITY +int capable(int cap) { - return -ENOSYS; + if (vx_check_bit(VXC_CAP_MASK, cap) && !vx_mcaps(1L << cap)) + return 0; + if (cap_raised(current->cap_effective, cap)) { + current->flags |= PF_SUPERPRIV; + return 1; + } + return 0; } - -cond_syscall(sys_nfsservctl) -cond_syscall(sys_quotactl) -cond_syscall(sys_acct) -cond_syscall(sys_lookup_dcookie) -cond_syscall(sys_swapon) -cond_syscall(sys_swapoff) -cond_syscall(sys_kexec_load) -cond_syscall(sys_init_module) -cond_syscall(sys_delete_module) -cond_syscall(sys_socketpair) -cond_syscall(sys_bind) -cond_syscall(sys_listen) -cond_syscall(sys_accept) -cond_syscall(sys_connect) -cond_syscall(sys_getsockname) -cond_syscall(sys_getpeername) -cond_syscall(sys_sendto) -cond_syscall(sys_send) -cond_syscall(sys_recvfrom) -cond_syscall(sys_recv) -cond_syscall(sys_socket) -cond_syscall(sys_setsockopt) -cond_syscall(sys_getsockopt) -cond_syscall(sys_shutdown) -cond_syscall(sys_sendmsg) -cond_syscall(sys_recvmsg) -cond_syscall(sys_socketcall) -cond_syscall(sys_futex) -cond_syscall(compat_sys_futex) -cond_syscall(sys_epoll_create) -cond_syscall(sys_epoll_ctl) -cond_syscall(sys_epoll_wait) -cond_syscall(sys_semget) -cond_syscall(sys_semop) -cond_syscall(sys_semtimedop) -cond_syscall(sys_semctl) -cond_syscall(sys_msgget) -cond_syscall(sys_msgsnd) -cond_syscall(sys_msgrcv) -cond_syscall(sys_msgctl) -cond_syscall(sys_shmget) -cond_syscall(sys_shmdt) -cond_syscall(sys_shmctl) -cond_syscall(sys_mq_open) -cond_syscall(sys_mq_unlink) -cond_syscall(sys_mq_timedsend) -cond_syscall(sys_mq_timedreceive) -cond_syscall(sys_mq_notify) -cond_syscall(sys_mq_getsetattr) -cond_syscall(compat_sys_mq_open) -cond_syscall(compat_sys_mq_timedsend) -cond_syscall(compat_sys_mq_timedreceive) -cond_syscall(compat_sys_mq_notify) -cond_syscall(compat_sys_mq_getsetattr) -cond_syscall(sys_mbind) -cond_syscall(sys_get_mempolicy) -cond_syscall(sys_set_mempolicy) -cond_syscall(compat_mbind) -cond_syscall(compat_get_mempolicy) -cond_syscall(compat_set_mempolicy) - -/* arch-specific weak syscall entries */ -cond_syscall(sys_pciconfig_read) -cond_syscall(sys_pciconfig_write) -cond_syscall(sys_pciconfig_iobase) +EXPORT_SYMBOL(capable); +#endif static int set_one_prio(struct task_struct *p, int niceval, int error) { int no_nice; if (p->uid != current->euid && - p->uid != current->uid && !capable(CAP_SYS_NICE)) { + p->euid != current->euid && !capable(CAP_SYS_NICE)) { error = -EPERM; goto out; } - if (niceval < task_nice(p) && !capable(CAP_SYS_NICE)) { + if (niceval < task_nice(p) && !can_nice(p, niceval)) { if (vx_flags(VXF_IGNEG_NICE, 0)) error = 0; else @@ -351,19 +302,19 @@ asmlinkage long sys_setpriority(int which, int who, int niceval) } while_each_task_pid(who, PIDTYPE_PGID, p); break; case PRIO_USER: + user = current->user; if (!who) - user = current->user; + who = current->uid; else - user = find_user(vx_current_xid(), who); - - if (!user) - goto out_unlock; + if ((who != current->uid) && + !(user = find_user(vx_current_xid(), who))) + goto out_unlock; /* No processes for this user */ do_each_thread(g, p) if (p->uid == who) error = set_one_prio(p, niceval, error); while_each_thread(g, p); - if (who) + if (who != current->uid) free_uid(user); /* For find_user() */ break; } @@ -410,13 +361,13 @@ asmlinkage long sys_getpriority(int which, int who) } while_each_task_pid(who, PIDTYPE_PGID, p); break; case PRIO_USER: + user = current->user; if (!who) - user = current->user; + who = current->uid; else - user = find_user(vx_current_xid(), who); - - if (!user) - goto out_unlock; + if ((who != current->uid) && + !(user = find_user(vx_current_xid(), who))) + goto out_unlock; /* No processes for this user */ do_each_thread(g, p) if (p->uid == who) { @@ -425,7 +376,7 @@ asmlinkage long sys_getpriority(int which, int who) retval = niceval; } while_each_thread(g, p); - if (who) + if (who != current->uid) free_uid(user); /* for find_user() */ break; } @@ -435,7 +386,105 @@ out_unlock: return retval; } -long vs_reboot(unsigned int, void *); +/** + * emergency_restart - reboot the system + * + * Without shutting down any hardware or taking any locks + * reboot the system. This is called when we know we are in + * trouble so this is our best effort to reboot. This is + * safe to call in interrupt context. + */ +void emergency_restart(void) +{ + machine_emergency_restart(); +} +EXPORT_SYMBOL_GPL(emergency_restart); + +void kernel_restart_prepare(char *cmd) +{ + notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); + system_state = SYSTEM_RESTART; + device_shutdown(); +} + +/** + * kernel_restart - reboot the system + * @cmd: pointer to buffer containing command to execute for restart + * or %NULL + * + * Shutdown everything and perform a clean reboot. + * This is not safe to call in interrupt context. + */ +void kernel_restart(char *cmd) +{ + kernel_restart_prepare(cmd); + if (!cmd) { + printk(KERN_EMERG "Restarting system.\n"); + } else { + printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd); + } + printk(".\n"); + machine_restart(cmd); +} +EXPORT_SYMBOL_GPL(kernel_restart); + +/** + * kernel_kexec - reboot the system + * + * Move into place and start executing a preloaded standalone + * executable. If nothing was preloaded return an error. + */ +void kernel_kexec(void) +{ +#ifdef CONFIG_KEXEC + struct kimage *image; + image = xchg(&kexec_image, NULL); + if (!image) { + return; + } + kernel_restart_prepare(NULL); + printk(KERN_EMERG "Starting new kernel\n"); + machine_shutdown(); + machine_kexec(image); +#endif +} +EXPORT_SYMBOL_GPL(kernel_kexec); + +void kernel_shutdown_prepare(enum system_states state) +{ + notifier_call_chain(&reboot_notifier_list, + (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL); + system_state = state; + device_shutdown(); +} +/** + * kernel_halt - halt the system + * + * Shutdown everything and perform a clean system halt. + */ +void kernel_halt(void) +{ + kernel_shutdown_prepare(SYSTEM_HALT); + printk(KERN_EMERG "System halted.\n"); + machine_halt(); +} + +EXPORT_SYMBOL_GPL(kernel_halt); + +/** + * kernel_power_off - power_off the system + * + * Shutdown everything and perform a clean system power_off. + */ +void kernel_power_off(void) +{ + kernel_shutdown_prepare(SYSTEM_POWER_OFF); + printk(KERN_EMERG "Power down.\n"); + machine_power_off(); +} +EXPORT_SYMBOL_GPL(kernel_power_off); + +long vs_reboot(unsigned int, void __user *); /* * Reboot system call: for obvious reasons only root may call it, @@ -461,17 +510,19 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user magic2 != LINUX_REBOOT_MAGIC2C)) return -EINVAL; + /* Instead of trying to make the power_off code look like + * halt when pm_power_off is not set do it the easy way. + */ + if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off) + cmd = LINUX_REBOOT_CMD_HALT; + if (!vx_check(0, VX_ADMIN|VX_WATCH)) return vs_reboot(cmd, arg); lock_kernel(); switch (cmd) { case LINUX_REBOOT_CMD_RESTART: - notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); - system_state = SYSTEM_RESTART; - device_shutdown(); - printk(KERN_EMERG "Restarting system.\n"); - machine_restart(NULL); + kernel_restart(NULL); break; case LINUX_REBOOT_CMD_CAD_ON: @@ -483,21 +534,13 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user break; case LINUX_REBOOT_CMD_HALT: - notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL); - system_state = SYSTEM_HALT; - device_shutdown(); - printk(KERN_EMERG "System halted.\n"); - machine_halt(); + kernel_halt(); unlock_kernel(); do_exit(0); break; case LINUX_REBOOT_CMD_POWER_OFF: - notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL); - system_state = SYSTEM_POWER_OFF; - device_shutdown(); - printk(KERN_EMERG "Power down.\n"); - machine_power_off(); + kernel_power_off(); unlock_kernel(); do_exit(0); break; @@ -509,32 +552,14 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user } buffer[sizeof(buffer) - 1] = '\0'; - notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer); - system_state = SYSTEM_RESTART; - device_shutdown(); - printk(KERN_EMERG "Restarting system with command '%s'.\n", buffer); - machine_restart(buffer); + kernel_restart(buffer); break; -#ifdef CONFIG_KEXEC case LINUX_REBOOT_CMD_KEXEC: - { - struct kimage *image; - image = xchg(&kexec_image, 0); - if (!image) { - unlock_kernel(); - return -EINVAL; - } - notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); - system_state = SYSTEM_RESTART; - device_shutdown(); - system_state = SYSTEM_BOOTING; - printk(KERN_EMERG "Starting new kernel\n"); - machine_shutdown(); - machine_kexec(image); - break; - } -#endif + kernel_kexec(); + unlock_kernel(); + return -EINVAL; + #ifdef CONFIG_SOFTWARE_SUSPEND case LINUX_REBOOT_CMD_SW_SUSPEND: { @@ -554,8 +579,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user static void deferred_cad(void *dummy) { - notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); - machine_restart(NULL); + kernel_restart(NULL); } /* @@ -625,7 +649,7 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid) if (new_egid != old_egid) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } if (rgid != (gid_t) -1 || (egid != (gid_t) -1 && egid != old_rgid)) @@ -633,9 +657,8 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid) current->fsgid = new_egid; current->egid = new_egid; current->gid = new_rgid; - - ckrm_cb_gid(); - + key_fsgid_changed(current); + proc_id_connector(current, PROC_EVENT_GID); return 0; } @@ -658,7 +681,7 @@ asmlinkage long sys_setgid(gid_t gid) if(old_egid != gid) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } current->gid = current->egid = current->sgid = current->fsgid = gid; } @@ -667,15 +690,15 @@ asmlinkage long sys_setgid(gid_t gid) if(old_egid != gid) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } current->egid = current->fsgid = gid; } else return -EPERM; - ckrm_cb_gid(); - + key_fsgid_changed(current); + proc_id_connector(current, PROC_EVENT_GID); return 0; } @@ -688,7 +711,7 @@ static int set_user(uid_t new_ruid, int dumpclear) return -EAGAIN; if (atomic_read(&new_user->processes) >= - current->rlim[RLIMIT_NPROC].rlim_cur && + current->signal->rlim[RLIMIT_NPROC].rlim_cur && new_user != &root_user) { free_uid(new_user); return -EAGAIN; @@ -699,7 +722,7 @@ static int set_user(uid_t new_ruid, int dumpclear) if(dumpclear) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } current->uid = new_ruid; return 0; @@ -756,7 +779,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) if (new_euid != old_euid) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } current->fsuid = current->euid = new_euid; if (ruid != (uid_t) -1 || @@ -764,7 +787,8 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) current->suid = current->euid; current->fsuid = current->euid; - ckrm_cb_uid(); + key_fsuid_changed(current); + proc_id_connector(current, PROC_EVENT_UID); return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE); } @@ -806,12 +830,13 @@ asmlinkage long sys_setuid(uid_t uid) if (old_euid != uid) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } current->fsuid = current->euid = uid; current->suid = new_suid; - ckrm_cb_uid(); + key_fsuid_changed(current); + proc_id_connector(current, PROC_EVENT_UID); return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID); } @@ -851,7 +876,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) if (euid != current->euid) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } current->euid = euid; } @@ -859,7 +884,8 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) if (suid != (uid_t) -1) current->suid = suid; - ckrm_cb_uid(); + key_fsuid_changed(current); + proc_id_connector(current, PROC_EVENT_UID); return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES); } @@ -901,7 +927,7 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) if (egid != current->egid) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } current->egid = egid; } @@ -911,8 +937,8 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) if (sgid != (gid_t) -1) current->sgid = sgid; - ckrm_cb_gid(); - + key_fsgid_changed(current); + proc_id_connector(current, PROC_EVENT_GID); return 0; } @@ -949,11 +975,14 @@ asmlinkage long sys_setfsuid(uid_t uid) if (uid != old_fsuid) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } current->fsuid = uid; } + key_fsuid_changed(current); + proc_id_connector(current, PROC_EVENT_UID); + security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS); return old_fsuid; @@ -977,9 +1006,11 @@ asmlinkage long sys_setfsgid(gid_t gid) if (gid != old_fsgid) { current->mm->dumpable = suid_dumpable; - wmb(); + smp_wmb(); } current->fsgid = gid; + key_fsgid_changed(current); + proc_id_connector(current, PROC_EVENT_GID); } return old_fsgid; } @@ -994,39 +1025,73 @@ asmlinkage long sys_times(struct tms __user * tbuf) */ if (tbuf) { struct tms tmp; - struct task_struct *tsk = current; - struct task_struct *t; - unsigned long utime, stime, cutime, cstime; + cputime_t utime, stime, cutime, cstime; + +#ifdef CONFIG_SMP + if (thread_group_empty(current)) { + /* + * Single thread case without the use of any locks. + * + * We may race with release_task if two threads are + * executing. However, release task first adds up the + * counters (__exit_signal) before removing the task + * from the process tasklist (__unhash_process). + * __exit_signal also acquires and releases the + * siglock which results in the proper memory ordering + * so that the list modifications are always visible + * after the counters have been updated. + * + * If the counters have been updated by the second thread + * but the thread has not yet been removed from the list + * then the other branch will be executing which will + * block on tasklist_lock until the exit handling of the + * other task is finished. + * + * This also implies that the sighand->siglock cannot + * be held by another processor. So we can also + * skip acquiring that lock. + */ + utime = cputime_add(current->signal->utime, current->utime); + stime = cputime_add(current->signal->utime, current->stime); + cutime = current->signal->cutime; + cstime = current->signal->cstime; + } else +#endif + { - read_lock(&tasklist_lock); - utime = tsk->signal->utime; - stime = tsk->signal->stime; - t = tsk; - do { - utime += t->utime; - stime += t->stime; - t = next_thread(t); - } while (t != tsk); - - /* - * While we have tasklist_lock read-locked, no dying thread - * can be updating current->signal->[us]time. Instead, - * we got their counts included in the live thread loop. - * However, another thread can come in right now and - * do a wait call that updates current->signal->c[us]time. - * To make sure we always see that pair updated atomically, - * we take the siglock around fetching them. - */ - spin_lock_irq(&tsk->sighand->siglock); - cutime = tsk->signal->cutime; - cstime = tsk->signal->cstime; - spin_unlock_irq(&tsk->sighand->siglock); - read_unlock(&tasklist_lock); + /* Process with multiple threads */ + struct task_struct *tsk = current; + struct task_struct *t; - tmp.tms_utime = jiffies_to_clock_t(utime); - tmp.tms_stime = jiffies_to_clock_t(stime); - tmp.tms_cutime = jiffies_to_clock_t(cutime); - tmp.tms_cstime = jiffies_to_clock_t(cstime); + read_lock(&tasklist_lock); + utime = tsk->signal->utime; + stime = tsk->signal->stime; + t = tsk; + do { + utime = cputime_add(utime, t->utime); + stime = cputime_add(stime, t->stime); + t = next_thread(t); + } while (t != tsk); + + /* + * While we have tasklist_lock read-locked, no dying thread + * can be updating current->signal->[us]time. Instead, + * we got their counts included in the live thread loop. + * However, another thread can come in right now and + * do a wait call that updates current->signal->c[us]time. + * To make sure we always see that pair updated atomically, + * we take the siglock around fetching them. + */ + spin_lock_irq(&tsk->sighand->siglock); + cutime = tsk->signal->cutime; + cstime = tsk->signal->cstime; + spin_unlock_irq(&tsk->sighand->siglock); + read_unlock(&tasklist_lock); + } + tmp.tms_utime = cputime_to_clock_t(utime); + tmp.tms_stime = cputime_to_clock_t(stime); + tmp.tms_cutime = cputime_to_clock_t(cutime); + tmp.tms_cstime = cputime_to_clock_t(cstime); if (copy_to_user(tbuf, &tmp, sizeof(struct tms))) return -EFAULT; } @@ -1049,11 +1114,12 @@ asmlinkage long sys_times(struct tms __user * tbuf) asmlinkage long sys_setpgid(pid_t pid, pid_t pgid) { struct task_struct *p; - int err = -EINVAL; + struct task_struct *group_leader = current->group_leader; pid_t rpgid; + int err = -EINVAL; if (!pid) - pid = vx_map_pid(current->pid); + pid = vx_map_pid(group_leader->pid); if (!pgid) pgid = pid; if (pgid < 0) @@ -1075,16 +1141,16 @@ asmlinkage long sys_setpgid(pid_t pid, pid_t pgid) if (!thread_group_leader(p)) goto out; - if (p->parent == current || p->real_parent == current) { + if (p->real_parent == group_leader) { err = -EPERM; - if (p->signal->session != current->signal->session) + if (p->signal->session != group_leader->signal->session) goto out; err = -EACCES; if (p->did_exec) goto out; } else { err = -ESRCH; - if (p != current) + if (p != group_leader) goto out; } @@ -1096,7 +1162,7 @@ asmlinkage long sys_setpgid(pid_t pid, pid_t pgid) struct task_struct *p; do_each_task_pid(rpgid, PIDTYPE_PGID, p) { - if (p->signal->session == current->signal->session) + if (p->signal->session == group_leader->signal->session) goto ok_pgid; } while_each_task_pid(rpgid, PIDTYPE_PGID, p); goto out; @@ -1176,25 +1242,25 @@ asmlinkage long sys_getsid(pid_t pid) asmlinkage long sys_setsid(void) { + struct task_struct *group_leader = current->group_leader; struct pid *pid; int err = -EPERM; - if (!thread_group_leader(current)) - return -EINVAL; - + down(&tty_sem); write_lock_irq(&tasklist_lock); - pid = find_pid(PIDTYPE_PGID, current->pid); + pid = find_pid(PIDTYPE_PGID, group_leader->pid); if (pid) goto out; - current->signal->leader = 1; - __set_special_pids(current->pid, current->pid); - current->signal->tty = NULL; - current->signal->tty_old_pgrp = 0; - err = process_group(current); + group_leader->signal->leader = 1; + __set_special_pids(group_leader->pid, group_leader->pid); + group_leader->signal->tty = NULL; + group_leader->signal->tty_old_pgrp = 0; + err = process_group(group_leader); out: write_unlock_irq(&tasklist_lock); + up(&tty_sem); return err; } @@ -1296,7 +1362,7 @@ static int groups_from_user(struct group_info *group_info, return 0; } -/* a simple shell-metzner sort */ +/* a simple Shell sort */ static void groups_sort(struct group_info *group_info) { int base, max, stride; @@ -1326,7 +1392,7 @@ static void groups_sort(struct group_info *group_info) } /* a simple bsearch */ -static int groups_search(struct group_info *group_info, gid_t grp) +int groups_search(struct group_info *group_info, gid_t grp) { int left, right; @@ -1482,7 +1548,7 @@ asmlinkage long sys_sethostname(char __user *name, int len) int errno; char tmp[__NEW_UTS_LEN]; - if (!capable(CAP_SYS_ADMIN) && !vx_ccaps(VXC_SET_UTSNAME)) + if (!vx_capable(CAP_SYS_ADMIN, VXC_SET_UTSNAME)) return -EPERM; if (len < 0 || len > __NEW_UTS_LEN) return -EINVAL; @@ -1531,7 +1597,7 @@ asmlinkage long sys_setdomainname(char __user *name, int len) int errno; char tmp[__NEW_UTS_LEN]; - if (!capable(CAP_SYS_ADMIN) && !vx_ccaps(VXC_SET_UTSNAME)) + if (!vx_capable(CAP_SYS_ADMIN, VXC_SET_UTSNAME)) return -EPERM; if (len < 0 || len > __NEW_UTS_LEN) return -EINVAL; @@ -1553,9 +1619,13 @@ asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit __user *rlim) { if (resource >= RLIM_NLIMITS) return -EINVAL; - else - return copy_to_user(rlim, current->rlim + resource, sizeof(*rlim)) - ? -EFAULT : 0; + else { + struct rlimit value; + task_lock(current->group_leader); + value = current->signal->rlim[resource]; + task_unlock(current->group_leader); + return copy_to_user(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0; + } } #ifdef __ARCH_WANT_SYS_OLD_GETRLIMIT @@ -1570,7 +1640,9 @@ asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit __user *r if (resource >= RLIM_NLIMITS) return -EINVAL; - memcpy(&x, current->rlim + resource, sizeof(*rlim)); + task_lock(current->group_leader); + x = current->signal->rlim[resource]; + task_unlock(current->group_leader); if(x.rlim_cur > 0x7FFFFFFF) x.rlim_cur = 0x7FFFFFFF; if(x.rlim_max > 0x7FFFFFFF) @@ -1591,21 +1663,46 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim) return -EFAULT; if (new_rlim.rlim_cur > new_rlim.rlim_max) return -EINVAL; - old_rlim = current->rlim + resource; - if (((new_rlim.rlim_cur > old_rlim->rlim_max) || - (new_rlim.rlim_max > old_rlim->rlim_max)) && - !capable(CAP_SYS_RESOURCE) && !vx_ccaps(VXC_SET_RLIMIT)) + old_rlim = current->signal->rlim + resource; + if ((new_rlim.rlim_max > old_rlim->rlim_max) && + !vx_capable(CAP_SYS_RESOURCE, VXC_SET_RLIMIT)) return -EPERM; - if (resource == RLIMIT_NOFILE) { - if (new_rlim.rlim_cur > NR_OPEN || new_rlim.rlim_max > NR_OPEN) + if (resource == RLIMIT_NOFILE && new_rlim.rlim_max > NR_OPEN) return -EPERM; - } retval = security_task_setrlimit(resource, &new_rlim); if (retval) return retval; + task_lock(current->group_leader); *old_rlim = new_rlim; + task_unlock(current->group_leader); + + if (resource == RLIMIT_CPU && new_rlim.rlim_cur != RLIM_INFINITY && + (cputime_eq(current->signal->it_prof_expires, cputime_zero) || + new_rlim.rlim_cur <= cputime_to_secs( + current->signal->it_prof_expires))) { + unsigned long rlim_cur = new_rlim.rlim_cur; + cputime_t cputime; + + if (rlim_cur == 0) { + /* + * The caller is asking for an immediate RLIMIT_CPU + * expiry. But we use the zero value to mean "it was + * never set". So let's cheat and make it one second + * instead + */ + rlim_cur = 1; + } + cputime = secs_to_cputime(rlim_cur); + read_lock(&tasklist_lock); + spin_lock_irq(¤t->sighand->siglock); + set_process_cpu_timer(current, CPUCLOCK_PROF, + &cputime, NULL); + spin_unlock_irq(¤t->sighand->siglock); + read_unlock(&tasklist_lock); + } + return 0; } @@ -1629,18 +1726,21 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim) * given child after it's reaped, or none so this sample is before reaping. */ -void k_getrusage(struct task_struct *p, int who, struct rusage *r) +static void k_getrusage(struct task_struct *p, int who, struct rusage *r) { struct task_struct *t; unsigned long flags; - unsigned long utime, stime; + cputime_t utime, stime; memset((char *) r, 0, sizeof *r); if (unlikely(!p->signal)) return; + utime = stime = cputime_zero; + switch (who) { + case RUSAGE_BOTH: case RUSAGE_CHILDREN: spin_lock_irqsave(&p->sighand->siglock, flags); utime = p->signal->cutime; @@ -1650,45 +1750,35 @@ void k_getrusage(struct task_struct *p, int who, struct rusage *r) r->ru_minflt = p->signal->cmin_flt; r->ru_majflt = p->signal->cmaj_flt; spin_unlock_irqrestore(&p->sighand->siglock, flags); - jiffies_to_timeval(utime, &r->ru_utime); - jiffies_to_timeval(stime, &r->ru_stime); - break; + + if (who == RUSAGE_CHILDREN) + break; + case RUSAGE_SELF: - spin_lock_irqsave(&p->sighand->siglock, flags); - utime = stime = 0; - goto sum_group; - case RUSAGE_BOTH: - spin_lock_irqsave(&p->sighand->siglock, flags); - utime = p->signal->cutime; - stime = p->signal->cstime; - r->ru_nvcsw = p->signal->cnvcsw; - r->ru_nivcsw = p->signal->cnivcsw; - r->ru_minflt = p->signal->cmin_flt; - r->ru_majflt = p->signal->cmaj_flt; - sum_group: - utime += p->signal->utime; - stime += p->signal->stime; + utime = cputime_add(utime, p->signal->utime); + stime = cputime_add(stime, p->signal->stime); r->ru_nvcsw += p->signal->nvcsw; r->ru_nivcsw += p->signal->nivcsw; r->ru_minflt += p->signal->min_flt; r->ru_majflt += p->signal->maj_flt; t = p; do { - utime += t->utime; - stime += t->stime; + utime = cputime_add(utime, t->utime); + stime = cputime_add(stime, t->stime); r->ru_nvcsw += t->nvcsw; r->ru_nivcsw += t->nivcsw; r->ru_minflt += t->min_flt; r->ru_majflt += t->maj_flt; t = next_thread(t); } while (t != p); - spin_unlock_irqrestore(&p->sighand->siglock, flags); - jiffies_to_timeval(utime, &r->ru_utime); - jiffies_to_timeval(stime, &r->ru_stime); break; + default: BUG(); } + + cputime_to_timeval(utime, &r->ru_utime); + cputime_to_timeval(stime, &r->ru_stime); } int getrusage(struct task_struct *p, int who, struct rusage __user *ru) @@ -1716,8 +1806,7 @@ asmlinkage long sys_umask(int mask) asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { - int error; - int sig; + long error; error = security_task_prctl(option, arg2, arg3, arg4, arg5); if (error) @@ -1725,22 +1814,20 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, switch (option) { case PR_SET_PDEATHSIG: - sig = arg2; - if (sig < 0 || sig > _NSIG) { + if (!valid_signal(arg2)) { error = -EINVAL; break; } - current->pdeath_signal = sig; + current->pdeath_signal = arg2; break; case PR_GET_PDEATHSIG: error = put_user(current->pdeath_signal, (int __user *)arg2); break; case PR_GET_DUMPABLE: - if (current->mm->dumpable) - error = 1; + error = current->mm->dumpable; break; case PR_SET_DUMPABLE: - if (arg2 < 0 || arg2 > 2) { + if (arg2 < 0 || arg2 > 1) { error = -EINVAL; break; } @@ -1797,6 +1884,15 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, set_task_comm(me, ncomm); return 0; } + case PR_GET_NAME: { + struct task_struct *me = current; + unsigned char tcomm[sizeof(me->comm)]; + + get_task_comm(tcomm, me); + if (copy_to_user((char __user *)arg2, tcomm, sizeof(tcomm))) + return -EFAULT; + return 0; + } default: error = -EINVAL; break;