diff options
Diffstat (limited to 'arch/ia64/kernel/unaligned.c')
| -rw-r--r-- | arch/ia64/kernel/unaligned.c | 89 |
1 files changed, 55 insertions, 34 deletions
diff --git a/arch/ia64/kernel/unaligned.c b/arch/ia64/kernel/unaligned.c index 43b45b65ee5..622772b7fb6 100644 --- a/arch/ia64/kernel/unaligned.c +++ b/arch/ia64/kernel/unaligned.c @@ -13,10 +13,11 @@ * 2001/08/13 Correct size of extended floats (float_fsz) from 16 to 10 bytes. * 2001/01/17 Add support emulation of unaligned kernel accesses. */ +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/smp_lock.h> #include <linux/tty.h> +#include <linux/ratelimit.h> #include <asm/intrinsics.h> #include <asm/processor.h> @@ -24,12 +25,12 @@ #include <asm/uaccess.h> #include <asm/unaligned.h> -extern void die_if_kernel(char *str, struct pt_regs *regs, long err) __attribute__ ((noreturn)); +extern int die_if_kernel(char *str, struct pt_regs *regs, long err); #undef DEBUG_UNALIGNED_TRAP #ifdef DEBUG_UNALIGNED_TRAP -# define DPRINT(a...) do { printk("%s %u: ", __FUNCTION__, __LINE__); printk (a); } while (0) +# define DPRINT(a...) do { printk("%s %u: ", __func__, __LINE__); printk (a); } while (0) # define DDUMP(str,vp,len) dump(str, vp, len) static void @@ -53,6 +54,15 @@ dump (const char *str, void *vp, size_t len) #define SIGN_EXT9 0xffffffffffffff00ul /* + * sysctl settable hook which tells the kernel whether to honor the + * IA64_THREAD_UAC_NOPRINT prctl. Because this is user settable, we want + * to allow the super user to enable/disable this for security reasons + * (i.e. don't allow attacker to fill up logs with unaligned accesses). + */ +int no_unaligned_warning; +int unaligned_dump_stack; + +/* * For M-unit: * * opcode | m | x6 | @@ -666,9 +676,10 @@ emulate_load_updates (update_t type, load_store_t ld, struct pt_regs *regs, unsi * just in case. */ if (ld.x6_op == 1 || ld.x6_op == 3) { - printk(KERN_ERR "%s: register update on speculative load, error\n", __FUNCTION__); - die_if_kernel("unaligned reference on speculative load with register update\n", - regs, 30); + printk(KERN_ERR "%s: register update on speculative load, error\n", __func__); + if (die_if_kernel("unaligned reference on speculative load with register update\n", + regs, 30)) + return; } @@ -1095,7 +1106,7 @@ emulate_load_floatpair (unsigned long ifa, load_store_t ld, struct pt_regs *regs */ if (ld.x6_op == 1 || ld.x6_op == 3) printk(KERN_ERR "%s: register update on speculative load pair, error\n", - __FUNCTION__); + __func__); setreg(ld.r3, ifa, 0, regs); } @@ -1273,23 +1284,9 @@ emulate_store_float (unsigned long ifa, load_store_t ld, struct pt_regs *regs) /* * Make sure we log the unaligned access, so that user/sysadmin can notice it and * eventually fix the program. However, we don't want to do that for every access so we - * pace it with jiffies. This isn't really MP-safe, but it doesn't really have to be - * either... + * pace it with jiffies. */ -static int -within_logging_rate_limit (void) -{ - static unsigned long count, last_time; - - if (jiffies - last_time > 5*HZ) - count = 0; - if (++count < 5) { - last_time = jiffies; - return 1; - } - return 0; - -} +static DEFINE_RATELIMIT_STATE(logging_rate_limit, 5 * HZ, 5); void ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs) @@ -1308,7 +1305,8 @@ ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs) if (ia64_psr(regs)->be) { /* we don't support big-endian accesses */ - die_if_kernel("big-endian unaligned accesses are not supported", regs, 0); + if (die_if_kernel("big-endian unaligned accesses are not supported", regs, 0)) + return; goto force_sigbus; } @@ -1323,14 +1321,16 @@ ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs) if ((current->thread.flags & IA64_THREAD_UAC_SIGBUS) != 0) goto force_sigbus; - if (!(current->thread.flags & IA64_THREAD_UAC_NOPRINT) - && within_logging_rate_limit()) + if (!no_unaligned_warning && + !(current->thread.flags & IA64_THREAD_UAC_NOPRINT) && + __ratelimit(&logging_rate_limit)) { char buf[200]; /* comm[] is at most 16 bytes... */ size_t len; len = sprintf(buf, "%s(%d): unaligned access to 0x%016lx, " - "ip=0x%016lx\n\r", current->comm, current->pid, + "ip=0x%016lx\n\r", current->comm, + task_pid_nr(current), ifa, regs->cr_iip + ipsr->ri); /* * Don't call tty_write_message() if we're in the kernel; we might @@ -1339,12 +1339,29 @@ ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs) if (user_mode(regs)) tty_write_message(current->signal->tty, buf); buf[len-1] = '\0'; /* drop '\r' */ - printk(KERN_WARNING "%s", buf); /* watch for command names containing %s */ + /* watch for command names containing %s */ + printk(KERN_WARNING "%s", buf); + } else { + if (no_unaligned_warning) { + printk_once(KERN_WARNING "%s(%d) encountered an " + "unaligned exception which required\n" + "kernel assistance, which degrades " + "the performance of the application.\n" + "Unaligned exception warnings have " + "been disabled by the system " + "administrator\n" + "echo 0 > /proc/sys/kernel/ignore-" + "unaligned-usertrap to re-enable\n", + current->comm, task_pid_nr(current)); + } } } else { - if (within_logging_rate_limit()) + if (__ratelimit(&logging_rate_limit)) { printk(KERN_WARNING "kernel unaligned access to 0x%016lx, ip=0x%016lx\n", ifa, regs->cr_iip + ipsr->ri); + if (unaligned_dump_stack) + dump_stack(); + } set_fs(KERNEL_DS); } @@ -1462,16 +1479,19 @@ ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs) case LDFA_OP: case LDFCCLR_OP: case LDFCNC_OP: - case LDF_IMM_OP: - case LDFA_IMM_OP: - case LDFCCLR_IMM_OP: - case LDFCNC_IMM_OP: if (u.insn.x) ret = emulate_load_floatpair(ifa, u.insn, regs); else ret = emulate_load_float(ifa, u.insn, regs); break; + case LDF_IMM_OP: + case LDFA_IMM_OP: + case LDFCCLR_IMM_OP: + case LDFCNC_IMM_OP: + ret = emulate_load_float(ifa, u.insn, regs); + break; + case STF_OP: case STF_IMM_OP: ret = emulate_store_float(ifa, u.insn, regs); @@ -1505,7 +1525,8 @@ ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs) ia64_handle_exception(regs, eh); goto done; } - die_if_kernel("error during unaligned kernel access\n", regs, ret); + if (die_if_kernel("error during unaligned kernel access\n", regs, ret)) + return; /* NOT_REACHED */ } force_sigbus: |
