This is a compromising fix for sysmips(MIPS_ATOMIC_SET, ...). It forces SIGSYS when the return value is a small negative value. This limit is OK for glibc. This patches works for CPUs both with ll/sc and without ll/sc. Derived from Maceij's sys_test_and_set patch. Jun diff -Nru linux/arch/mips/kernel/sysmips.c.orig linux/arch/mips/kernel/sysmips.c --- linux/arch/mips/kernel/sysmips.c.orig Mon Apr 23 11:32:54 2001 +++ linux/arch/mips/kernel/sysmips.c Tue Jun 26 11:50:48 2001 @@ -16,7 +16,7 @@ #include #include #include - +#include #include #include #include @@ -75,53 +75,83 @@ } case MIPS_ATOMIC_SET: { -#ifdef CONFIG_CPU_HAS_LLSC - unsigned int tmp; + int *ptr, val, ret, err, tmp; + struct siginfo info; - p = (int *) arg1; - errno = verify_area(VERIFY_WRITE, p, sizeof(*p)); - if (errno) - return errno; - errno = 0; - - __asm__(".set\tpush\t\t\t# sysmips(MIPS_ATOMIC, ...)\n\t" - ".set\tmips2\n\t" - ".set\tnoat\n\t" - "1:\tll\t%0, %4\n\t" - "move\t$1, %3\n\t" - "2:\tsc\t$1, %1\n\t" - "beqz\t$1, 1b\n\t" - ".set\tpop\n\t" - ".section\t.fixup,\"ax\"\n" - "3:\tli\t%2, 1\t\t\t# error\n\t" - ".previous\n\t" - ".section\t__ex_table,\"a\"\n\t" - ".word\t1b, 3b\n\t" - ".word\t2b, 3b\n\t" - ".previous\n\t" - : "=&r" (tmp), "=o" (* (u32 *) p), "=r" (errno) - : "r" (arg2), "o" (* (u32 *) p), "2" (errno) - : "$1"); + ptr = (int *)arg1; + val = (int)arg2; - if (errno) - return -EFAULT; + /* Don't emulate unaligned accesses. */ + if ((int)ptr & 3) { + info.si_signo = SIGBUS; + info.si_code = BUS_ADRALN; + goto fault; + } + + /* A zero here saves us three instructions. */ + err = verify_area(VERIFY_WRITE, ptr, 0); + if (err) { + info.si_signo = SIGSEGV; + info.si_code = SEGV_ACCERR; + goto fault; + } - /* We're skipping error handling etc. */ - if (current->ptrace & PT_TRACESYS) - syscall_trace(); - - ((struct pt_regs *)&cmd)->regs[2] = tmp; - ((struct pt_regs *)&cmd)->regs[7] = 0; - - __asm__ __volatile__( - "move\t$29, %0\n\t" - "j\to32_ret_from_sys_call" - : /* No outputs */ - : "r" (&cmd)); - /* Unreached */ +#ifdef CONFIG_CPU_HAS_LLSC + __asm__(".set mips2\n\t" + "1:\n\t" + "ll %0,%5\n\t" + ".set push\n\t" + ".set noreorder\n\t" + "beq %0,%4,3f\n\t" + " move %3,%4\n" + ".set pop\n\t" + "2:\n\t" + "sc %3,%1\n\t" + "beqz %3,1b\n\t" + "3:\n\t" + ".set mips0\n\t" + ".section .fixup,\"ax\"\n" + "4:\n\t" + "li %2,%7\n\t" + "j 3b\n\t" + ".previous\n\t" + ".section __ex_table,\"a\"\n\t" + ".word 1b,4b\n\t" + ".word 2b,4b\n\t" + ".previous" + : "=&r" (ret), "=R" (*ptr), "=r" (err), "=&r" (tmp) + : "r" (val), "1" (*ptr), "2" (0), "i" (-EFAULT)); #else - printk("sys_sysmips(MIPS_ATOMIC_SET, ...) not ready for !CONFIG_CPU_HAS_LLSC\n"); + save_and_cli(tmp); + err = __get_user(ret, ptr); + if (ret != val) + err |= __put_user(val, ptr); /* No fault + unless unwriteable. */ + restore_flags(tmp); #endif + + if (err) { + info.si_signo = SIGSEGV; + info.si_code = SEGV_MAPERR; + goto fault; + } + + if ( (ret < 0) && (ret >= -EMAXERRNO) ) { + info.si_signo = SIGSYS; + info.si_code = 0; + goto fault; + } + + return ret; + +fault: + /* Go back to SYSCALL. */ + ((struct pt_regs *)&cmd)->cp0_epc -= 4; + + info.si_addr = (void *)((struct pt_regs *)&cmd)->cp0_epc; + force_sig_info(info.si_signo, &info, current); + + return 0; } case MIPS_FIXADE: