aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasoud Asgharifard Sharbiani <masouds@google.com>2007-07-22 05:12:28 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-22 14:03:37 -0400
commitabd4f7505bafdd6c5319fe3cb5caf9af6104e17a (patch)
treea543fce720331dbf6194a2c0471f36b7727b9736
parent5fa63fccc579ac609fc7f86d29ccb3a2edf910d7 (diff)
x86: i386-show-unhandled-signals-v3
This patch makes the i386 behave the same way that x86_64 does when a segfault happens. A line gets printed to the kernel log so that tools that need to check for failures can behave more uniformly between debug.show_unhandled_signals sysctl variable to 0 (or by doing echo 0 > /proc/sys/debug/exception-trace) Also, all of the lines being printed are now using printk_ratelimit() to deny the ability of DoS from a local user with a program like the following: main() { while (1) if (!fork()) *(int *)0 = 0; } This new revision also includes the fix that Andrew did which got rid of new sysctl that was added to the system in earlier versions of this. Also, 'show-unhandled-signals' sysctl has been renamed back to the old 'exception-trace' to avoid breakage of people's scripts. AK: Enabling by default for i386 will be likely controversal, but let's see what happens AK: Really folks, before complaining just fix your segfaults AK: I bet this will find a lot of silent issues Signed-off-by: Masoud Sharbiani <masouds@google.com> Signed-off-by: Andi Kleen <ak@suse.de> [ Personally, I've found the complaints useful on x86-64, so I'm all for this. That said, I wonder if we could do it more prettily.. -Linus ] Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/i386/kernel/signal.c7
-rw-r--r--arch/i386/kernel/traps.c7
-rw-r--r--arch/i386/mm/fault.c10
-rw-r--r--arch/x86_64/kernel/signal.c2
-rw-r--r--arch/x86_64/kernel/traps.c6
-rw-r--r--arch/x86_64/mm/fault.c15
-rw-r--r--arch/x86_64/mm/init.c33
-rw-r--r--include/asm-x86_64/proto.h2
-rw-r--r--include/linux/signal.h3
-rw-r--r--kernel/signal.c10
-rw-r--r--kernel/sysctl.c10
11 files changed, 55 insertions, 50 deletions
diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c
index d574e38f0f77..f5dd85656c18 100644
--- a/arch/i386/kernel/signal.c
+++ b/arch/i386/kernel/signal.c
@@ -199,6 +199,13 @@ asmlinkage int sys_sigreturn(unsigned long __unused)
199 return eax; 199 return eax;
200 200
201badframe: 201badframe:
202 if (show_unhandled_signals && printk_ratelimit())
203 printk("%s%s[%d] bad frame in sigreturn frame:%p eip:%lx"
204 " esp:%lx oeax:%lx\n",
205 current->pid > 1 ? KERN_INFO : KERN_EMERG,
206 current->comm, current->pid, frame, regs->eip,
207 regs->esp, regs->orig_eax);
208
202 force_sig(SIGSEGV, current); 209 force_sig(SIGSEGV, current);
203 return 0; 210 return 0;
204} 211}
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index 57772a18c394..438949da3b63 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -618,6 +618,13 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs,
618 618
619 current->thread.error_code = error_code; 619 current->thread.error_code = error_code;
620 current->thread.trap_no = 13; 620 current->thread.trap_no = 13;
621 if (show_unhandled_signals && unhandled_signal(current, SIGSEGV) &&
622 printk_ratelimit())
623 printk(KERN_INFO
624 "%s[%d] general protection eip:%lx esp:%lx error:%lx\n",
625 current->comm, current->pid,
626 regs->eip, regs->esp, error_code);
627
621 force_sig(SIGSEGV, current); 628 force_sig(SIGSEGV, current);
622 return; 629 return;
623 630
diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c
index e92a10124935..01ffdd4964f0 100644
--- a/arch/i386/mm/fault.c
+++ b/arch/i386/mm/fault.c
@@ -283,6 +283,8 @@ static inline int vmalloc_fault(unsigned long address)
283 return 0; 283 return 0;
284} 284}
285 285
286int show_unhandled_signals = 1;
287
286/* 288/*
287 * This routine handles page faults. It determines the address, 289 * This routine handles page faults. It determines the address,
288 * and the problem, and then passes it off to one of the appropriate 290 * and the problem, and then passes it off to one of the appropriate
@@ -469,6 +471,14 @@ bad_area_nosemaphore:
469 if (is_prefetch(regs, address, error_code)) 471 if (is_prefetch(regs, address, error_code))
470 return; 472 return;
471 473
474 if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
475 printk_ratelimit()) {
476 printk("%s%s[%d]: segfault at %08lx eip %08lx "
477 "esp %08lx error %lx\n",
478 tsk->pid > 1 ? KERN_INFO : KERN_EMERG,
479 tsk->comm, tsk->pid, address, regs->eip,
480 regs->esp, error_code);
481 }
472 tsk->thread.cr2 = address; 482 tsk->thread.cr2 = address;
473 /* Kernel addresses are always protection faults */ 483 /* Kernel addresses are always protection faults */
474 tsk->thread.error_code = error_code | (address >= TASK_SIZE); 484 tsk->thread.error_code = error_code | (address >= TASK_SIZE);
diff --git a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c
index 4886afcd6287..739175b01e06 100644
--- a/arch/x86_64/kernel/signal.c
+++ b/arch/x86_64/kernel/signal.c
@@ -487,7 +487,7 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
487void signal_fault(struct pt_regs *regs, void __user *frame, char *where) 487void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
488{ 488{
489 struct task_struct *me = current; 489 struct task_struct *me = current;
490 if (exception_trace) 490 if (show_unhandled_signals && printk_ratelimit())
491 printk("%s[%d] bad frame in %s frame:%p rip:%lx rsp:%lx orax:%lx\n", 491 printk("%s[%d] bad frame in %s frame:%p rip:%lx rsp:%lx orax:%lx\n",
492 me->comm,me->pid,where,frame,regs->rip,regs->rsp,regs->orig_rax); 492 me->comm,me->pid,where,frame,regs->rip,regs->rsp,regs->orig_rax);
493 493
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c
index 8713ad4a4db1..03888420775d 100644
--- a/arch/x86_64/kernel/traps.c
+++ b/arch/x86_64/kernel/traps.c
@@ -584,7 +584,8 @@ static void __kprobes do_trap(int trapnr, int signr, char *str,
584 tsk->thread.error_code = error_code; 584 tsk->thread.error_code = error_code;
585 tsk->thread.trap_no = trapnr; 585 tsk->thread.trap_no = trapnr;
586 586
587 if (exception_trace && unhandled_signal(tsk, signr)) 587 if (show_unhandled_signals && unhandled_signal(tsk, signr) &&
588 printk_ratelimit())
588 printk(KERN_INFO 589 printk(KERN_INFO
589 "%s[%d] trap %s rip:%lx rsp:%lx error:%lx\n", 590 "%s[%d] trap %s rip:%lx rsp:%lx error:%lx\n",
590 tsk->comm, tsk->pid, str, 591 tsk->comm, tsk->pid, str,
@@ -688,7 +689,8 @@ asmlinkage void __kprobes do_general_protection(struct pt_regs * regs,
688 tsk->thread.error_code = error_code; 689 tsk->thread.error_code = error_code;
689 tsk->thread.trap_no = 13; 690 tsk->thread.trap_no = 13;
690 691
691 if (exception_trace && unhandled_signal(tsk, SIGSEGV)) 692 if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
693 printk_ratelimit())
692 printk(KERN_INFO 694 printk(KERN_INFO
693 "%s[%d] general protection rip:%lx rsp:%lx error:%lx\n", 695 "%s[%d] general protection rip:%lx rsp:%lx error:%lx\n",
694 tsk->comm, tsk->pid, 696 tsk->comm, tsk->pid,
diff --git a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c
index 2074bddd4f04..5e9ac70c135e 100644
--- a/arch/x86_64/mm/fault.c
+++ b/arch/x86_64/mm/fault.c
@@ -221,16 +221,6 @@ static int is_errata93(struct pt_regs *regs, unsigned long address)
221 return 0; 221 return 0;
222} 222}
223 223
224int unhandled_signal(struct task_struct *tsk, int sig)
225{
226 if (is_init(tsk))
227 return 1;
228 if (tsk->ptrace & PT_PTRACED)
229 return 0;
230 return (tsk->sighand->action[sig-1].sa.sa_handler == SIG_IGN) ||
231 (tsk->sighand->action[sig-1].sa.sa_handler == SIG_DFL);
232}
233
234static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs, 224static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs,
235 unsigned long error_code) 225 unsigned long error_code)
236{ 226{
@@ -302,7 +292,7 @@ static int vmalloc_fault(unsigned long address)
302} 292}
303 293
304static int page_fault_trace; 294static int page_fault_trace;
305int exception_trace = 1; 295int show_unhandled_signals = 1;
306 296
307/* 297/*
308 * This routine handles page faults. It determines the address, 298 * This routine handles page faults. It determines the address,
@@ -494,7 +484,8 @@ bad_area_nosemaphore:
494 (address >> 32)) 484 (address >> 32))
495 return; 485 return;
496 486
497 if (exception_trace && unhandled_signal(tsk, SIGSEGV)) { 487 if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
488 printk_ratelimit()) {
498 printk( 489 printk(
499 "%s%s[%d]: segfault at %016lx rip %016lx rsp %016lx error %lx\n", 490 "%s%s[%d]: segfault at %016lx rip %016lx rsp %016lx error %lx\n",
500 tsk->pid > 1 ? KERN_INFO : KERN_EMERG, 491 tsk->pid > 1 ? KERN_INFO : KERN_EMERG,
diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c
index 381c2ecd407e..88678e82e23d 100644
--- a/arch/x86_64/mm/init.c
+++ b/arch/x86_64/mm/init.c
@@ -697,39 +697,6 @@ int kern_addr_valid(unsigned long addr)
697 return pfn_valid(pte_pfn(*pte)); 697 return pfn_valid(pte_pfn(*pte));
698} 698}
699 699
700#ifdef CONFIG_SYSCTL
701#include <linux/sysctl.h>
702
703static ctl_table debug_table2[] = {
704 {
705 .ctl_name = 99,
706 .procname = "exception-trace",
707 .data = &exception_trace,
708 .maxlen = sizeof(int),
709 .mode = 0644,
710 .proc_handler = proc_dointvec
711 },
712 {}
713};
714
715static ctl_table debug_root_table2[] = {
716 {
717 .ctl_name = CTL_DEBUG,
718 .procname = "debug",
719 .mode = 0555,
720 .child = debug_table2
721 },
722 {}
723};
724
725static __init int x8664_sysctl_init(void)
726{
727 register_sysctl_table(debug_root_table2);
728 return 0;
729}
730__initcall(x8664_sysctl_init);
731#endif
732
733/* A pseudo VMA to allow ptrace access for the vsyscall page. This only 700/* A pseudo VMA to allow ptrace access for the vsyscall page. This only
734 covers the 64bit vsyscall page now. 32bit has a real VMA now and does 701 covers the 64bit vsyscall page now. 32bit has a real VMA now and does
735 not need special handling anymore. */ 702 not need special handling anymore. */
diff --git a/include/asm-x86_64/proto.h b/include/asm-x86_64/proto.h
index d6e3225549c0..31f20ad65876 100644
--- a/include/asm-x86_64/proto.h
+++ b/include/asm-x86_64/proto.h
@@ -75,8 +75,6 @@ extern void setup_node_bootmem(int nodeid, unsigned long start, unsigned long en
75extern void early_quirks(void); 75extern void early_quirks(void);
76extern void check_efer(void); 76extern void check_efer(void);
77 77
78extern int unhandled_signal(struct task_struct *tsk, int sig);
79
80extern void select_idle_routine(const struct cpuinfo_x86 *c); 78extern void select_idle_routine(const struct cpuinfo_x86 *c);
81 79
82extern unsigned long table_start, table_end; 80extern unsigned long table_start, table_end;
diff --git a/include/linux/signal.h b/include/linux/signal.h
index ea91abe740da..0ae338866240 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -237,12 +237,15 @@ extern int group_send_sig_info(int sig, struct siginfo *info, struct task_struct
237extern int __group_send_sig_info(int, struct siginfo *, struct task_struct *); 237extern int __group_send_sig_info(int, struct siginfo *, struct task_struct *);
238extern long do_sigpending(void __user *, unsigned long); 238extern long do_sigpending(void __user *, unsigned long);
239extern int sigprocmask(int, sigset_t *, sigset_t *); 239extern int sigprocmask(int, sigset_t *, sigset_t *);
240extern int show_unhandled_signals;
240 241
241struct pt_regs; 242struct pt_regs;
242extern int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct pt_regs *regs, void *cookie); 243extern int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct pt_regs *regs, void *cookie);
243 244
244extern struct kmem_cache *sighand_cachep; 245extern struct kmem_cache *sighand_cachep;
245 246
247int unhandled_signal(struct task_struct *tsk, int sig);
248
246/* 249/*
247 * In POSIX a signal is sent either to a specific thread (Linux task) 250 * In POSIX a signal is sent either to a specific thread (Linux task)
248 * or to the process as a whole (Linux thread group). How the signal 251 * or to the process as a whole (Linux thread group). How the signal
diff --git a/kernel/signal.c b/kernel/signal.c
index 39d122753bac..ef8156a6aad5 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -255,6 +255,16 @@ flush_signal_handlers(struct task_struct *t, int force_default)
255 } 255 }
256} 256}
257 257
258int unhandled_signal(struct task_struct *tsk, int sig)
259{
260 if (is_init(tsk))
261 return 1;
262 if (tsk->ptrace & PT_PTRACED)
263 return 0;
264 return (tsk->sighand->action[sig-1].sa.sa_handler == SIG_IGN) ||
265 (tsk->sighand->action[sig-1].sa.sa_handler == SIG_DFL);
266}
267
258 268
259/* Notify the system that a driver wants to block all signals for this 269/* Notify the system that a driver wants to block all signals for this
260 * process, and wants to be notified if any signals at all were to be 270 * process, and wants to be notified if any signals at all were to be
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 222299844ad1..ddebf3f2affe 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1203,6 +1203,16 @@ static ctl_table fs_table[] = {
1203}; 1203};
1204 1204
1205static ctl_table debug_table[] = { 1205static ctl_table debug_table[] = {
1206#ifdef CONFIG_X86
1207 {
1208 .ctl_name = CTL_UNNUMBERED,
1209 .procname = "exception-trace",
1210 .data = &show_unhandled_signals,
1211 .maxlen = sizeof(int),
1212 .mode = 0644,
1213 .proc_handler = proc_dointvec
1214 },
1215#endif
1206 { .ctl_name = 0 } 1216 { .ctl_name = 0 }
1207}; 1217};
1208 1218