2022-05-09 15:25:10

by Francis Laniel

[permalink] [raw]
Subject: [RFC PATCH v1 0/1] Call forget_syscall() if different than execve*()

Hi.


First, I hope you are fine and the same for your relatives.

With this contribution, I enabled using syscalls:sys_exit_execve and
syscalls:sys_exit_execveat as tracepoints on arm64.
Indeed, before this contribution, the above tracepoint would not print their
information as syscall number was set to -1 by calling forget_syscall().

Now, forget_syscall() is called only if previous syscall number was different
than __NR_execve and __NR_execveat.
I tested it by compiling a kernel for arm64 and running it within a VM:
# Perf was compiled with linux kernel source.
root@vm-arm64:~# perf record -ag -e 'syscalls:sys_exit_execve' -e 'syscalls:sys_enter_execve' &
[1] 263
root@vm-arm64:~# ls
perf.data share
root@vm-arm64:~# fg
perf record -ag -e 'syscalls:sys_exit_execve' -e 'syscalls:sys_enter_execve'
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.061 MB perf.data (2 samples) ]
root@vm-arm64:~# perf script
bash 264 [000] 66.220187: syscalls:sys_enter_execve: filename: 0xaaab05d9d
...
# Below line does not appear with this patch.
ls 264 [000] 66.226848: syscalls:sys_exit_execve: 0x0
...

Nonetheless, this contribution is not perfect, hence I marked it as RFC.
First, I am not really sure if this is safe to not call forget_syscall() all the
time, even though I did not have problem while testing it.
Then, by including <asm-generic/unistd.h> to the modified file I ended with
some warnings at compile time:

So, if you see any way to improve this contribution, feel free to share!

Francis Laniel (1):
arm64: Forget syscall if different from execve*()

arch/arm64/include/asm/processor.h | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)


Best regards and thank you in advance.
--
2.25.1



2022-05-09 15:25:13

by Francis Laniel

[permalink] [raw]
Subject: [RFC PATCH v1 1/1] arm64: Forget syscall if different from execve*()

This patch enables exeve*() to be traced by syscalls:sys_exit_execve
tracepoint.
Previously, calling forget_syscall() would set syscall to -1, which impedes
this tracepoint to prints its information.
So, this patch makes call to forget_syscall() conditional by only calling
it when syscall number is not execve() or execveat().

Signed-off-by: Francis Laniel <[email protected]>
---
arch/arm64/include/asm/processor.h | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 73e38d9a540c..e12ceb363d6a 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -34,6 +34,8 @@

#include <vdso/processor.h>

+#include <asm-generic/unistd.h>
+
#include <asm/alternative.h>
#include <asm/cpufeature.h>
#include <asm/hw_breakpoint.h>
@@ -250,8 +252,12 @@ void tls_preserve_current_state(void);

static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
{
+ s32 previous_syscall = regs->syscallno;
memset(regs, 0, sizeof(*regs));
- forget_syscall(regs);
+ if (previous_syscall == __NR_execve || previous_syscall == __NR_execveat)
+ regs->syscallno = previous_syscall;
+ else
+ forget_syscall(regs);
regs->pc = pc;

if (system_uses_irq_prio_masking())
--
2.25.1


2022-05-10 13:14:02

by Will Deacon

[permalink] [raw]
Subject: Re: [RFC PATCH v1 1/1] arm64: Forget syscall if different from execve*()

On Mon, May 09, 2022 at 04:19:57PM +0100, Francis Laniel wrote:
> This patch enables exeve*() to be traced by syscalls:sys_exit_execve
> tracepoint.
> Previously, calling forget_syscall() would set syscall to -1, which impedes
> this tracepoint to prints its information.
> So, this patch makes call to forget_syscall() conditional by only calling
> it when syscall number is not execve() or execveat().
>
> Signed-off-by: Francis Laniel <[email protected]>
> ---
> arch/arm64/include/asm/processor.h | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
> index 73e38d9a540c..e12ceb363d6a 100644
> --- a/arch/arm64/include/asm/processor.h
> +++ b/arch/arm64/include/asm/processor.h
> @@ -34,6 +34,8 @@
>
> #include <vdso/processor.h>
>
> +#include <asm-generic/unistd.h>
> +
> #include <asm/alternative.h>
> #include <asm/cpufeature.h>
> #include <asm/hw_breakpoint.h>
> @@ -250,8 +252,12 @@ void tls_preserve_current_state(void);
>
> static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
> {
> + s32 previous_syscall = regs->syscallno;
> memset(regs, 0, sizeof(*regs));
> - forget_syscall(regs);
> + if (previous_syscall == __NR_execve || previous_syscall == __NR_execveat)
> + regs->syscallno = previous_syscall;
> + else
> + forget_syscall(regs);

Hmm, this really looks like a bodge and it doesn't handle the compat case
either.

How do other architectures handle this?

Will

2022-05-10 20:47:03

by Will Deacon

[permalink] [raw]
Subject: Re: [RFC PATCH v1 1/1] arm64: Forget syscall if different from execve*()

On Tue, May 10, 2022 at 03:00:11PM +0100, Francis Laniel wrote:
> Le mardi 10 mai 2022, 11:59:48 BST Will Deacon a ?crit :
> > On Mon, May 09, 2022 at 04:19:57PM +0100, Francis Laniel wrote:
> > > diff --git a/arch/arm64/include/asm/processor.h
> > > b/arch/arm64/include/asm/processor.h index 73e38d9a540c..e12ceb363d6a
> > > 100644
> > > --- a/arch/arm64/include/asm/processor.h
> > > +++ b/arch/arm64/include/asm/processor.h
> > > @@ -34,6 +34,8 @@
> > >
> > > #include <vdso/processor.h>
> > >
> > > +#include <asm-generic/unistd.h>
> > > +
> > >
> > > #include <asm/alternative.h>
> > > #include <asm/cpufeature.h>
> > > #include <asm/hw_breakpoint.h>
> > >
> > > @@ -250,8 +252,12 @@ void tls_preserve_current_state(void);
> > >
> > > static inline void start_thread_common(struct pt_regs *regs, unsigned
> > > long pc) {
> > >
> > > + s32 previous_syscall = regs->syscallno;
> > >
> > > memset(regs, 0, sizeof(*regs));
> > >
> > > - forget_syscall(regs);
> > > + if (previous_syscall == __NR_execve || previous_syscall ==
> > > __NR_execveat)
> > > + regs->syscallno = previous_syscall;
> > > + else
> > > + forget_syscall(regs);
> >
> > Hmm, this really looks like a bodge and it doesn't handle the compat case
> > either.
> >
> > How do other architectures handle this?
>
> My understanding of others architectures is quite limited, but here are my
> findings and understanding of some of them:
> * arm (32 bits) EABI: start_thread() sets r7 to previous r7 for ELF FDPIC and
> to 0 for other binfmts [1].
> * arm (32 bits) OABI: syscall number is set to -1 if
> ptrace_report_syscall_entry() failed [2].
> * mips: start_thread() does not modify current_thread_info->syscall which is
> taken directly from v0 [3, 4].
> * riscv: start_thread() does not modify a7 [5].
> * x86_64: start_thread_common() does not touch orig_ax which seems to contain
> the syscall number [6].

Hmm, so the million dollar question is why on Earth we have that
forget_syscall() call to start with. Amusingly I've, err, forgotten;
forget_forget_syscall() perhaps?

Catalin? It's been there since day one afaict.

Will

2022-05-10 21:51:00

by Francis Laniel

[permalink] [raw]
Subject: Re: [RFC PATCH v1 1/1] arm64: Forget syscall if different from execve*()

Hi.

Le mardi 10 mai 2022, 11:59:48 BST Will Deacon a ?crit :
> On Mon, May 09, 2022 at 04:19:57PM +0100, Francis Laniel wrote:
> > This patch enables exeve*() to be traced by syscalls:sys_exit_execve
> > tracepoint.
> > Previously, calling forget_syscall() would set syscall to -1, which
> > impedes
> > this tracepoint to prints its information.
> > So, this patch makes call to forget_syscall() conditional by only calling
> > it when syscall number is not execve() or execveat().
> >
> > Signed-off-by: Francis Laniel <[email protected]>
> > ---
> >
> > arch/arm64/include/asm/processor.h | 8 +++++++-
> > 1 file changed, 7 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/include/asm/processor.h
> > b/arch/arm64/include/asm/processor.h index 73e38d9a540c..e12ceb363d6a
> > 100644
> > --- a/arch/arm64/include/asm/processor.h
> > +++ b/arch/arm64/include/asm/processor.h
> > @@ -34,6 +34,8 @@
> >
> > #include <vdso/processor.h>
> >
> > +#include <asm-generic/unistd.h>
> > +
> >
> > #include <asm/alternative.h>
> > #include <asm/cpufeature.h>
> > #include <asm/hw_breakpoint.h>
> >
> > @@ -250,8 +252,12 @@ void tls_preserve_current_state(void);
> >
> > static inline void start_thread_common(struct pt_regs *regs, unsigned
> > long pc) {
> >
> > + s32 previous_syscall = regs->syscallno;
> >
> > memset(regs, 0, sizeof(*regs));
> >
> > - forget_syscall(regs);
> > + if (previous_syscall == __NR_execve || previous_syscall ==
> > __NR_execveat)
> > + regs->syscallno = previous_syscall;
> > + else
> > + forget_syscall(regs);
>
> Hmm, this really looks like a bodge and it doesn't handle the compat case
> either.
>
> How do other architectures handle this?

My understanding of others architectures is quite limited, but here are my
findings and understanding of some of them:
* arm (32 bits) EABI: start_thread() sets r7 to previous r7 for ELF FDPIC and
to 0 for other binfmts [1].
* arm (32 bits) OABI: syscall number is set to -1 if
ptrace_report_syscall_entry() failed [2].
* mips: start_thread() does not modify current_thread_info->syscall which is
taken directly from v0 [3, 4].
* riscv: start_thread() does not modify a7 [5].
* x86_64: start_thread_common() does not touch orig_ax which seems to contain
the syscall number [6].

> Will

Best regards.
---
[1] https://elixir.bootlin.com/linux/v5.18-rc6/source/arch/arm/include/asm/
processor.h#L52
[2] https://elixir.bootlin.com/linux/v5.18-rc6/source/arch/arm/kernel/
ptrace.c#L847
[3] https://elixir.bootlin.com/linux/v5.18-rc6/source/arch/mips/kernel/
process.c#L52
[4] https://elixir.bootlin.com/linux/v5.18-rc6/source/arch/mips/kernel/
scall64-n64.S#L85
[5] https://elixir.bootlin.com/linux/v5.18-rc6/source/arch/riscv/kernel/
process.c#L87
[6] https://elixir.bootlin.com/linux/v5.18-rc6/source/arch/x86/kernel/
process_64.c#L505



2022-05-10 23:06:15

by Francis Laniel

[permalink] [raw]
Subject: Re: [RFC PATCH v1 1/1] arm64: Forget syscall if different from execve*()

Le mardi 10 mai 2022, 15:03:33 BST Will Deacon a ?crit :
> On Tue, May 10, 2022 at 03:00:11PM +0100, Francis Laniel wrote:
> > Le mardi 10 mai 2022, 11:59:48 BST Will Deacon a ?crit :
> > > On Mon, May 09, 2022 at 04:19:57PM +0100, Francis Laniel wrote:
> > > > diff --git a/arch/arm64/include/asm/processor.h
> > > > b/arch/arm64/include/asm/processor.h index 73e38d9a540c..e12ceb363d6a
> > > > 100644
> > > > --- a/arch/arm64/include/asm/processor.h
> > > > +++ b/arch/arm64/include/asm/processor.h
> > > > @@ -34,6 +34,8 @@
> > > >
> > > > #include <vdso/processor.h>
> > > >
> > > > +#include <asm-generic/unistd.h>
> > > > +
> > > >
> > > > #include <asm/alternative.h>
> > > > #include <asm/cpufeature.h>
> > > > #include <asm/hw_breakpoint.h>
> > > >
> > > > @@ -250,8 +252,12 @@ void tls_preserve_current_state(void);
> > > >
> > > > static inline void start_thread_common(struct pt_regs *regs, unsigned
> > > > long pc) {
> > > >
> > > > + s32 previous_syscall = regs->syscallno;
> > > >
> > > > memset(regs, 0, sizeof(*regs));
> > > >
> > > > - forget_syscall(regs);
> > > > + if (previous_syscall == __NR_execve || previous_syscall ==
> > > > __NR_execveat)
> > > > + regs->syscallno = previous_syscall;
> > > > + else
> > > > + forget_syscall(regs);
> > >
> > > Hmm, this really looks like a bodge and it doesn't handle the compat
> > > case
> > > either.
> > >
> > > How do other architectures handle this?
> >
> > My understanding of others architectures is quite limited, but here are my
> > findings and understanding of some of them:
> > * arm (32 bits) EABI: start_thread() sets r7 to previous r7 for ELF FDPIC
> > and to 0 for other binfmts [1].
> > * arm (32 bits) OABI: syscall number is set to -1 if
> > ptrace_report_syscall_entry() failed [2].
> > * mips: start_thread() does not modify current_thread_info->syscall which
> > is taken directly from v0 [3, 4].
> > * riscv: start_thread() does not modify a7 [5].
> > * x86_64: start_thread_common() does not touch orig_ax which seems to
> > contain the syscall number [6].
>
> Hmm, so the million dollar question is why on Earth we have that
> forget_syscall() call to start with. Amusingly I've, err, forgotten;
> forget_forget_syscall() perhaps?

I think this is maybe tied to this comment [1]:
The de-facto standard way to skip a system call using ptrace
is to set the system call to -1 (NO_SYSCALL)

But I will let the original author explain as his/her explaination will be
better than mine.

> Catalin? It's been there since day one afaict.
>
> Will

---
[1] https://elixir.bootlin.com/linux/v5.18-rc6/source/arch/arm64/kernel/
syscall.c#L121



2022-05-18 13:35:29

by Catalin Marinas

[permalink] [raw]
Subject: Re: [RFC PATCH v1 1/1] arm64: Forget syscall if different from execve*()

On Tue, May 10, 2022 at 03:03:33PM +0100, Will Deacon wrote:
> On Tue, May 10, 2022 at 03:00:11PM +0100, Francis Laniel wrote:
> > Le mardi 10 mai 2022, 11:59:48 BST Will Deacon a �crit :
> > > On Mon, May 09, 2022 at 04:19:57PM +0100, Francis Laniel wrote:
> > > > diff --git a/arch/arm64/include/asm/processor.h
> > > > b/arch/arm64/include/asm/processor.h index 73e38d9a540c..e12ceb363d6a
> > > > 100644
> > > > --- a/arch/arm64/include/asm/processor.h
> > > > +++ b/arch/arm64/include/asm/processor.h
> > > > @@ -34,6 +34,8 @@
> > > >
> > > > #include <vdso/processor.h>
> > > >
> > > > +#include <asm-generic/unistd.h>
> > > > +
> > > >
> > > > #include <asm/alternative.h>
> > > > #include <asm/cpufeature.h>
> > > > #include <asm/hw_breakpoint.h>
> > > >
> > > > @@ -250,8 +252,12 @@ void tls_preserve_current_state(void);
> > > >
> > > > static inline void start_thread_common(struct pt_regs *regs, unsigned
> > > > long pc) {
> > > >
> > > > + s32 previous_syscall = regs->syscallno;
> > > >
> > > > memset(regs, 0, sizeof(*regs));
> > > >
> > > > - forget_syscall(regs);
> > > > + if (previous_syscall == __NR_execve || previous_syscall ==
> > > > __NR_execveat)
> > > > + regs->syscallno = previous_syscall;
> > > > + else
> > > > + forget_syscall(regs);
> > >
> > > Hmm, this really looks like a bodge and it doesn't handle the compat case
> > > either.
> > >
> > > How do other architectures handle this?
> >
> > My understanding of others architectures is quite limited, but here are my
> > findings and understanding of some of them:
> > * arm (32 bits) EABI: start_thread() sets r7 to previous r7 for ELF FDPIC and
> > to 0 for other binfmts [1].
> > * arm (32 bits) OABI: syscall number is set to -1 if
> > ptrace_report_syscall_entry() failed [2].
> > * mips: start_thread() does not modify current_thread_info->syscall which is
> > taken directly from v0 [3, 4].
> > * riscv: start_thread() does not modify a7 [5].
> > * x86_64: start_thread_common() does not touch orig_ax which seems to contain
> > the syscall number [6].
>
> Hmm, so the million dollar question is why on Earth we have that
> forget_syscall() call to start with. Amusingly I've, err, forgotten;
> forget_forget_syscall() perhaps?
>
> Catalin? It's been there since day one afaict.

Looking at the old logs, this appeared somewhere between day 0 and 1. We
had a memset(regs, 0) with a pt_regs of 36 registers but moved to
dedicated names for syscallno etc. and that's where I changed it from 0
to ~0UL.

A few weeks ago James Morse (cc'ed) came across this issue and even sent
a patch internally to remove forget_syscall() here. But that looks like
a bodge and James' suggestion was that maybe the core code can preserve
the syscallno and this would fix it for all architectures.

--
Catalin