Skip to contentSkip to conversation

improve sys_personality for compat architectures

PATCH In review Christoph Hellwig 5 participants 10 messages Feb 1, 2010 – Mar 9, 2010

Subsystems (7)
x86 PowerPC MIPS s390 +3 more subsystems
lore
Share & tools
lore
#0 lore raw

PATCH 4/6 improve sys_personality for compat architectures

On an architecture that supports 32-bit compat we need to claim to be PER_LINUX even if we're actually PER_LINUX32. Instead of duplicating the code for that in all architectures and having various bugs in the handling of personality flags do it once and right in the common code.

arch/ia64/ia32/ia32_entry.S +1 -1
Index: linux-2.6/arch/ia64/ia32/ia32_entry.S
===================================================================
@@ -314,7 +314,7 @@ ia32_syscall_table:
 	data8 sys_fchdir
 	data8 sys_ni_syscall	/* sys_bdflush */
 	data8 sys_sysfs		/* 135 */
-	data8 sys32_personality
+	data8 sys_personality
 	data8 sys_ni_syscall	  /* for afs_syscall */
 	data8 sys_setfsuid	/* 16-bit version */
 	data8 sys_setfsgid	/* 16-bit version */
arch/ia64/ia32/sys_ia32.c +0 -13
Index: linux-2.6/arch/ia64/ia32/sys_ia32.c
===================================================================
@@ -1867,19 +1867,6 @@ sys32_sendfile (int out_fd, int in_fd, i
 	return ret;
 }
 
-asmlinkage long
-sys32_personality (unsigned int personality)
-{
-	long ret;
-
-	if (current->personality == PER_LINUX32 && personality == PER_LINUX)
-		personality = PER_LINUX32;
-	ret = sys_personality(personality);
-	if (ret == PER_LINUX32)
-		ret = PER_LINUX;
-	return ret;
-}
-
 asmlinkage unsigned long
 sys32_brk (unsigned int brk)
 {
arch/mips/kernel/linux32.c +0 -13
Index: linux-2.6/arch/mips/kernel/linux32.c
===================================================================
@@ -266,19 +266,6 @@ SYSCALL_DEFINE1(32_newuname, struct new_
 	return ret;
 }
 
-SYSCALL_DEFINE1(32_personality, unsigned long, personality)
-{
-	int ret;
-	personality &= 0xffffffff;
-	if (personality(current->personality) == PER_LINUX32 &&
-	    personality == PER_LINUX)
-		personality = PER_LINUX32;
-	ret = sys_personality(personality);
-	if (ret == PER_LINUX32)
-		ret = PER_LINUX;
-	return ret;
-}
-
 SYSCALL_DEFINE4(32_sendfile, long, out_fd, long, in_fd,
 	compat_off_t __user *, offset, s32, count)
 {
arch/mips/kernel/scall64-n32.S +1 -1
Index: linux-2.6/arch/mips/kernel/scall64-n32.S
===================================================================
@@ -252,7 +252,7 @@ EXPORT(sysn32_call_table)
 	PTR	sys32_sigaltstack
 	PTR	compat_sys_utime		/* 6130 */
 	PTR	sys_mknod
-	PTR	sys_32_personality
+	PTR	sys_personality
 	PTR	compat_sys_ustat
 	PTR	compat_sys_statfs
 	PTR	compat_sys_fstatfs		/* 6135 */
arch/mips/kernel/scall64-o32.S +1 -1
Index: linux-2.6/arch/mips/kernel/scall64-o32.S
===================================================================
@@ -339,7 +339,7 @@ sys_call_table:
 	PTR	sys_fchdir
 	PTR	sys_bdflush
 	PTR	sys_sysfs			/* 4135 */
-	PTR	sys_32_personality
+	PTR	sys_personality
 	PTR	sys_ni_syscall	 		/* for afs_syscall */
 	PTR	sys_setfsuid
 	PTR	sys_setfsgid
arch/parisc/kernel/sys_parisc.c +0 -15
Index: linux-2.6/arch/parisc/kernel/sys_parisc.c
===================================================================
@@ -220,21 +220,6 @@ asmlinkage int sys_free_hugepages(unsign
 	return -EINVAL;
 }
 
-long parisc_personality(unsigned long personality)
-{
-	long err;
-
-	if (personality(current->personality) == PER_LINUX32
-	    && personality == PER_LINUX)
-		personality = PER_LINUX32;
-
-	err = sys_personality(personality);
-	if (err == PER_LINUX32)
-		err = PER_LINUX;
-
-	return err;
-}
-
 long parisc_newuname(struct new_utsname __user *name)
 {
 	int err = sys_newuname(name);
arch/parisc/kernel/syscall_table.S +1 -1
Index: linux-2.6/arch/parisc/kernel/syscall_table.S
===================================================================
@@ -216,7 +216,7 @@
 	ENTRY_SAME(fchdir)
 	ENTRY_SAME(bdflush)
 	ENTRY_SAME(sysfs)		/* 135 */
-	ENTRY_OURS(personality)
+	ENTRY_SAME(personality)
 	ENTRY_SAME(ni_syscall)	/* for afs_syscall */
 	ENTRY_SAME(setfsuid)
 	ENTRY_SAME(setfsgid)
arch/powerpc/include/asm/syscalls.h +0 -1
Index: linux-2.6/arch/powerpc/include/asm/syscalls.h
===================================================================
@@ -35,7 +35,6 @@ asmlinkage long sys_pipe2(int __user *fi
 asmlinkage long sys_rt_sigaction(int sig,
 		const struct sigaction __user *act,
 		struct sigaction __user *oact, size_t sigsetsize);
-asmlinkage long ppc64_personality(unsigned long personality);
 asmlinkage int ppc_rtas(struct rtas_args __user *uargs);
 asmlinkage time_t sys64_time(time_t __user * tloc);
 asmlinkage long ppc_newuname(struct new_utsname __user * name);
arch/powerpc/include/asm/systbl.h +1 -1
Index: linux-2.6/arch/powerpc/include/asm/systbl.h
===================================================================
@@ -139,7 +139,7 @@ COMPAT_SYS_SPU(getpgid)
 SYSCALL_SPU(fchdir)
 SYSCALL_SPU(bdflush)
 COMPAT_SYS(sysfs)
-SYSX_SPU(ppc64_personality,ppc64_personality,sys_personality)
+SYSCALL_SPU(personality)
 SYSCALL(ni_syscall)
 SYSCALL_SPU(setfsuid)
 SYSCALL_SPU(setfsgid)
arch/powerpc/kernel/syscalls.c +0 -15
Index: linux-2.6/arch/powerpc/kernel/syscalls.c
===================================================================
@@ -102,21 +102,6 @@ ppc_select(int n, fd_set __user *inp, fd
 #endif
 
 #ifdef CONFIG_PPC64
-long ppc64_personality(unsigned long personality)
-{
-	long ret;
-
-	if (personality(current->personality) == PER_LINUX32
-	    && personality == PER_LINUX)
-		personality = PER_LINUX32;
-	ret = sys_personality(personality);
-	if (ret == PER_LINUX32)
-		ret = PER_LINUX;
-	return ret;
-}
-#endif
-
-#ifdef CONFIG_PPC64
 #define OVERRIDE_MACHINE    (personality(current->personality) == PER_LINUX32)
 #else
 #define OVERRIDE_MACHINE    0
arch/s390/kernel/compat_wrapper.S +1 -1
Index: linux-2.6/arch/s390/kernel/compat_wrapper.S
===================================================================
@@ -615,7 +615,7 @@ sys32_sysfs_wrapper:
 	.globl	sys32_personality_wrapper
 sys32_personality_wrapper:
 	llgfr	%r2,%r2			# unsigned long
-	jg	sys_s390_personality	# branch to system call
+	jg	sys_personality		# branch to system call
 
 	.globl	sys32_setfsuid16_wrapper
 sys32_setfsuid16_wrapper:
arch/s390/kernel/entry.h +0 -1
Index: linux-2.6/arch/s390/kernel/entry.h
===================================================================
@@ -33,7 +33,6 @@ long sys_mmap2(struct s390_mmap_arg_stru
 long sys_s390_ipc(uint call, int first, unsigned long second,
 	     unsigned long third, void __user *ptr);
 long sys_s390_newuname(struct new_utsname __user *name);
-long sys_s390_personality(unsigned long personality);
 long sys_s390_fadvise64(int fd, u32 offset_high, u32 offset_low,
 		    size_t len, int advice);
 long sys_s390_fadvise64_64(struct fadvise64_64_args __user *args);
arch/s390/kernel/sys_s390.c +0 -13
Index: linux-2.6/arch/s390/kernel/sys_s390.c
===================================================================
@@ -141,19 +141,6 @@ SYSCALL_DEFINE1(s390_newuname, struct ne
 	}
 	return ret;
 }
-
-SYSCALL_DEFINE1(s390_personality, unsigned long, personality)
-{
-	int ret;
-
-	if (current->personality == PER_LINUX32 && personality == PER_LINUX)
-		personality = PER_LINUX32;
-	ret = sys_personality(personality);
-	if (ret == PER_LINUX32)
-		ret = PER_LINUX;
-
-	return ret;
-}
 #endif /* CONFIG_64BIT */
 
 /*
arch/s390/kernel/syscalls.S +1 -1
Index: linux-2.6/arch/s390/kernel/syscalls.S
===================================================================
@@ -144,7 +144,7 @@ SYSCALL(sys_getpgid,sys_getpgid,sys32_ge
 SYSCALL(sys_fchdir,sys_fchdir,sys32_fchdir_wrapper)
 SYSCALL(sys_bdflush,sys_bdflush,sys32_bdflush_wrapper)
 SYSCALL(sys_sysfs,sys_sysfs,sys32_sysfs_wrapper)		/* 135 */
-SYSCALL(sys_personality,sys_s390_personality,sys32_personality_wrapper)
+SYSCALL(sys_personality,sys_personality,sys32_personality_wrapper)
 NI_SYSCALL							/* for afs_syscall */
 SYSCALL(sys_setfsuid16,sys_ni_syscall,sys32_setfsuid16_wrapper)	/* old setfsuid16 syscall */
 SYSCALL(sys_setfsgid16,sys_ni_syscall,sys32_setfsgid16_wrapper)	/* old setfsgid16 syscall */
arch/sparc/kernel/sys_sparc_64.c +0 -14
Index: linux-2.6/arch/sparc/kernel/sys_sparc_64.c
===================================================================
@@ -522,20 +522,6 @@ SYSCALL_DEFINE1(sparc64_newuname, struct
 	return ret;
 }
 
-SYSCALL_DEFINE1(sparc64_personality, unsigned long, personality)
-{
-	int ret;
-
-	if (current->personality == PER_LINUX32 &&
-	    personality == PER_LINUX)
-		personality = PER_LINUX32;
-	ret = sys_personality(personality);
-	if (ret == PER_LINUX32)
-		ret = PER_LINUX;
-
-	return ret;
-}
-
 int sparc_mmap_check(unsigned long addr, unsigned long len)
 {
 	if (test_thread_flag(TIF_32BIT)) {
arch/sparc/kernel/systbls.h +0 -1
Index: linux-2.6/arch/sparc/kernel/systbls.h
===================================================================
@@ -15,7 +15,6 @@ extern asmlinkage long sys_sparc_ipc(uns
 			       unsigned long third,
 			       void __user *ptr, long fifth);
 extern asmlinkage long sparc64_newuname(struct new_utsname __user *name);
-extern asmlinkage long sparc64_personality(unsigned long personality);
 extern asmlinkage long sys64_munmap(unsigned long addr, size_t len);
 extern asmlinkage unsigned long sys64_mremap(unsigned long addr,
 					     unsigned long old_len,
arch/sparc/kernel/systbls_64.S +2 -2
Index: linux-2.6/arch/sparc/kernel/systbls_64.S
===================================================================
@@ -56,7 +56,7 @@ sys_call_table32:
 	.word sys_setsid, sys_fchdir, sys32_fgetxattr, sys_listxattr, sys_llistxattr
 /*180*/	.word sys32_flistxattr, sys_removexattr, sys_lremovexattr, compat_sys_sigpending, sys_ni_syscall
 	.word sys32_setpgid, sys32_fremovexattr, sys32_tkill, sys32_exit_group, sys_sparc64_newuname
-/*190*/	.word sys32_init_module, sys_sparc64_personality, sys_remap_file_pages, sys32_epoll_create, sys32_epoll_ctl
+/*190*/	.word sys32_init_module, sys_personality, sys_remap_file_pages, sys32_epoll_create, sys32_epoll_ctl
 	.word sys32_epoll_wait, sys32_ioprio_set, sys_getppid, sys32_sigaction, sys_sgetmask
 /*200*/	.word sys32_ssetmask, sys_sigsuspend, compat_sys_newlstat, sys_uselib, compat_sys_old_readdir
 	.word sys32_readahead, sys32_socketcall, sys32_syslog, sys32_lookup_dcookie, sys32_fadvise64
@@ -131,7 +131,7 @@ sys_call_table:
 	.word sys_setsid, sys_fchdir, sys_fgetxattr, sys_listxattr, sys_llistxattr
 /*180*/	.word sys_flistxattr, sys_removexattr, sys_lremovexattr, sys_nis_syscall, sys_ni_syscall
 	.word sys_setpgid, sys_fremovexattr, sys_tkill, sys_exit_group, sys_sparc64_newuname
-/*190*/	.word sys_init_module, sys_sparc64_personality, sys_remap_file_pages, sys_epoll_create, sys_epoll_ctl
+/*190*/	.word sys_init_module, sys_personality, sys_remap_file_pages, sys_epoll_create, sys_epoll_ctl
 	.word sys_epoll_wait, sys_ioprio_set, sys_getppid, sys_nis_syscall, sys_sgetmask
 /*200*/	.word sys_ssetmask, sys_nis_syscall, sys_newlstat, sys_uselib, sys_nis_syscall
 	.word sys_readahead, sys_socketcall, sys_syslog, sys_lookup_dcookie, sys_fadvise64
arch/x86/ia32/sys_ia32.c +0 -14
Index: linux-2.6/arch/x86/ia32/sys_ia32.c
===================================================================
@@ -414,20 +414,6 @@ asmlinkage long sys32_pwrite(unsigned in
 			  ((loff_t)AA(poshi) << 32) | AA(poslo));
 }
 
-
-asmlinkage long sys32_personality(unsigned long personality)
-{
-	int ret;
-
-	if (personality(current->personality) == PER_LINUX32 &&
-		personality == PER_LINUX)
-		personality = PER_LINUX32;
-	ret = sys_personality(personality);
-	if (ret == PER_LINUX32)
-		ret = PER_LINUX;
-	return ret;
-}
-
 asmlinkage long sys32_sendfile(int out_fd, int in_fd,
 			       compat_off_t __user *offset, s32 count)
 {
arch/x86/include/asm/sys_ia32.h +0 -1
Index: linux-2.6/arch/x86/include/asm/sys_ia32.h
===================================================================
@@ -51,7 +51,6 @@ asmlinkage long sys32_rt_sigqueueinfo(in
 asmlinkage long sys32_pread(unsigned int, char __user *, u32, u32, u32);
 asmlinkage long sys32_pwrite(unsigned int, char __user *, u32, u32, u32);
 
-asmlinkage long sys32_personality(unsigned long);
 asmlinkage long sys32_sendfile(int, int, compat_off_t __user *, s32);
 
 struct oldold_utsname;
kernel/exec_domain.c +10 -1
Index: linux-2.6/kernel/exec_domain.c
===================================================================
@@ -188,17 +188,26 @@ static int __init proc_execdomains_init(
 module_init(proc_execdomains_init);
 #endif
 
+/*
+ * Get/set the personality.
+ *
+ * Note that we simply return PER_LINUX even if we actually have a 32-bit
+ * task (PER_LINUX32) as this is expected by 32-bit userland.
+ */
 SYSCALL_DEFINE1(personality, u_long, personality)
 {
 	u_long old = current->personality;
 
 	if (personality != 0xffffffff) {
+		if (personality(old) == PER_LINUX32 &&
+		    personality == PER_LINUX)
+			personality = PER_LINUX32;
 		set_personality(personality);
 		if (current->personality != personality)
 			return -EINVAL;
 	}
 
-	return (long)old;
+	return (long)(old == PER_LINUX32 ? PER_LINUX : old);
 }
 
 
maintainer
#1 lore raw
Quoted 12 lines · Christoph Hellwig
kernel/exec_domain.c +7 -0
+/*
+ * Get/set the personality.
+ *
+ * Note that we simply return PER_LINUX even if we actually have a 32-bit
+ * task (PER_LINUX32) as this is expected by 32-bit userland.
+ */
 SYSCALL_DEFINE1(personality, u_long, personality)
 {
        u_long old = current->personality;
 
        if (personality != 0xffffffff) {
+               if (personality(old) == PER_LINUX32 &&

kernel/exec_domain.c static int __init proc_execdomains_init( · line 211

Quoted 10 lines
kernel/exec_domain.c +3 -1
+                   personality == PER_LINUX)
+                       personality = PER_LINUX32;
                set_personality(personality);
                if (current->personality != personality)
                        return -EINVAL;
        }
 
-       return (long)old;
+       return (long)(old == PER_LINUX32 ? PER_LINUX : old);
 }

What does this do for a native 64 bit process setting PER_LINUX32? It looks to me like it could never set it back to the original value, or am I missing something here?

It's what the arch specific code does already, but it seems a bit strange anyway.

Arnd

#2 lore raw
Quoted 10 lines · Arnd Bergmann

kernel/exec_domain.c static int __init proc_execdomains_init( · line 211

Quoted 3 lines
kernel/exec_domain.c +1 -1
-       return (long)old;
+       return (long)(old == PER_LINUX32 ? PER_LINUX : old);
 }

What does this do for a native 64 bit process setting PER_LINUX32? It looks to me like it could never set it back to the original value, or am I missing something here?

It's what the arch specific code does already, but it seems a bit strange anyway.

Indeed, this prevents a process from going back to 64bit. And while the setarch tool in util-linux also allows going back to 64-bit the typical use case seems to be going into 32-bit mode.

But if the consensus is that we should fix this properly I can replace the patch with one introducing a compat_sys_personality which only gets used for compat tasks.

maintainer
#3 lore raw
Quoted 10 lines · On Tuesday 02 February 2010 17:31:45 Christoph Hellwig wrote:
Quoted 8 lines · Arnd Bergmann

kernel/exec_domain.c static int __init proc_execdomains_init( · line 211

Quoted 3 lines
kernel/exec_domain.c +1 -1
-       return (long)old;
+       return (long)(old == PER_LINUX32 ? PER_LINUX : old);
 }

What does this do for a native 64 bit process setting PER_LINUX32? It looks to me like it could never set it back to the original value, or am I missing something here?

Quoted 6 lines
Quoted 2 lines

It's what the arch specific code does already, but it seems a bit strange anyway.

Indeed, this prevents a process from going back to 64bit. And while the setarch tool in util-linux also allows going back to 64-bit the typical use case seems to be going into 32-bit mode.

It appears that setarch can only go back into 64 bit mode on x86 because that does not wire up sys32_personality at all -- it always uses the regular sys_personality, which allows going both ways.

Your patch changes the behavior on x86 to also make the change to PER_LINUX32 permanent, which seems consistent but possibly not what people want. I still wonder if anything other than setarch actually calls personality() to set PER_LINUX32 and what the real intention of sys32_personality is. From what I can tell here, it may be best to kill the special handling of PER_LINUX32 entirely and make everyone use the existing sys_personality.

Quoted 3 lines

But if the consensus is that we should fix this properly I can replace the patch with one introducing a compat_sys_personality which only gets used for compat tasks.

Right now, sparc64 and powerpc64 use sys32_personality for both native and compat tasks, x86 never uses it and all others use it only for compat tasks. That seems more sensible if we keep this function at all.

Arnd

maintainer
#4 lore raw

From: Arnd Bergmann <[email protected]>
Date: Wed, 3 Feb 2010 18:06:27 +0100

Quoted 8 lines
Quoted 3 lines

But if the consensus is that we should fix this properly I can replace the patch with one introducing a compat_sys_personality which only gets used for compat tasks.

Right now, sparc64 and powerpc64 use sys32_personality for both native and compat tasks, x86 never uses it and all others use it only for compat tasks. That seems more sensible if we keep this function at all.

If it only gets used for compat tasks, you can only switch in one direction. I think it needs to be handled for both compat and non-compat tasks, in order to allow for that.

That's why powerpc64 and sparc64 do things the way they do, I am pretty sure.

#5 lore raw
Quoted 7 lines · David Miller

From: Arnd Bergmann <[email protected]>
Date: Wed, 3 Feb 2010 18:06:27 +0100

Quoted 4 lines
Quoted 3 lines

But if the consensus is that we should fix this properly I can replace the patch with one introducing a compat_sys_personality which only gets used for compat tasks.

Quoted 8 lines
Quoted 4 lines

Right now, sparc64 and powerpc64 use sys32_personality for both native and compat tasks, x86 never uses it and all others use it only for compat tasks. That seems more sensible if we keep this function at all.

If it only gets used for compat tasks, you can only switch in one direction. I think it needs to be handled for both compat and non-compat tasks, in order to allow for that.

That seems odd... with the wrapper you can ever only switch in one direction, and if you use it for non-compat tasks you wouldn't be ever to switch back, period.

Quoted 2 lines

That's why powerpc64 and sparc64 do things the way they do, I am pretty sure.

As far as I can tell, the ppc64 and sparc64 implementations would seem to be trapdoors from which no return is possible. That's a pretty defensible position in some ways -- it mimics the 32-bit machine even down to the personality() syscall -- but it definitely has disadvantages.

The x86 method of simply not bothering doesn't seem to have caused problems -- our compat (and noncompat) tasks happily return PER_LINUX32 if that is the mode and we don't seem to have had complaints with it. If userspace ever had an issue with it -- and they might have, at one point in history libc used to call personality() during startup, which it doesn't seem to anymore -- they presumably have worked through it.

As such, I'm more than a little reluctant to change the current behavior.

-hpa

maintainer
#6 lore raw
Quoted 8 lines · H. Peter Anvin

The x86 method of simply not bothering doesn't seem to have caused problems -- our compat (and noncompat) tasks happily return PER_LINUX32 if that is the mode and we don't seem to have had complaints with it. If userspace ever had an issue with it -- and they might have, at one point in history libc used to call personality() during startup, which it doesn't seem to anymore -- they presumably have worked through it.

As such, I'm more than a little reluctant to change the current behavior.

I think it would be better to have a consistent behavior across architectures, so /something/ should be changed. On x86, the unused sys32_personality function can probable just be removed, if nothing else.

If we can get everyone to agree with the x86 way of handling this, we can also remove all the special cases for sys32_personality in the other architectures.

Arnd

#7 lore raw
Quoted 8 lines · Arnd Bergmann

I think it would be better to have a consistent behavior across architectures, so /something/ should be changed. On x86, the unused sys32_personality function can probable just be removed, if nothing else.

If we can get everyone to agree with the x86 way of handling this, we can also remove all the special cases for sys32_personality in the other architectures.

Yes, this should be common over architectures. Anyway, I expect there to be some discussion until we've reached that point, so for now I'll rebase patches 5 and 6 to not require this patch, and will send one to kill the unused x86 implementation. Whatever is the final outcome can be implemented on top.

#8 lore raw
Quoted 10 lines · On Thu, 4 Feb 2010 17:00:57 +0100 Christoph Hellwig <[email protected]> wrote:
Quoted 8 lines · Arnd Bergmann

I think it would be better to have a consistent behavior across architectures, so /something/ should be changed. On x86, the unused sys32_personality function can probable just be removed, if nothing else.

If we can get everyone to agree with the x86 way of handling this, we can also remove all the special cases for sys32_personality in the other architectures.

Quoted 6 lines

Yes, this should be common over architectures. Anyway, I expect there to be some discussion until we've reached that point, so for now I'll rebase patches 5 and 6 to not require this patch, and will send one to kill the unused x86 implementation. Whatever is the final outcome can be implemented on top.

This never happened?

#9 lore raw
Quoted 8 lines · Andrew Morton
Quoted 6 lines

Yes, this should be common over architectures. Anyway, I expect there to be some discussion until we've reached that point, so for now I'll rebase patches 5 and 6 to not require this patch, and will send one to kill the unused x86 implementation. Whatever is the final outcome can be implemented on top.

This never happened?

Seems like no one cared enough to discuss this issue in detail. I'll start a fresh discussion once we got the other patches in and I have a clean sheet to work against.