signal_kern.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 312 行

C
312
字号
/*  * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */#include "linux/config.h"#include "linux/stddef.h"#include "linux/sys.h"#include "linux/sched.h"#include "linux/wait.h"#include "linux/kernel.h"#include "linux/smp_lock.h"#include "linux/module.h"#include "linux/slab.h"#include "linux/tty.h"#include "linux/binfmts.h"#include "linux/ptrace.h"#include "asm/signal.h"#include "asm/uaccess.h"#include "asm/unistd.h"#include "user_util.h"#include "asm/ucontext.h"#include "kern_util.h"#include "signal_kern.h"#include "signal_user.h"#include "kern.h"#include "frame_kern.h"#include "sigcontext.h"#include "mode.h"EXPORT_SYMBOL(block_signals);EXPORT_SYMBOL(unblock_signals);#define _S(nr) (1<<((nr)-1))#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))/* * OK, we're invoking a handler */	static int handle_signal(struct pt_regs *regs, unsigned long signr, 			 struct k_sigaction *ka, siginfo_t *info, 			 sigset_t *oldset, int error){        __sighandler_t handler;	void (*restorer)(void);	unsigned long sp;	sigset_t save;	int err, ret;	ret = 0;	/* Always make any pending restarted system calls return -EINTR */	current_thread_info()->restart_block.fn = do_no_restart_syscall;	switch(error){	case -ERESTART_RESTARTBLOCK:	case -ERESTARTNOHAND:		ret = -EINTR;		break;	case -ERESTARTSYS:		if (!(ka->sa.sa_flags & SA_RESTART)) {			ret = -EINTR;			break;		}		/* fallthrough */	case -ERESTARTNOINTR:		PT_REGS_RESTART_SYSCALL(regs);		PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);		/* This is because of the UM_SET_SYSCALL_RETURN and the fact		 * that on i386 the system call number and return value are		 * in the same register.  When the system call restarts, %eax		 * had better have the system call number in it.  Since the		 * return value doesn't matter (except that it shouldn't be		 * -ERESTART*), we'll stick the system call number there.		 */		ret = PT_REGS_SYSCALL_NR(regs);		break;	}	handler = ka->sa.sa_handler;	save = *oldset;	if (ka->sa.sa_flags & SA_ONESHOT)		ka->sa.sa_handler = SIG_DFL;	if (!(ka->sa.sa_flags & SA_NODEFER)) {		spin_lock_irq(&current->sighand->siglock);		sigorsets(&current->blocked, &current->blocked, 			  &ka->sa.sa_mask);		sigaddset(&current->blocked, signr);		recalc_sigpending();		spin_unlock_irq(&current->sighand->siglock);	}	sp = PT_REGS_SP(regs);	if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))		sp = current->sas_ss_sp + current->sas_ss_size;		if(error != 0) PT_REGS_SET_SYSCALL_RETURN(regs, ret);	if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer;	else restorer = NULL;	if(ka->sa.sa_flags & SA_SIGINFO)		err = setup_signal_stack_si(sp, signr, (unsigned long) handler,					    restorer, regs, info, &save);	else		err = setup_signal_stack_sc(sp, signr, (unsigned long) handler,					    restorer, regs, &save);	if(err) goto segv;	return(0); segv:	force_sigsegv(signr, current);	return(1);}static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset, int error){	struct k_sigaction ka_copy;	siginfo_t info;	int err, sig;	if (!oldset)		oldset = &current->blocked;	sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL);	if(sig == 0)		return(0);	/* Whee!  Actually deliver the signal.  */	err = handle_signal(regs, sig, &ka_copy, &info, oldset, error);	if(!err)		return(1);	/* Did we come from a system call? */	if(PT_REGS_SYSCALL_NR(regs) >= 0){		/* Restart the system call - no handlers present */		if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND ||		   PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS ||		   PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOINTR){			PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);			PT_REGS_RESTART_SYSCALL(regs);		}		else if(PT_REGS_SYSCALL_RET(regs) == -ERESTART_RESTARTBLOCK){			PT_REGS_SYSCALL_RET(regs) = __NR_restart_syscall;			PT_REGS_RESTART_SYSCALL(regs); 		}	}	/* This closes a way to execute a system call on the host.  If	 * you set a breakpoint on a system call instruction and singlestep	 * from it, the tracing thread used to PTRACE_SINGLESTEP the process	 * rather than PTRACE_SYSCALL it, allowing the system call to execute	 * on the host.  The tracing thread will check this flag and 	 * PTRACE_SYSCALL if necessary.	 */	if((current->ptrace & PT_DTRACE) && 	   is_syscall(PT_REGS_IP(&current->thread.regs))) 		(void) CHOOSE_MODE(current->thread.mode.tt.singlestep_syscall = 1, 0);	return(0);}int do_signal(int error){	return(kern_do_signal(&current->thread.regs, NULL, error));}/* * Atomically swap in the new signal mask, and wait for a signal. */int sys_sigsuspend(int history0, int history1, old_sigset_t mask){	sigset_t saveset;	mask &= _BLOCKABLE;	spin_lock_irq(&current->sighand->siglock);	saveset = current->blocked;	siginitset(&current->blocked, mask);	recalc_sigpending();	spin_unlock_irq(&current->sighand->siglock);	while (1) {		current->state = TASK_INTERRUPTIBLE;		schedule();		if(kern_do_signal(&current->thread.regs, &saveset, -EINTR))			return(-EINTR);	}}int sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize){	sigset_t saveset, newset;	/* XXX: Don't preclude handling different sized sigset_t's.  */	if (sigsetsize != sizeof(sigset_t))		return -EINVAL;	if (copy_from_user(&newset, unewset, sizeof(newset)))		return -EFAULT;	sigdelsetmask(&newset, ~_BLOCKABLE);	spin_lock_irq(&current->sighand->siglock);	saveset = current->blocked;	current->blocked = newset;	recalc_sigpending();	spin_unlock_irq(&current->sighand->siglock);	while (1) {		current->state = TASK_INTERRUPTIBLE;		schedule();		if (kern_do_signal(&current->thread.regs, &saveset, -EINTR))			return(-EINTR);	}}int sys_sigaction(int sig, const struct old_sigaction __user *act,			 struct old_sigaction __user *oact){	struct k_sigaction new_ka, old_ka;	int ret;	if (act) {		old_sigset_t mask;		if (verify_area(VERIFY_READ, act, sizeof(*act)) ||		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))			return -EFAULT;		__get_user(new_ka.sa.sa_flags, &act->sa_flags);		__get_user(mask, &act->sa_mask);		siginitset(&new_ka.sa.sa_mask, mask);	}	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);	if (!ret && oact) {		if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))			return -EFAULT;		__put_user(old_ka.sa.sa_flags, &oact->sa_flags);		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);	}	return ret;}int sys_sigaltstack(const stack_t *uss, stack_t *uoss){	return(do_sigaltstack(uss, uoss, PT_REGS_SP(&current->thread.regs)));}extern int userspace_pid[];static int copy_sc_from_user(struct pt_regs *to, void *from, 			     struct arch_frame_data *arch){	int ret;	ret = CHOOSE_MODE(copy_sc_from_user_tt(UPT_SC(&to->regs), from, arch),			  copy_sc_from_user_skas(userspace_pid[0],						 &to->regs, from));	return(ret);}int sys_sigreturn(struct pt_regs regs){	void __user *sc = sp_to_sc(PT_REGS_SP(&current->thread.regs));	void __user *mask = sp_to_mask(PT_REGS_SP(&current->thread.regs));	int sig_size = (_NSIG_WORDS - 1) * sizeof(unsigned long);	spin_lock_irq(&current->sighand->siglock);	copy_from_user(&current->blocked.sig[0], sc_sigmask(sc), 		       sizeof(current->blocked.sig[0]));	copy_from_user(&current->blocked.sig[1], mask, sig_size);	sigdelsetmask(&current->blocked, ~_BLOCKABLE);	recalc_sigpending();	spin_unlock_irq(&current->sighand->siglock);	copy_sc_from_user(&current->thread.regs, sc, 			  &signal_frame_sc.common.arch);	return(PT_REGS_SYSCALL_RET(&current->thread.regs));}int sys_rt_sigreturn(struct pt_regs regs){	unsigned long sp = PT_REGS_SP(&current->thread.regs);	struct ucontext __user *uc = sp_to_uc(sp);	int sig_size = _NSIG_WORDS * sizeof(unsigned long);	spin_lock_irq(&current->sighand->siglock);	copy_from_user(&current->blocked, &uc->uc_sigmask, sig_size);	sigdelsetmask(&current->blocked, ~_BLOCKABLE);	recalc_sigpending();	spin_unlock_irq(&current->sighand->siglock);	copy_sc_from_user(&current->thread.regs, &uc->uc_mcontext,			  &signal_frame_si.common.arch);	return(PT_REGS_SYSCALL_RET(&current->thread.regs));}/* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only.  This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-file-style: "linux" * End: */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?