Futexes are broken on (at least) MIPS64/n32

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;

[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]