2015-11-30 21:54:39

by Dmitry V. Levin

[permalink] [raw]
Subject: [PATCH] x86/signal: fix restart_syscall number for x32 tasks

When restarting a syscall with regs->ax == -ERESTART_RESTARTBLOCK,
regs->ax is assigned to a restart_syscall number. For x32 tasks,
this syscall number must have __X32_SYSCALL_BIT set, otherwise it
will be an x86_64 syscall number instead of a valid x32 syscall number.

Reported-by: strace/tests/restart_syscall.test
Reported-and-tested-by: Elvira Khabirova <[email protected]>
Signed-off-by: Dmitry V. Levin <[email protected]>
Cc: [email protected]
---
arch/x86/kernel/signal.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index b7ffb7c..cb6282c 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -690,12 +690,15 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
signal_setup_done(failed, ksig, stepping);
}

-#ifdef CONFIG_X86_32
-#define NR_restart_syscall __NR_restart_syscall
-#else /* !CONFIG_X86_32 */
-#define NR_restart_syscall \
- test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall : __NR_restart_syscall
-#endif /* CONFIG_X86_32 */
+static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
+{
+#if defined(CONFIG_X86_32) || !defined(CONFIG_X86_64)
+ return __NR_restart_syscall;
+#else /* !CONFIG_X86_32 && CONFIG_X86_64 */
+ return test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall :
+ __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
+#endif /* CONFIG_X86_32 || !CONFIG_X86_64 */
+}

/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
@@ -724,7 +727,7 @@ void do_signal(struct pt_regs *regs)
break;

case -ERESTART_RESTARTBLOCK:
- regs->ax = NR_restart_syscall;
+ regs->ax = get_nr_restart_syscall(regs);
regs->ip -= 2;
break;
}
--
ldv


Subject: [tip:x86/urgent] x86/signal: Fix restart_syscall number for x32 tasks

Commit-ID: 22eab1108781eff09961ae7001704f7bd8fb1dce
Gitweb: http://git.kernel.org/tip/22eab1108781eff09961ae7001704f7bd8fb1dce
Author: Dmitry V. Levin <[email protected]>
AuthorDate: Tue, 1 Dec 2015 00:54:36 +0300
Committer: Thomas Gleixner <[email protected]>
CommitDate: Sat, 5 Dec 2015 18:52:14 +0100

x86/signal: Fix restart_syscall number for x32 tasks

When restarting a syscall with regs->ax == -ERESTART_RESTARTBLOCK,
regs->ax is assigned to a restart_syscall number. For x32 tasks, this
syscall number must have __X32_SYSCALL_BIT set, otherwise it will be
an x86_64 syscall number instead of a valid x32 syscall number. This
issue has been there since the introduction of x32.

Reported-by: strace/tests/restart_syscall.test
Reported-and-tested-by: Elvira Khabirova <[email protected]>
Signed-off-by: Dmitry V. Levin <[email protected]>
Cc: Elvira Khabirova <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/signal.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index b7ffb7c..cb6282c 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -690,12 +690,15 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
signal_setup_done(failed, ksig, stepping);
}

-#ifdef CONFIG_X86_32
-#define NR_restart_syscall __NR_restart_syscall
-#else /* !CONFIG_X86_32 */
-#define NR_restart_syscall \
- test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall : __NR_restart_syscall
-#endif /* CONFIG_X86_32 */
+static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
+{
+#if defined(CONFIG_X86_32) || !defined(CONFIG_X86_64)
+ return __NR_restart_syscall;
+#else /* !CONFIG_X86_32 && CONFIG_X86_64 */
+ return test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall :
+ __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
+#endif /* CONFIG_X86_32 || !CONFIG_X86_64 */
+}

/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
@@ -724,7 +727,7 @@ void do_signal(struct pt_regs *regs)
break;

case -ERESTART_RESTARTBLOCK:
- regs->ax = NR_restart_syscall;
+ regs->ax = get_nr_restart_syscall(regs);
regs->ip -= 2;
break;
}

2015-12-07 23:22:27

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH] x86/signal: fix restart_syscall number for x32 tasks

[not real reply because I'm using a bad internet connection right now
and I'm not set up with my usual Gmane reply hack right now]

The new code is (whitespace-damaged):

static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
{
#if defined(CONFIG_X86_32) || !defined(CONFIG_X86_64)
return __NR_restart_syscall;
#else /* !CONFIG_X86_32 && CONFIG_X86_64 */
return test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall :
__NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
#endif /* CONFIG_X86_32 || !CONFIG_X86_64 */
}

This is IMO awful. This use of TIF_IA32 is wrong, and this is
otherwise gross. Can we do it for real:

if (is_ia32_task())
return __NR_ia32_restart_syscall;
else
return __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
/* preserve x32 bit */

I'd send the patch myself, but you apparently have a good test case
for this, and I don't.

(this isn't a regression, and I'm not suggesting any change for 4.4 or
for stable. But for 4.5, can we do it right, please?)

And yes, I'll send a patch to rename is_ia32_task, but that's orthogonal.

--Andy

P.S. I'm still hoping to kill TIF_IA32 entirely some time soon.

2015-12-13 03:44:44

by Dmitry V. Levin

[permalink] [raw]
Subject: Re: [PATCH] x86/signal: fix restart_syscall number for x32 tasks

On Mon, Dec 07, 2015 at 03:22:06PM -0800, Andy Lutomirski wrote:
> [not real reply because I'm using a bad internet connection right now
> and I'm not set up with my usual Gmane reply hack right now]
>
> The new code is (whitespace-damaged):
>
> static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
> {
> #if defined(CONFIG_X86_32) || !defined(CONFIG_X86_64)
> return __NR_restart_syscall;
> #else /* !CONFIG_X86_32 && CONFIG_X86_64 */
> return test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall :
> __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
> #endif /* CONFIG_X86_32 || !CONFIG_X86_64 */
> }
>
> This is IMO awful. This use of TIF_IA32 is wrong, and this is
> otherwise gross. Can we do it for real:
>
> if (is_ia32_task())
> return __NR_ia32_restart_syscall;
> else
> return __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
> /* preserve x32 bit */
>
> I'd send the patch myself, but you apparently have a good test case
> for this, and I don't.

Unfortunately, this won't compile on CONFIG_X86_32 because
__NR_ia32_restart_syscall is defined for CONFIG_X86_64 only.

Something like this should work:

static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
{
#ifdef CONFIG_X86_64
if (is_ia32_task())
return __NR_ia32_restart_syscall;
# ifdef CONFIG_X86_X32_ABI
if (regs->orig_ax & __X32_SYSCALL_BIT)
return __NR_restart_syscall | __X32_SYSCALL_BIT;
# endif
#endif
return __NR_restart_syscall;
}

I don't see any way to avoid ifdefs here, sorry.


--
ldv

2015-12-17 20:27:39

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH] x86/signal: fix restart_syscall number for x32 tasks

On Sat, Dec 12, 2015 at 7:44 PM, Dmitry V. Levin <[email protected]> wrote:
> On Mon, Dec 07, 2015 at 03:22:06PM -0800, Andy Lutomirski wrote:
>> [not real reply because I'm using a bad internet connection right now
>> and I'm not set up with my usual Gmane reply hack right now]
>>
>> The new code is (whitespace-damaged):
>>
>> static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
>> {
>> #if defined(CONFIG_X86_32) || !defined(CONFIG_X86_64)
>> return __NR_restart_syscall;
>> #else /* !CONFIG_X86_32 && CONFIG_X86_64 */
>> return test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall :
>> __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
>> #endif /* CONFIG_X86_32 || !CONFIG_X86_64 */
>> }
>>
>> This is IMO awful. This use of TIF_IA32 is wrong, and this is
>> otherwise gross. Can we do it for real:
>>
>> if (is_ia32_task())
>> return __NR_ia32_restart_syscall;
>> else
>> return __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
>> /* preserve x32 bit */
>>
>> I'd send the patch myself, but you apparently have a good test case
>> for this, and I don't.
>
> Unfortunately, this won't compile on CONFIG_X86_32 because
> __NR_ia32_restart_syscall is defined for CONFIG_X86_64 only.
>
> Something like this should work:
>
> static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
> {
> #ifdef CONFIG_X86_64
> if (is_ia32_task())
> return __NR_ia32_restart_syscall;
> # ifdef CONFIG_X86_X32_ABI
> if (regs->orig_ax & __X32_SYSCALL_BIT)
> return __NR_restart_syscall | __X32_SYSCALL_BIT;
> # endif
> #endif
> return __NR_restart_syscall;
> }

Looks good to me. Want to send a patch?

--Andy

2015-12-18 23:37:39

by Dmitry V. Levin

[permalink] [raw]
Subject: [PATCH] x86/signal: Cleanup get_nr_restart_syscall

Check for TS_COMPAT instead of TIF_IA32 to distinguish ia32 tasks
from 64-bit tasks.
Check for __X32_SYSCALL_BIT only if CONFIG_X86_X32_ABI is defined.

Signed-off-by: Dmitry V. Levin <[email protected]>
Cc: Elvira Khabirova <[email protected]>
Cc: Andy Lutomirski <[email protected]>
---
arch/x86/kernel/signal.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index cb6282c..ff7dedc 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -692,12 +692,15 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)

static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
{
-#if defined(CONFIG_X86_32) || !defined(CONFIG_X86_64)
+#ifdef CONFIG_X86_64
+ if (is_ia32_task())
+ return __NR_ia32_restart_syscall;
+# ifdef CONFIG_X86_X32_ABI
+ if (regs->orig_ax & __X32_SYSCALL_BIT)
+ return __NR_restart_syscall | __X32_SYSCALL_BIT;
+# endif
+#endif
return __NR_restart_syscall;
-#else /* !CONFIG_X86_32 && CONFIG_X86_64 */
- return test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall :
- __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
-#endif /* CONFIG_X86_32 || !CONFIG_X86_64 */
}

/*
--
ldv

2015-12-19 06:35:17

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH] x86/signal: Cleanup get_nr_restart_syscall

On 12/18/15 15:37, Dmitry V. Levin wrote:
> Check for TS_COMPAT instead of TIF_IA32 to distinguish ia32 tasks
> from 64-bit tasks.
> Check for __X32_SYSCALL_BIT only if CONFIG_X86_X32_ABI is defined.
>
> Signed-off-by: Dmitry V. Levin <[email protected]>
> Cc: Elvira Khabirova <[email protected]>
> Cc: Andy Lutomirski <[email protected]>
> ---
> arch/x86/kernel/signal.c | 13 ++++++++-----
> 1 file changed, 8 insertions(+), 5 deletions(-)
>
> diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
> index cb6282c..ff7dedc 100644
> --- a/arch/x86/kernel/signal.c
> +++ b/arch/x86/kernel/signal.c
> @@ -692,12 +692,15 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
>
> static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
> {
> -#if defined(CONFIG_X86_32) || !defined(CONFIG_X86_64)
> +#ifdef CONFIG_X86_64
> + if (is_ia32_task())
> + return __NR_ia32_restart_syscall;
> +# ifdef CONFIG_X86_X32_ABI
> + if (regs->orig_ax & __X32_SYSCALL_BIT)
> + return __NR_restart_syscall | __X32_SYSCALL_BIT;
> +# endif
> +#endif
> return __NR_restart_syscall;
> -#else /* !CONFIG_X86_32 && CONFIG_X86_64 */
> - return test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall :
> - __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
> -#endif /* CONFIG_X86_32 || !CONFIG_X86_64 */
> }
>
> /*
>

I bet you actually made the code slower.

-hpa

2015-12-19 14:43:37

by Dmitry V. Levin

[permalink] [raw]
Subject: [PATCH v2] x86/signal: Cleanup get_nr_restart_syscall

Check for TS_COMPAT instead of TIF_IA32 to distinguish ia32 tasks
from 64-bit tasks.
Check for __X32_SYSCALL_BIT iff CONFIG_X86_X32_ABI is defined.

Signed-off-by: Dmitry V. Levin <[email protected]>
Cc: Elvira Khabirova <[email protected]>
Cc: Andy Lutomirski <[email protected]>
---
v2: reintroduced __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT)

arch/x86/kernel/signal.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index cb6282c..c07ff5d 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -692,12 +692,15 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)

static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
{
-#if defined(CONFIG_X86_32) || !defined(CONFIG_X86_64)
+#ifdef CONFIG_X86_64
+ if (is_ia32_task())
+ return __NR_ia32_restart_syscall;
+#endif
+#ifdef CONFIG_X86_X32_ABI
+ return __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
+#else
return __NR_restart_syscall;
-#else /* !CONFIG_X86_32 && CONFIG_X86_64 */
- return test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall :
- __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
-#endif /* CONFIG_X86_32 || !CONFIG_X86_64 */
+#endif
}

/*
--
ldv

2015-12-19 20:48:33

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v2] x86/signal: Cleanup get_nr_restart_syscall

On Sat, Dec 19, 2015 at 6:43 AM, Dmitry V. Levin <[email protected]> wrote:
> Check for TS_COMPAT instead of TIF_IA32 to distinguish ia32 tasks
> from 64-bit tasks.
> Check for __X32_SYSCALL_BIT iff CONFIG_X86_X32_ABI is defined.

LGTM.

--Andy