1805 lines · 40.9 KB · C
RawDownload
/* *  linux/kernel/sys.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ #include <linux/module.h>#include <linux/mm.h>#include <linux/utsname.h>#include <linux/mman.h>#include <linux/notifier.h>#include <linux/reboot.h>#include <linux/prctl.h>#include <linux/highuid.h>#include <linux/fs.h>#include <linux/perf_event.h>#include <linux/resource.h>#include <linux/kernel.h>#include <linux/kexec.h>#include <linux/workqueue.h>#include <linux/capability.h>#include <linux/device.h>#include <linux/key.h>#include <linux/times.h>#include <linux/posix-timers.h>#include <linux/security.h>#include <linux/dcookies.h>#include <linux/suspend.h>#include <linux/tty.h>#include <linux/signal.h>#include <linux/cn_proc.h>#include <linux/getcpu.h>#include <linux/task_io_accounting_ops.h>#include <linux/seccomp.h>#include <linux/cpu.h>#include <linux/personality.h>#include <linux/ptrace.h>#include <linux/fs_struct.h>#include <linux/gfp.h> #include <linux/compat.h>#include <linux/syscalls.h>#include <linux/kprobes.h>#include <linux/user_namespace.h> #include <asm/uaccess.h>#include <asm/io.h>#include <asm/unistd.h> #ifndef SET_UNALIGN_CTL# define SET_UNALIGN_CTL(a,b)	(-EINVAL)#endif#ifndef GET_UNALIGN_CTL# define GET_UNALIGN_CTL(a,b)	(-EINVAL)#endif#ifndef SET_FPEMU_CTL# define SET_FPEMU_CTL(a,b)	(-EINVAL)#endif#ifndef GET_FPEMU_CTL# define GET_FPEMU_CTL(a,b)	(-EINVAL)#endif#ifndef SET_FPEXC_CTL# define SET_FPEXC_CTL(a,b)	(-EINVAL)#endif#ifndef GET_FPEXC_CTL# define GET_FPEXC_CTL(a,b)	(-EINVAL)#endif#ifndef GET_ENDIAN# define GET_ENDIAN(a,b)	(-EINVAL)#endif#ifndef SET_ENDIAN# define SET_ENDIAN(a,b)	(-EINVAL)#endif#ifndef GET_TSC_CTL# define GET_TSC_CTL(a)		(-EINVAL)#endif#ifndef SET_TSC_CTL# define SET_TSC_CTL(a)		(-EINVAL)#endif /* * this is where the system-wide overflow UID and GID are defined, for * architectures that now have 32-bit UID/GID but didn't in the past */ int overflowuid = DEFAULT_OVERFLOWUID;int overflowgid = DEFAULT_OVERFLOWGID; #ifdef CONFIG_UID16EXPORT_SYMBOL(overflowuid);EXPORT_SYMBOL(overflowgid);#endif /* * the same as above, but for filesystems which can only store a 16-bit * UID and GID. as such, this is needed on all architectures */ int fs_overflowuid = DEFAULT_FS_OVERFLOWUID;int fs_overflowgid = DEFAULT_FS_OVERFLOWUID; EXPORT_SYMBOL(fs_overflowuid);EXPORT_SYMBOL(fs_overflowgid); /* * this indicates whether you can reboot with ctrl-alt-del: the default is yes */ int C_A_D = 1;struct pid *cad_pid;EXPORT_SYMBOL(cad_pid); /* * If set, this is used for preparing the system to power off. */ void (*pm_power_off_prepare)(void); /* * set the priority of a task * - the caller must hold the RCU read lock */static int set_one_prio(struct task_struct *p, int niceval, int error){	const struct cred *cred = current_cred(), *pcred = __task_cred(p);	int no_nice; 	if (pcred->uid  != cred->euid &&	    pcred->euid != cred->euid && !capable(CAP_SYS_NICE)) {		error = -EPERM;		goto out;	}	if (niceval < task_nice(p) && !can_nice(p, niceval)) {		error = -EACCES;		goto out;	}	no_nice = security_task_setnice(p, niceval);	if (no_nice) {		error = no_nice;		goto out;	}	if (error == -ESRCH)		error = 0;	set_user_nice(p, niceval);out:	return error;} SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval){	struct task_struct *g, *p;	struct user_struct *user;	const struct cred *cred = current_cred();	int error = -EINVAL;	struct pid *pgrp; 	if (which > PRIO_USER || which < PRIO_PROCESS)		goto out; 	/* normalize: avoid signed division (rounding problems) */	error = -ESRCH;	if (niceval < -20)		niceval = -20;	if (niceval > 19)		niceval = 19; 	rcu_read_lock();	read_lock(&tasklist_lock);	switch (which) {		case PRIO_PROCESS:			if (who)				p = find_task_by_vpid(who);			else				p = current;			if (p)				error = set_one_prio(p, niceval, error);			break;		case PRIO_PGRP:			if (who)				pgrp = find_vpid(who);			else				pgrp = task_pgrp(current);			do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {				error = set_one_prio(p, niceval, error);			} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);			break;		case PRIO_USER:			user = (struct user_struct *) cred->user;			if (!who)				who = cred->uid;			else if ((who != cred->uid) &&				 !(user = find_user(who)))				goto out_unlock;	/* No processes for this user */ 			do_each_thread(g, p) {				if (__task_cred(p)->uid == who)					error = set_one_prio(p, niceval, error);			} while_each_thread(g, p);			if (who != cred->uid)				free_uid(user);		/* For find_user() */			break;	}out_unlock:	read_unlock(&tasklist_lock);	rcu_read_unlock();out:	return error;} /* * Ugh. To avoid negative return values, "getpriority()" will * not return the normal nice-value, but a negated value that * has been offset by 20 (ie it returns 40..1 instead of -20..19) * to stay compatible. */SYSCALL_DEFINE2(getpriority, int, which, int, who){	struct task_struct *g, *p;	struct user_struct *user;	const struct cred *cred = current_cred();	long niceval, retval = -ESRCH;	struct pid *pgrp; 	if (which > PRIO_USER || which < PRIO_PROCESS)		return -EINVAL; 	rcu_read_lock();	read_lock(&tasklist_lock);	switch (which) {		case PRIO_PROCESS:			if (who)				p = find_task_by_vpid(who);			else				p = current;			if (p) {				niceval = 20 - task_nice(p);				if (niceval > retval)					retval = niceval;			}			break;		case PRIO_PGRP:			if (who)				pgrp = find_vpid(who);			else				pgrp = task_pgrp(current);			do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {				niceval = 20 - task_nice(p);				if (niceval > retval)					retval = niceval;			} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);			break;		case PRIO_USER:			user = (struct user_struct *) cred->user;			if (!who)				who = cred->uid;			else if ((who != cred->uid) &&				 !(user = find_user(who)))				goto out_unlock;	/* No processes for this user */ 			do_each_thread(g, p) {				if (__task_cred(p)->uid == who) {					niceval = 20 - task_nice(p);					if (niceval > retval)						retval = niceval;				}			} while_each_thread(g, p);			if (who != cred->uid)				free_uid(user);		/* for find_user() */			break;	}out_unlock:	read_unlock(&tasklist_lock);	rcu_read_unlock(); 	return retval;} /** *	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){	blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);	system_state = SYSTEM_RESTART;	device_shutdown();	sysdev_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);	machine_restart(cmd);}EXPORT_SYMBOL_GPL(kernel_restart); static void kernel_shutdown_prepare(enum system_states state){	blocking_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);	sysdev_shutdown();	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);	if (pm_power_off_prepare)		pm_power_off_prepare();	disable_nonboot_cpus();	sysdev_shutdown();	printk(KERN_EMERG "Power down.\n");	machine_power_off();}EXPORT_SYMBOL_GPL(kernel_power_off); static DEFINE_MUTEX(reboot_mutex); /* * Reboot system call: for obvious reasons only root may call it, * and even root needs to set up some magic numbers in the registers * so that some mistake won't make this reboot the whole machine. * You can also set the meaning of the ctrl-alt-del-key here. * * reboot doesn't sync: do that yourself before calling this. */SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,		void __user *, arg){	char buffer[256];	int ret = 0; 	/* We only trust the superuser with rebooting the system. */	if (!capable(CAP_SYS_BOOT))		return -EPERM; 	/* For safety, we require "magic" arguments. */	if (magic1 != LINUX_REBOOT_MAGIC1 ||	    (magic2 != LINUX_REBOOT_MAGIC2 &&	                magic2 != LINUX_REBOOT_MAGIC2A &&			magic2 != LINUX_REBOOT_MAGIC2B &&	                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; 	mutex_lock(&reboot_mutex);	switch (cmd) {	case LINUX_REBOOT_CMD_RESTART:		kernel_restart(NULL);		break; 	case LINUX_REBOOT_CMD_CAD_ON:		C_A_D = 1;		break; 	case LINUX_REBOOT_CMD_CAD_OFF:		C_A_D = 0;		break; 	case LINUX_REBOOT_CMD_HALT:		kernel_halt();		do_exit(0);		panic("cannot halt"); 	case LINUX_REBOOT_CMD_POWER_OFF:		kernel_power_off();		do_exit(0);		break; 	case LINUX_REBOOT_CMD_RESTART2:		if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {			ret = -EFAULT;			break;		}		buffer[sizeof(buffer) - 1] = '\0'; 		kernel_restart(buffer);		break; #ifdef CONFIG_KEXEC	case LINUX_REBOOT_CMD_KEXEC:		ret = kernel_kexec();		break;#endif #ifdef CONFIG_HIBERNATION	case LINUX_REBOOT_CMD_SW_SUSPEND:		ret = hibernate();		break;#endif 	default:		ret = -EINVAL;		break;	}	mutex_unlock(&reboot_mutex);	return ret;} static void deferred_cad(struct work_struct *dummy){	kernel_restart(NULL);} /* * This function gets called by ctrl-alt-del - ie the keyboard interrupt. * As it's called within an interrupt, it may NOT sync: the only choice * is whether to reboot at once, or just ignore the ctrl-alt-del. */void ctrl_alt_del(void){	static DECLARE_WORK(cad_work, deferred_cad); 	if (C_A_D)		schedule_work(&cad_work);	else		kill_cad_pid(SIGINT, 1);}	/* * Unprivileged users may change the real gid to the effective gid * or vice versa.  (BSD-style) * * If you set the real gid at all, or set the effective gid to a value not * equal to the real gid, then the saved gid is set to the new effective gid. * * This makes it possible for a setgid program to completely drop its * privileges, which is often a useful assertion to make when you are doing * a security audit over a program. * * The general idea is that a program which uses just setregid() will be * 100% compatible with BSD.  A program which uses just setgid() will be * 100% compatible with POSIX with saved IDs.  * * SMP: There are not races, the GIDs are checked only by filesystem *      operations (as far as semantic preservation is concerned). */SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid){	const struct cred *old;	struct cred *new;	int retval; 	new = prepare_creds();	if (!new)		return -ENOMEM;	old = current_cred(); 	retval = -EPERM;	if (rgid != (gid_t) -1) {		if (old->gid == rgid ||		    old->egid == rgid ||		    capable(CAP_SETGID))			new->gid = rgid;

Lines 1–500 of 1805.