AArch64: Detect exit from execve syscall

Message ID 20190122111457.6589-1-alan.hayward@arm.com
State New, archived
Headers

Commit Message

Alan Hayward Jan. 22, 2019, 11:15 a.m. UTC
  Checking the syscall number when stopped on entry/exit relies on checking
the value in register X8.

However, on exit from an execve syscall, the registers will all be cleared.
Given this is only checked on syscall entry/exit, then a cleared register
state either means execve exit or syscall 0 (io_setup) entry with invalid
parameters and an invalid FR and LR, which in reality should never happen.
Use this to detect execve exit.

Move function to allow use of aarch64_sys_execve enum, and use newer
regcache functions.

Fixes gdb.base/catch-syscall.exp on Aarch64.

gdb/ChangeLog:

2019-01-22  Alan Hayward  <alan.hayward@arm.com>

	* aarch64-linux-tdep.c (aarch64_linux_get_syscall_number): Check
	for execve.
---
 gdb/aarch64-linux-tdep.c | 56 ++++++++++++++++++++++++----------------
 1 file changed, 34 insertions(+), 22 deletions(-)
  

Comments

Alan Hayward Jan. 31, 2019, 10:12 a.m. UTC | #1
Ping.

> On 22 Jan 2019, at 11:15, Alan Hayward <Alan.Hayward@arm.com> wrote:
> 
> Checking the syscall number when stopped on entry/exit relies on checking
> the value in register X8.
> 
> However, on exit from an execve syscall, the registers will all be cleared.
> Given this is only checked on syscall entry/exit, then a cleared register
> state either means execve exit or syscall 0 (io_setup) entry with invalid
> parameters and an invalid FR and LR, which in reality should never happen.
> Use this to detect execve exit.
> 
> Move function to allow use of aarch64_sys_execve enum, and use newer
> regcache functions.
> 
> Fixes gdb.base/catch-syscall.exp on Aarch64.
> 
> gdb/ChangeLog:
> 
> 2019-01-22  Alan Hayward  <alan.hayward@arm.com>
> 
> 	* aarch64-linux-tdep.c (aarch64_linux_get_syscall_number): Check
> 	for execve.
> ---
> gdb/aarch64-linux-tdep.c | 56 ++++++++++++++++++++++++----------------
> 1 file changed, 34 insertions(+), 22 deletions(-)
> 
> diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
> index 62cfc76c82..39e607658f 100644
> --- a/gdb/aarch64-linux-tdep.c
> +++ b/gdb/aarch64-linux-tdep.c
> @@ -757,28 +757,6 @@ aarch64_stap_parse_special_token (struct gdbarch *gdbarch,
>   return 1;
> }
> 
> -/* Implement the "get_syscall_number" gdbarch method.  */
> -
> -static LONGEST
> -aarch64_linux_get_syscall_number (struct gdbarch *gdbarch,
> -				  thread_info *thread)
> -{
> -  struct regcache *regs = get_thread_regcache (thread);
> -  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> -
> -  /* The content of register x8.  */
> -  gdb_byte buf[X_REGISTER_SIZE];
> -  /* The result.  */
> -  LONGEST ret;
> -
> -  /* Getting the system call number from the register x8.  */
> -  regs->cooked_read (AARCH64_DWARF_X0 + 8, buf);
> -
> -  ret = extract_signed_integer (buf, X_REGISTER_SIZE, byte_order);
> -
> -  return ret;
> -}
> -
> /* AArch64 process record-replay constructs: syscall, signal etc.  */
> 
> struct linux_record_tdep aarch64_linux_record_tdep;
> @@ -1334,6 +1312,40 @@ aarch64_canonicalize_syscall (enum aarch64_syscall syscall_number)
>   }
> }
> 
> +/* Retrieve the syscall number at a ptrace syscall-stop, either on syscall entry
> +   or exit.  Return -1 upon error.  */
> +
> +static LONGEST
> +aarch64_linux_get_syscall_number (struct gdbarch *gdbarch, thread_info *thread)
> +{
> +  struct regcache *regs = get_thread_regcache (thread);
> +  LONGEST ret;
> +
> +  /* Get the system call number from register x8.  */
> +  regs->cooked_read (AARCH64_X0_REGNUM + 8, &ret);
> +
> +  /* On exit from a successful execve, we will be in a new process and all the
> +     registers will be cleared - x0 to x30 will be 0, except for a 1 in x7.
> +     This function will only ever get called when stopped at the entry or exit
> +     of a syscall, so by checking for 0 in x0 (arg0/retval), x1 (arg1), x8
> +     (syscall), x29 (FP) and x30 (LR) we can infer:
> +     1) Either inferior is at exit from sucessful execve.
> +     2) Or inferior is at entry to a call to io_setup with invalid arguments and
> +	a corrupted FP and LR.
> +     It should be safe enough to assume case 1.  */
> +  if (ret == 0)
> +    {
> +      LONGEST x1 = -1, fp = -1, lr = -1;
> +      regs->cooked_read (AARCH64_X0_REGNUM + 1, &x1);
> +      regs->cooked_read (AARCH64_FP_REGNUM, &fp);
> +      regs->cooked_read (AARCH64_LR_REGNUM, &lr);
> +      if (x1 == 0 && fp ==0 && lr == 0)
> +	return aarch64_sys_execve;
> +    }
> +
> +  return ret;
> +}
> +
> /* Record all registers but PC register for process-record.  */
> 
> static int
> -- 
> 2.17.2 (Apple Git-113)
>
  
Pedro Alves Feb. 7, 2019, 5:40 p.m. UTC | #2
LGTM.

Thanks,
Pedro Alves

On 01/31/2019 10:12 AM, Alan Hayward wrote:
> Ping.
> 
>> On 22 Jan 2019, at 11:15, Alan Hayward <Alan.Hayward@arm.com> wrote:
>>
>> Checking the syscall number when stopped on entry/exit relies on checking
>> the value in register X8.
>>
>> However, on exit from an execve syscall, the registers will all be cleared.
>> Given this is only checked on syscall entry/exit, then a cleared register
>> state either means execve exit or syscall 0 (io_setup) entry with invalid
>> parameters and an invalid FR and LR, which in reality should never happen.
>> Use this to detect execve exit.
>>
>> Move function to allow use of aarch64_sys_execve enum, and use newer
>> regcache functions.
>>
>> Fixes gdb.base/catch-syscall.exp on Aarch64.
>>
>> gdb/ChangeLog:
>>
>> 2019-01-22  Alan Hayward  <alan.hayward@arm.com>
>>
>> 	* aarch64-linux-tdep.c (aarch64_linux_get_syscall_number): Check
>> 	for execve.
>> ---
>> gdb/aarch64-linux-tdep.c | 56 ++++++++++++++++++++++++----------------
>> 1 file changed, 34 insertions(+), 22 deletions(-)
>>
>> diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
>> index 62cfc76c82..39e607658f 100644
>> --- a/gdb/aarch64-linux-tdep.c
>> +++ b/gdb/aarch64-linux-tdep.c
>> @@ -757,28 +757,6 @@ aarch64_stap_parse_special_token (struct gdbarch *gdbarch,
>>   return 1;
>> }
>>
>> -/* Implement the "get_syscall_number" gdbarch method.  */
>> -
>> -static LONGEST
>> -aarch64_linux_get_syscall_number (struct gdbarch *gdbarch,
>> -				  thread_info *thread)
>> -{
>> -  struct regcache *regs = get_thread_regcache (thread);
>> -  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
>> -
>> -  /* The content of register x8.  */
>> -  gdb_byte buf[X_REGISTER_SIZE];
>> -  /* The result.  */
>> -  LONGEST ret;
>> -
>> -  /* Getting the system call number from the register x8.  */
>> -  regs->cooked_read (AARCH64_DWARF_X0 + 8, buf);
>> -
>> -  ret = extract_signed_integer (buf, X_REGISTER_SIZE, byte_order);
>> -
>> -  return ret;
>> -}
>> -
>> /* AArch64 process record-replay constructs: syscall, signal etc.  */
>>
>> struct linux_record_tdep aarch64_linux_record_tdep;
>> @@ -1334,6 +1312,40 @@ aarch64_canonicalize_syscall (enum aarch64_syscall syscall_number)
>>   }
>> }
>>
>> +/* Retrieve the syscall number at a ptrace syscall-stop, either on syscall entry
>> +   or exit.  Return -1 upon error.  */
>> +
>> +static LONGEST
>> +aarch64_linux_get_syscall_number (struct gdbarch *gdbarch, thread_info *thread)
>> +{
>> +  struct regcache *regs = get_thread_regcache (thread);
>> +  LONGEST ret;
>> +
>> +  /* Get the system call number from register x8.  */
>> +  regs->cooked_read (AARCH64_X0_REGNUM + 8, &ret);
>> +
>> +  /* On exit from a successful execve, we will be in a new process and all the
>> +     registers will be cleared - x0 to x30 will be 0, except for a 1 in x7.
>> +     This function will only ever get called when stopped at the entry or exit
>> +     of a syscall, so by checking for 0 in x0 (arg0/retval), x1 (arg1), x8
>> +     (syscall), x29 (FP) and x30 (LR) we can infer:
>> +     1) Either inferior is at exit from sucessful execve.
>> +     2) Or inferior is at entry to a call to io_setup with invalid arguments and
>> +	a corrupted FP and LR.
>> +     It should be safe enough to assume case 1.  */
>> +  if (ret == 0)
>> +    {
>> +      LONGEST x1 = -1, fp = -1, lr = -1;
>> +      regs->cooked_read (AARCH64_X0_REGNUM + 1, &x1);
>> +      regs->cooked_read (AARCH64_FP_REGNUM, &fp);
>> +      regs->cooked_read (AARCH64_LR_REGNUM, &lr);
>> +      if (x1 == 0 && fp ==0 && lr == 0)
>> +	return aarch64_sys_execve;
>> +    }
>> +
>> +  return ret;
>> +}
>> +
>> /* Record all registers but PC register for process-record.  */
>>
>> static int
>> -- 
>> 2.17.2 (Apple Git-113)
>>
>
  

Patch

diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index 62cfc76c82..39e607658f 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -757,28 +757,6 @@  aarch64_stap_parse_special_token (struct gdbarch *gdbarch,
   return 1;
 }
 
-/* Implement the "get_syscall_number" gdbarch method.  */
-
-static LONGEST
-aarch64_linux_get_syscall_number (struct gdbarch *gdbarch,
-				  thread_info *thread)
-{
-  struct regcache *regs = get_thread_regcache (thread);
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-
-  /* The content of register x8.  */
-  gdb_byte buf[X_REGISTER_SIZE];
-  /* The result.  */
-  LONGEST ret;
-
-  /* Getting the system call number from the register x8.  */
-  regs->cooked_read (AARCH64_DWARF_X0 + 8, buf);
-
-  ret = extract_signed_integer (buf, X_REGISTER_SIZE, byte_order);
-
-  return ret;
-}
-
 /* AArch64 process record-replay constructs: syscall, signal etc.  */
 
 struct linux_record_tdep aarch64_linux_record_tdep;
@@ -1334,6 +1312,40 @@  aarch64_canonicalize_syscall (enum aarch64_syscall syscall_number)
   }
 }
 
+/* Retrieve the syscall number at a ptrace syscall-stop, either on syscall entry
+   or exit.  Return -1 upon error.  */
+
+static LONGEST
+aarch64_linux_get_syscall_number (struct gdbarch *gdbarch, thread_info *thread)
+{
+  struct regcache *regs = get_thread_regcache (thread);
+  LONGEST ret;
+
+  /* Get the system call number from register x8.  */
+  regs->cooked_read (AARCH64_X0_REGNUM + 8, &ret);
+
+  /* On exit from a successful execve, we will be in a new process and all the
+     registers will be cleared - x0 to x30 will be 0, except for a 1 in x7.
+     This function will only ever get called when stopped at the entry or exit
+     of a syscall, so by checking for 0 in x0 (arg0/retval), x1 (arg1), x8
+     (syscall), x29 (FP) and x30 (LR) we can infer:
+     1) Either inferior is at exit from sucessful execve.
+     2) Or inferior is at entry to a call to io_setup with invalid arguments and
+	a corrupted FP and LR.
+     It should be safe enough to assume case 1.  */
+  if (ret == 0)
+    {
+      LONGEST x1 = -1, fp = -1, lr = -1;
+      regs->cooked_read (AARCH64_X0_REGNUM + 1, &x1);
+      regs->cooked_read (AARCH64_FP_REGNUM, &fp);
+      regs->cooked_read (AARCH64_LR_REGNUM, &lr);
+      if (x1 == 0 && fp ==0 && lr == 0)
+	return aarch64_sys_execve;
+    }
+
+  return ret;
+}
+
 /* Record all registers but PC register for process-record.  */
 
 static int