Hi All, futexes doesn't work correctly on (at least) MIPS64/n32 if the value argument is not properly sign extended (glibc doesn't sign extend it properly). According to the following discussion kernel shall be able to handle such a case (https://lkml.org/lkml/2007/6/4/456): On Mon, 4 Jun 2007 17:04:12 -0700 "H. Peter Anvin" wrote: > On Mon, 4 Jun 2007 17:26:27 EST David Miller wrote: >> On Mon, 4 Jun 2007 20:56:57 UTC Joseph S. Myers wrote: >>> What should the kernel syscall ABI be in such cases (any case where >>> the syscall implementations expect arguments narrower than registers, >>> so mainly 32-bit arguments on 64-bit platforms)? There are two >>> obvious possibilities: >> In general we've taken the stance that the syscall dispatch >> should create the proper calling environment for C code >> implementing the system calls, and this thus means properly >> sign and zero extending the arguments as expected by the C >> calling convention. > This is, in fact, rather fundamental (some ABIs don't require sign or > zero extension, e.g. x86-64); otherwise libc's job becomes a whole lot > harder. Seems there isn't a generic solution addressing this in 2.6.32.67, 3.2.69 and 3.4.108 kernels. On newer kernels it's addressed by COMPAT_SYSCALL_DEFINE infrastructure. For futexes it can be solved by cherry-picking the following commits (patch against 2.6.32.67 is attached): 468366138850f20543f1d4878028900672b23dae 90caf58dad77a4021e9dade5d051756cea2a2cd7 but as the issue can have potential security impact (see CVE-2009-0029), all syscalls shall be fixed, probably. I've originally reported this issue to the glibc bugzilla: https://sourceware.org/bugzilla/show_bug.cgi?id=18726 where you can find an example demonstrating the issue. Cheers, Petr
Cherry-pick 468366138850f20543f1d4878028900672b23dae 90caf58dad77a4021e9dade5d051756cea2a2cd7 to make futexes working even if the value argument hasn't been properly sign extended (glibc doesn't sign extend arguments on MIPS64/n32, see glibc bug #18726). Signed-off-by: Petr Malat <oss@xxxxxxxxx> diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h index 82c882c..e4d864c 100644 --- a/arch/s390/include/asm/compat.h +++ b/arch/s390/include/asm/compat.h @@ -7,6 +7,9 @@ #include <linux/sched.h> #include <linux/thread_info.h> +#define __TYPE_IS_PTR(t) (!__builtin_types_compatible_p(typeof(0?(t)0:0ULL), u64)) +#define __SC_DELOUSE(t,v) (t)(__TYPE_IS_PTR(t) ? ((v) & 0x7fffffff) : (v)) + #define PSW32_MASK_PER 0x40000000UL #define PSW32_MASK_DAT 0x04000000UL #define PSW32_MASK_IO 0x02000000UL diff --git a/include/linux/compat.h b/include/linux/compat.h index 510266f..ca84044 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -15,6 +15,52 @@ #include <asm/siginfo.h> #include <asm/signal.h> +#ifndef COMPAT_USE_64BIT_TIME +#define COMPAT_USE_64BIT_TIME 0 +#endif + +#ifndef __SC_DELOUSE +#define __SC_DELOUSE(t,v) ((t)(unsigned long)(v)) +#endif + +#define __SC_CCAST1(t1, a1) __SC_DELOUSE(t1,a1) +#define __SC_CCAST2(t2, a2, ...) __SC_DELOUSE(t2,a2), __SC_CCAST1(__VA_ARGS__) +#define __SC_CCAST3(t3, a3, ...) __SC_DELOUSE(t3,a3), __SC_CCAST2(__VA_ARGS__) +#define __SC_CCAST4(t4, a4, ...) __SC_DELOUSE(t4,a4), __SC_CCAST3(__VA_ARGS__) +#define __SC_CCAST5(t5, a5, ...) __SC_DELOUSE(t5,a5), __SC_CCAST4(__VA_ARGS__) +#define __SC_CCAST6(t6, a6, ...) __SC_DELOUSE(t6,a6), __SC_CCAST5(__VA_ARGS__) +#define COMPAT_SYSCALL_DEFINE1(name, ...) \ + COMPAT_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) +#define COMPAT_SYSCALL_DEFINE2(name, ...) \ + COMPAT_SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) +#define COMPAT_SYSCALL_DEFINE3(name, ...) \ + COMPAT_SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) +#define COMPAT_SYSCALL_DEFINE4(name, ...) \ + COMPAT_SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) +#define COMPAT_SYSCALL_DEFINE5(name, ...) \ + COMPAT_SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) +#define COMPAT_SYSCALL_DEFINE6(name, ...) \ + COMPAT_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) + +#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS + +#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long compat_sys##name(__SC_DECL##x(__VA_ARGS__)); \ + static inline long C_SYSC##name(__SC_DECL##x(__VA_ARGS__)); \ + asmlinkage long compat_SyS##name(__SC_LONG##x(__VA_ARGS__)) \ + { \ + return (long) C_SYSC##name(__SC_CCAST##x(__VA_ARGS__)); \ + } \ + SYSCALL_ALIAS(compat_sys##name, compat_SyS##name); \ + static inline long C_SYSC##name(__SC_DECL##x(__VA_ARGS__)) + +#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */ + +#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long compat_sys##name(__SC_DECL##x(__VA_ARGS__)) + +#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */ + #define compat_jiffies_to_clock_t(x) \ (((unsigned long)(x) * COMPAT_USER_HZ) / HZ) diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c index 2357165..c3e8e66 100644 --- a/kernel/futex_compat.c +++ b/kernel/futex_compat.c @@ -10,6 +10,8 @@ #include <linux/compat.h> #include <linux/nsproxy.h> #include <linux/futex.h> +#include <linux/syscalls.h> +#include <linux/compat.h> #include <asm/uaccess.h> @@ -114,9 +116,9 @@ void compat_exit_robust_list(struct task_struct *curr) } } -asmlinkage long -compat_sys_set_robust_list(struct compat_robust_list_head __user *head, - compat_size_t len) +COMPAT_SYSCALL_DEFINE2(set_robust_list, + struct compat_robust_list_head __user *, head, + compat_size_t, len) { if (!futex_cmpxchg_enabled) return -ENOSYS; @@ -129,9 +131,9 @@ compat_sys_set_robust_list(struct compat_robust_list_head __user *head, return 0; } -asmlinkage long -compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, - compat_size_t __user *len_ptr) +COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid, + compat_uptr_t __user *, head_ptr, + compat_size_t __user *, len_ptr) { struct compat_robust_list_head __user *head; unsigned long ret; @@ -170,9 +172,9 @@ err_unlock: return ret; } -asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val, - struct compat_timespec __user *utime, u32 __user *uaddr2, - u32 val3) +COMPAT_SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val, + struct compat_timespec __user *, utime, u32 __user *, uaddr2, + u32, val3) { struct timespec ts; ktime_t t, *tp = NULL;