public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525)
@ 2023-07-05 13:41 Pedro Alves
  2023-07-05 14:45 ` Matt Turner
  2023-07-05 17:59 ` Andrew Burgess
  0 siblings, 2 replies; 9+ messages in thread
From: Pedro Alves @ 2023-07-05 13:41 UTC (permalink / raw)
  To: gdb-patches; +Cc: Matt Turner

Since commit 05c06f318fd9 ("Linux: Access memory even if threads are
running"), GDB prefers pread64/pwrite64 to access inferior memory
instead of ptrace.  That change broke reading shared libraries on
SPARC Linux, as reported by PR gdb/30525 ("gdb cannot read shared
libraries on SPARC64").

On SPARC64 Linux, surprisingly (to me), userspace shared libraries are
mapped at high 64-bit addresses:

   (gdb) info sharedlibrary
   Cannot access memory at address 0xfff80001002011e0
   Cannot access memory at address 0xfff80001002011d8
   Cannot access memory at address 0xfff80001002011d8
   From                To                  Syms Read   Shared Object Library
   0xfff80001000010a0  0xfff8000100021f80  Yes (*)     /lib64/ld-linux.so.2
   (*): Shared library is missing debugging information.

Those addresses are 64-bit addresses with the high bits set.  When
interpreted as signed, they're negative.

The Linux kernel rejects pread64/pwrite64 if the offset argument of
type off_t (a signed type) is negative, which happens if the memory
address we're accessing has its high bit set.  See
linux/fs/read_write.c sys_pread64 and sys_pwrite64 in Linux.

Thanksfully, lseek does not fail in that situation.  So the fix is to
use the 'lseek + read|write' path if the offset would be negative.

Fix this in both native GDB and GDBserver.

Tested on a SPARC64 GNU/Linux and x86_64 GNU/Linux.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30525
Change-Id: I79c724f918037ea67b7396fadb521bc9d1b10dc5
---
 gdb/linux-nat.c        | 30 ++++++++++++++++++++----------
 gdbserver/linux-low.cc | 33 ++++++++++++++++++++-------------
 2 files changed, 40 insertions(+), 23 deletions(-)

diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 383ef58fa23..c5efacbb3fa 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -3909,18 +3909,28 @@ linux_proc_xfer_memory_partial_fd (int fd, int pid,
 
   gdb_assert (fd != -1);
 
-  /* Use pread64/pwrite64 if available, since they save a syscall and can
-     handle 64-bit offsets even on 32-bit platforms (for instance, SPARC
-     debugging a SPARC64 application).  */
+  /* Use pread64/pwrite64 if available, since they save a syscall and
+     can handle 64-bit offsets even on 32-bit platforms (for instance,
+     SPARC debugging a SPARC64 application).  But only use them if the
+     offset isn't so high that when cast to off_t it'd be negative, as
+     seen on SPARC64.  pread64/pwrite64 outright reject such offsets.
+     lseek does not.  */
 #ifdef HAVE_PREAD64
-  ret = (readbuf ? pread64 (fd, readbuf, len, offset)
-	 : pwrite64 (fd, writebuf, len, offset));
-#else
-  ret = lseek (fd, offset, SEEK_SET);
-  if (ret != -1)
-    ret = (readbuf ? read (fd, readbuf, len)
-	   : write (fd, writebuf, len));
+  if ((off_t) offset >= 0)
+    {
+      ret = (readbuf != nullptr
+	     ? pread64 (fd, readbuf, len, offset)
+	     : pwrite64 (fd, writebuf, len, offset));
+    }
+  else
 #endif
+    {
+      ret = lseek (fd, offset, SEEK_SET);
+      if (ret != -1)
+	ret = (readbuf
+	       ? read (fd, readbuf, len)
+	       : write (fd, writebuf, len));
+    }
 
   if (ret == -1)
     {
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 8ab16698632..63668d81b45 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -5377,21 +5377,28 @@ proc_xfer_memory (CORE_ADDR memaddr, unsigned char *readbuf,
     {
       int bytes;
 
-      /* If pread64 is available, use it.  It's faster if the kernel
-	 supports it (only one syscall), and it's 64-bit safe even on
-	 32-bit platforms (for instance, SPARC debugging a SPARC64
-	 application).  */
+      /* Use pread64/pwrite64 if available, since they save a syscall
+	 and can handle 64-bit offsets even on 32-bit platforms (for
+	 instance, SPARC debugging a SPARC64 application).  But only
+	 use them if the offset isn't so high that when cast to off_t
+	 it'd be negative, as seen on SPARC64.  pread64/pwrite64
+	 outright reject such offsets.  lseek does not.  */
 #ifdef HAVE_PREAD64
-      bytes = (readbuf != nullptr
-	       ? pread64 (fd, readbuf, len, memaddr)
-	       : pwrite64 (fd, writebuf, len, memaddr));
-#else
-      bytes = -1;
-      if (lseek (fd, memaddr, SEEK_SET) != -1)
-	bytes = (readbuf != nullptr
-		 ? read (fd, readbuf, len)
-		 : write (fd, writebuf, len));
+      if ((off_t) memaddr >= 0)
+	{
+	  bytes = (readbuf != nullptr
+		   ? pread64 (fd, readbuf, len, memaddr)
+		   : pwrite64 (fd, writebuf, len, memaddr));
+	}
+      else
 #endif
+	{
+	  bytes = -1;
+	  if (lseek (fd, memaddr, SEEK_SET) != -1)
+	    bytes = (readbuf != nullptr
+		     ? read (fd, readbuf, len)
+		     : write (fd, writebuf, len));
+	}
 
       if (bytes < 0)
 	return errno;

base-commit: a5042543d819df8a76ebec8c4d715f244efbab0a
-- 
2.34.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525)
  2023-07-05 13:41 [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525) Pedro Alves
@ 2023-07-05 14:45 ` Matt Turner
  2023-07-05 17:59 ` Andrew Burgess
  1 sibling, 0 replies; 9+ messages in thread
From: Matt Turner @ 2023-07-05 14:45 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Thank you!

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525)
  2023-07-05 13:41 [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525) Pedro Alves
  2023-07-05 14:45 ` Matt Turner
@ 2023-07-05 17:59 ` Andrew Burgess
  2023-07-06 13:43   ` Pedro Alves
  1 sibling, 1 reply; 9+ messages in thread
From: Andrew Burgess @ 2023-07-05 17:59 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches; +Cc: Matt Turner

Pedro Alves <pedro@palves.net> writes:

> Since commit 05c06f318fd9 ("Linux: Access memory even if threads are
> running"), GDB prefers pread64/pwrite64 to access inferior memory
> instead of ptrace.  That change broke reading shared libraries on
> SPARC Linux, as reported by PR gdb/30525 ("gdb cannot read shared
> libraries on SPARC64").
>
> On SPARC64 Linux, surprisingly (to me), userspace shared libraries are
> mapped at high 64-bit addresses:
>
>    (gdb) info sharedlibrary
>    Cannot access memory at address 0xfff80001002011e0
>    Cannot access memory at address 0xfff80001002011d8
>    Cannot access memory at address 0xfff80001002011d8
>    From                To                  Syms Read   Shared Object Library
>    0xfff80001000010a0  0xfff8000100021f80  Yes (*)     /lib64/ld-linux.so.2
>    (*): Shared library is missing debugging information.
>
> Those addresses are 64-bit addresses with the high bits set.  When
> interpreted as signed, they're negative.
>
> The Linux kernel rejects pread64/pwrite64 if the offset argument of
> type off_t (a signed type) is negative, which happens if the memory
> address we're accessing has its high bit set.  See
> linux/fs/read_write.c sys_pread64 and sys_pwrite64 in Linux.
>
> Thanksfully, lseek does not fail in that situation.  So the fix is to
> use the 'lseek + read|write' path if the offset would be negative.
>
> Fix this in both native GDB and GDBserver.
>
> Tested on a SPARC64 GNU/Linux and x86_64 GNU/Linux.
>
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30525
> Change-Id: I79c724f918037ea67b7396fadb521bc9d1b10dc5
> ---
>  gdb/linux-nat.c        | 30 ++++++++++++++++++++----------
>  gdbserver/linux-low.cc | 33 ++++++++++++++++++++-------------
>  2 files changed, 40 insertions(+), 23 deletions(-)
>
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index 383ef58fa23..c5efacbb3fa 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -3909,18 +3909,28 @@ linux_proc_xfer_memory_partial_fd (int fd, int pid,
>  
>    gdb_assert (fd != -1);
>  
> -  /* Use pread64/pwrite64 if available, since they save a syscall and can
> -     handle 64-bit offsets even on 32-bit platforms (for instance, SPARC
> -     debugging a SPARC64 application).  */
> +  /* Use pread64/pwrite64 if available, since they save a syscall and
> +     can handle 64-bit offsets even on 32-bit platforms (for instance,
> +     SPARC debugging a SPARC64 application).  But only use them if the
> +     offset isn't so high that when cast to off_t it'd be negative, as
> +     seen on SPARC64.  pread64/pwrite64 outright reject such offsets.
> +     lseek does not.  */
>  #ifdef HAVE_PREAD64
> -  ret = (readbuf ? pread64 (fd, readbuf, len, offset)
> -	 : pwrite64 (fd, writebuf, len, offset));
> -#else
> -  ret = lseek (fd, offset, SEEK_SET);
> -  if (ret != -1)
> -    ret = (readbuf ? read (fd, readbuf, len)
> -	   : write (fd, writebuf, len));
> +  if ((off_t) offset >= 0)
> +    {
> +      ret = (readbuf != nullptr
> +	     ? pread64 (fd, readbuf, len, offset)
> +	     : pwrite64 (fd, writebuf, len, offset));
> +    }

I haven't tested this, but the change looks good to me with one nit...

I think the '{' and '}' here (and in gdbserver below) aren't GDB style,
as there's only a single statement?

Otherwise,

Approved-By: Andrew Burgess <aburgess@redhat.com>

thanks,
Andrew

> +  else
>  #endif
> +    {
> +      ret = lseek (fd, offset, SEEK_SET);
> +      if (ret != -1)
> +	ret = (readbuf
> +	       ? read (fd, readbuf, len)
> +	       : write (fd, writebuf, len));
> +    }
>  
>    if (ret == -1)
>      {
> diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
> index 8ab16698632..63668d81b45 100644
> --- a/gdbserver/linux-low.cc
> +++ b/gdbserver/linux-low.cc
> @@ -5377,21 +5377,28 @@ proc_xfer_memory (CORE_ADDR memaddr, unsigned char *readbuf,
>      {
>        int bytes;
>  
> -      /* If pread64 is available, use it.  It's faster if the kernel
> -	 supports it (only one syscall), and it's 64-bit safe even on
> -	 32-bit platforms (for instance, SPARC debugging a SPARC64
> -	 application).  */
> +      /* Use pread64/pwrite64 if available, since they save a syscall
> +	 and can handle 64-bit offsets even on 32-bit platforms (for
> +	 instance, SPARC debugging a SPARC64 application).  But only
> +	 use them if the offset isn't so high that when cast to off_t
> +	 it'd be negative, as seen on SPARC64.  pread64/pwrite64
> +	 outright reject such offsets.  lseek does not.  */
>  #ifdef HAVE_PREAD64
> -      bytes = (readbuf != nullptr
> -	       ? pread64 (fd, readbuf, len, memaddr)
> -	       : pwrite64 (fd, writebuf, len, memaddr));
> -#else
> -      bytes = -1;
> -      if (lseek (fd, memaddr, SEEK_SET) != -1)
> -	bytes = (readbuf != nullptr
> -		 ? read (fd, readbuf, len)
> -		 : write (fd, writebuf, len));
> +      if ((off_t) memaddr >= 0)
> +	{
> +	  bytes = (readbuf != nullptr
> +		   ? pread64 (fd, readbuf, len, memaddr)
> +		   : pwrite64 (fd, writebuf, len, memaddr));
> +	}
> +      else
>  #endif
> +	{
> +	  bytes = -1;
> +	  if (lseek (fd, memaddr, SEEK_SET) != -1)
> +	    bytes = (readbuf != nullptr
> +		     ? read (fd, readbuf, len)
> +		     : write (fd, writebuf, len));
> +	}
>  
>        if (bytes < 0)
>  	return errno;
>
> base-commit: a5042543d819df8a76ebec8c4d715f244efbab0a
> -- 
> 2.34.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525)
  2023-07-05 17:59 ` Andrew Burgess
@ 2023-07-06 13:43   ` Pedro Alves
  2023-07-06 13:54     ` Pedro Alves
  0 siblings, 1 reply; 9+ messages in thread
From: Pedro Alves @ 2023-07-06 13:43 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Matt Turner

Hi Andrew,

On 2023-07-05 18:59, Andrew Burgess wrote:
> Pedro Alves <pedro@palves.net> writes:
>> +  if ((off_t) offset >= 0)
>> +    {
>> +      ret = (readbuf != nullptr
>> +	     ? pread64 (fd, readbuf, len, offset)
>> +	     : pwrite64 (fd, writebuf, len, offset));
>> +    }
> 
> I haven't tested this, but the change looks good to me with one nit...
> 
> I think the '{' and '}' here (and in gdbserver below) aren't GDB style,
> as there's only a single statement?

I don't really mind much either way, as we're not super consistent, but note that
we do have a GDB-specific rule about this.  Here:

https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards

It says:

  "Any two or more lines in code should be wrapped in braces, even if they are comments, as they look like separate statements:"

As that says "Any two or more lines in code", I think it applies in this situation too.

Pedro Alves

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525)
  2023-07-06 13:43   ` Pedro Alves
@ 2023-07-06 13:54     ` Pedro Alves
  2023-07-06 15:25       ` Tom Tromey
  0 siblings, 1 reply; 9+ messages in thread
From: Pedro Alves @ 2023-07-06 13:54 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Matt Turner

Hi again,

On 2023-07-06 14:43, Pedro Alves wrote:
> Hi Andrew,
> 
> On 2023-07-05 18:59, Andrew Burgess wrote:
>> Pedro Alves <pedro@palves.net> writes:
>>> +  if ((off_t) offset >= 0)
>>> +    {
>>> +      ret = (readbuf != nullptr
>>> +	     ? pread64 (fd, readbuf, len, offset)
>>> +	     : pwrite64 (fd, writebuf, len, offset));
>>> +    }
>>
>> I haven't tested this, but the change looks good to me with one nit...
>>
>> I think the '{' and '}' here (and in gdbserver below) aren't GDB style,
>> as there's only a single statement?
> 
> I don't really mind much either way, as we're not super consistent, but note that
> we do have a GDB-specific rule about this.  Here:
> 
> https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards
> 
> It says:
> 
>   "Any two or more lines in code should be wrapped in braces, even if they are comments, as they look like separate statements:"
> 
> As that says "Any two or more lines in code", I think it applies in this situation too.
> 

I just realized that the lseek path, which I just reindented, has an if branch
with more than one line which isn't using braces...  The patch should at least be
consistent in all the lines it touches...  :-)  If we follow the documented convention,
then here's what we get.

WDYT?  Should we tweak the convention instead?

From ab7a42f42e711f2a3565da4f53b0d2e6bea0039d Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Wed, 7 Jun 2023 10:38:14 +0100
Subject: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR
 gdb/30525)

Since commit 05c06f318fd9 ("Linux: Access memory even if threads are
running"), GDB prefers pread64/pwrite64 to access inferior memory
instead of ptrace.  That change broke reading shared libraries on
SPARC64 Linux, as reported by PR gdb/30525 ("gdb cannot read shared
libraries on SPARC64").

On SPARC64 Linux, surprisingly (to me), userspace shared libraries are
mapped at high 64-bit addresses:

   (gdb) info sharedlibrary
   Cannot access memory at address 0xfff80001002011e0
   Cannot access memory at address 0xfff80001002011d8
   Cannot access memory at address 0xfff80001002011d8
   From                To                  Syms Read   Shared Object Library
   0xfff80001000010a0  0xfff8000100021f80  Yes (*)     /lib64/ld-linux.so.2
   (*): Shared library is missing debugging information.

Those addresses are 64-bit addresses with the high bits set.  When
interpreted as signed, they're negative.

The Linux kernel rejects pread64/pwrite64 if the offset argument of
type off_t (a signed type) is negative, which happens if the memory
address we're accessing has its high bit set.  See
linux/fs/read_write.c sys_pread64 and sys_pwrite64 in Linux.

Thankfully, lseek does not fail in that situation.  So the fix is to
use the 'lseek + read|write' path if the offset would be negative.

Fix this in both native GDB and GDBserver.

Tested on a SPARC64 GNU/Linux and x86-64 GNU/Linux.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30525
Change-Id: I79c724f918037ea67b7396fadb521bc9d1b10dc5
---
 gdb/linux-nat.c        | 32 ++++++++++++++++++++++----------
 gdbserver/linux-low.cc | 35 ++++++++++++++++++++++-------------
 2 files changed, 44 insertions(+), 23 deletions(-)

diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 383ef58fa23..86f3ebd3573 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -3909,18 +3909,30 @@ linux_proc_xfer_memory_partial_fd (int fd, int pid,
 
   gdb_assert (fd != -1);
 
-  /* Use pread64/pwrite64 if available, since they save a syscall and can
-     handle 64-bit offsets even on 32-bit platforms (for instance, SPARC
-     debugging a SPARC64 application).  */
+  /* Use pread64/pwrite64 if available, since they save a syscall and
+     can handle 64-bit offsets even on 32-bit platforms (for instance,
+     SPARC debugging a SPARC64 application).  But only use them if the
+     offset isn't so high that when cast to off_t it'd be negative, as
+     seen on SPARC64.  pread64/pwrite64 outright reject such offsets.
+     lseek does not.  */
 #ifdef HAVE_PREAD64
-  ret = (readbuf ? pread64 (fd, readbuf, len, offset)
-	 : pwrite64 (fd, writebuf, len, offset));
-#else
-  ret = lseek (fd, offset, SEEK_SET);
-  if (ret != -1)
-    ret = (readbuf ? read (fd, readbuf, len)
-	   : write (fd, writebuf, len));
+  if ((off_t) offset >= 0)
+    {
+      ret = (readbuf != nullptr
+	     ? pread64 (fd, readbuf, len, offset)
+	     : pwrite64 (fd, writebuf, len, offset));
+    }
+  else
 #endif
+    {
+      ret = lseek (fd, offset, SEEK_SET);
+      if (ret != -1)
+	{
+	  ret = (readbuf != nullptr
+		 ? read (fd, readbuf, len)
+		 : write (fd, writebuf, len));
+	}
+    }
 
   if (ret == -1)
     {
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 8ab16698632..f580e4b7977 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -5377,21 +5377,30 @@ proc_xfer_memory (CORE_ADDR memaddr, unsigned char *readbuf,
     {
       int bytes;
 
-      /* If pread64 is available, use it.  It's faster if the kernel
-	 supports it (only one syscall), and it's 64-bit safe even on
-	 32-bit platforms (for instance, SPARC debugging a SPARC64
-	 application).  */
+      /* Use pread64/pwrite64 if available, since they save a syscall
+	 and can handle 64-bit offsets even on 32-bit platforms (for
+	 instance, SPARC debugging a SPARC64 application).  But only
+	 use them if the offset isn't so high that when cast to off_t
+	 it'd be negative, as seen on SPARC64.  pread64/pwrite64
+	 outright reject such offsets.  lseek does not.  */
 #ifdef HAVE_PREAD64
-      bytes = (readbuf != nullptr
-	       ? pread64 (fd, readbuf, len, memaddr)
-	       : pwrite64 (fd, writebuf, len, memaddr));
-#else
-      bytes = -1;
-      if (lseek (fd, memaddr, SEEK_SET) != -1)
-	bytes = (readbuf != nullptr
-		 ? read (fd, readbuf, len)
-		 : write (fd, writebuf, len));
+      if ((off_t) memaddr >= 0)
+	{
+	  bytes = (readbuf != nullptr
+		   ? pread64 (fd, readbuf, len, memaddr)
+		   : pwrite64 (fd, writebuf, len, memaddr));
+	}
+      else
 #endif
+	{
+	  bytes = -1;
+	  if (lseek (fd, memaddr, SEEK_SET) != -1)
+	    {
+	      bytes = (readbuf != nullptr
+		       ? read (fd, readbuf, len)
+		       : write (fd, writebuf, len));
+	    }
+	}
 
       if (bytes < 0)
 	return errno;

base-commit: a5042543d819df8a76ebec8c4d715f244efbab0a
-- 
2.34.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525)
  2023-07-06 13:54     ` Pedro Alves
@ 2023-07-06 15:25       ` Tom Tromey
  2023-07-06 16:47         ` Pedro Alves
  0 siblings, 1 reply; 9+ messages in thread
From: Tom Tromey @ 2023-07-06 15:25 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Andrew Burgess, gdb-patches, Matt Turner

>>>>> "Pedro" == Pedro Alves <pedro@palves.net> writes:

Pedro> I just realized that the lseek path, which I just reindented, has an if branch
Pedro> with more than one line which isn't using braces...  The patch should at least be
Pedro> consistent in all the lines it touches...  :-)  If we follow the documented convention,
Pedro> then here's what we get.

Pedro> WDYT?  Should we tweak the convention instead?

I tend to interpret this rule as "two statements and/or comments", so
braces are required if there's a comment but not for a single statement
that happens to be broken up across multiple lines.

Tom

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525)
  2023-07-06 15:25       ` Tom Tromey
@ 2023-07-06 16:47         ` Pedro Alves
  2023-07-06 17:00           ` Pedro Alves
  2023-07-07 15:18           ` Tom Tromey
  0 siblings, 2 replies; 9+ messages in thread
From: Pedro Alves @ 2023-07-06 16:47 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Andrew Burgess, gdb-patches, Matt Turner

On 2023-07-06 16:25, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <pedro@palves.net> writes:
> 
> Pedro> I just realized that the lseek path, which I just reindented, has an if branch
> Pedro> with more than one line which isn't using braces...  The patch should at least be
> Pedro> consistent in all the lines it touches...  :-)  If we follow the documented convention,
> Pedro> then here's what we get.
> 
> Pedro> WDYT?  Should we tweak the convention instead?
> 
> I tend to interpret this rule as "two statements and/or comments", so
> braces are required if there's a comment but not for a single statement
> that happens to be broken up across multiple lines.
> 

I don't see how the current wording leaves any room for interpretation, but
I could definitely see that the wording may have overreached the original intention.

I remembered this rule being discussed on the list, (but not the discussion itself),
so I did some digging, and found it.

It all started here:

  https://inbox.sourceware.org/gdb-patches/20111218115931.GA22952@host2.jankratochvil.net/

and then a few weeks later another similar discussion happened, which led Jan proposing
adding formalizing the convention:

  https://inbox.sourceware.org/gdb-patches/CADPb22Q9qi3TsYw3ZFkAyuO6a0VEJ_wtM_4pe_tXzDW5myi29Q@mail.gmail.com/

Curiously, Eli had pointed out the "comments or more ?" issue here:

  https://inbox.sourceware.org/gdb-patches/831ur4kot7.fsf@gnu.org/

but sadly there was no answer.

From what I can tell in those two threads, this was really only about the comments case.  I'll
drop the braces from my patch and merge it.

As for the text in the wiki, how about something like below.  (Note this tweaks the existing
example to use "cond" instead of "i", since we don't do implicit bool conversions, and "i"
looks like an int.)

```
Per the GNU coding standards, single statements are not wrapped in braces, while multiple statements are.

Thus, if you need to wrap a statement, write:

if (cond)
  func (foo (),
        bar ());

and not:

if (cond)
  {
    func (foo (),
          bar ());
  }

As a GDB-specific rule, statements preceded by comments should also be wrapped in braces, as they look like separate statements:

if (cond)
  {
    /* Return success.  */
    return 0;
  }

and not:

if (cond)
  /* Return success.  */
  return 0;
```


Oh the irony:

 https://inbox.sourceware.org/gdb-patches/4F1011EA.1030509@redhat.com/

:-)

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525)
  2023-07-06 16:47         ` Pedro Alves
@ 2023-07-06 17:00           ` Pedro Alves
  2023-07-07 15:18           ` Tom Tromey
  1 sibling, 0 replies; 9+ messages in thread
From: Pedro Alves @ 2023-07-06 17:00 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Andrew Burgess, gdb-patches, Matt Turner

On 2023-07-06 17:47, Pedro Alves wrote:

> From what I can tell in those two threads, this was really only about the comments case.  I'll
> drop the braces from my patch and merge it.

Done, like so.  Just realized I forgot to add Andrew's tag.  Sorry about that...

From 31a56a22c45d76df4c597439f337e3f75ac3065c Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Wed, 7 Jun 2023 10:38:14 +0100
Subject: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR
 gdb/30525)

Since commit 05c06f318fd9 ("Linux: Access memory even if threads are
running"), GDB prefers pread64/pwrite64 to access inferior memory
instead of ptrace.  That change broke reading shared libraries on
SPARC64 Linux, as reported by PR gdb/30525 ("gdb cannot read shared
libraries on SPARC64").

On SPARC64 Linux, surprisingly (to me), userspace shared libraries are
mapped at high 64-bit addresses:

   (gdb) info sharedlibrary
   Cannot access memory at address 0xfff80001002011e0
   Cannot access memory at address 0xfff80001002011d8
   Cannot access memory at address 0xfff80001002011d8
   From                To                  Syms Read   Shared Object Library
   0xfff80001000010a0  0xfff8000100021f80  Yes (*)     /lib64/ld-linux.so.2
   (*): Shared library is missing debugging information.

Those addresses are 64-bit addresses with the high bits set.  When
interpreted as signed, they're negative.

The Linux kernel rejects pread64/pwrite64 if the offset argument of
type off_t (a signed type) is negative, which happens if the memory
address we're accessing has its high bit set.  See
linux/fs/read_write.c sys_pread64 and sys_pwrite64 in Linux.

Thankfully, lseek does not fail in that situation.  So the fix is to
use the 'lseek + read|write' path if the offset would be negative.

Fix this in both native GDB and GDBserver.

Tested on a SPARC64 GNU/Linux and x86-64 GNU/Linux.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30525
Change-Id: I79c724f918037ea67b7396fadb521bc9d1b10dc5
---
 gdb/linux-nat.c        | 28 ++++++++++++++++++----------
 gdbserver/linux-low.cc | 29 +++++++++++++++++------------
 2 files changed, 35 insertions(+), 22 deletions(-)

diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 383ef58fa23..4f7b6f8194f 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -3909,18 +3909,26 @@ linux_proc_xfer_memory_partial_fd (int fd, int pid,
 
   gdb_assert (fd != -1);
 
-  /* Use pread64/pwrite64 if available, since they save a syscall and can
-     handle 64-bit offsets even on 32-bit platforms (for instance, SPARC
-     debugging a SPARC64 application).  */
+  /* Use pread64/pwrite64 if available, since they save a syscall and
+     can handle 64-bit offsets even on 32-bit platforms (for instance,
+     SPARC debugging a SPARC64 application).  But only use them if the
+     offset isn't so high that when cast to off_t it'd be negative, as
+     seen on SPARC64.  pread64/pwrite64 outright reject such offsets.
+     lseek does not.  */
 #ifdef HAVE_PREAD64
-  ret = (readbuf ? pread64 (fd, readbuf, len, offset)
-	 : pwrite64 (fd, writebuf, len, offset));
-#else
-  ret = lseek (fd, offset, SEEK_SET);
-  if (ret != -1)
-    ret = (readbuf ? read (fd, readbuf, len)
-	   : write (fd, writebuf, len));
+  if ((off_t) offset >= 0)
+    ret = (readbuf != nullptr
+	   ? pread64 (fd, readbuf, len, offset)
+	   : pwrite64 (fd, writebuf, len, offset));
+  else
 #endif
+    {
+      ret = lseek (fd, offset, SEEK_SET);
+      if (ret != -1)
+	ret = (readbuf != nullptr
+	       ? read (fd, readbuf, len)
+	       : write (fd, writebuf, len));
+    }
 
   if (ret == -1)
     {
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 8ab16698632..651f219b738 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -5377,21 +5377,26 @@ proc_xfer_memory (CORE_ADDR memaddr, unsigned char *readbuf,
     {
       int bytes;
 
-      /* If pread64 is available, use it.  It's faster if the kernel
-	 supports it (only one syscall), and it's 64-bit safe even on
-	 32-bit platforms (for instance, SPARC debugging a SPARC64
-	 application).  */
+      /* Use pread64/pwrite64 if available, since they save a syscall
+	 and can handle 64-bit offsets even on 32-bit platforms (for
+	 instance, SPARC debugging a SPARC64 application).  But only
+	 use them if the offset isn't so high that when cast to off_t
+	 it'd be negative, as seen on SPARC64.  pread64/pwrite64
+	 outright reject such offsets.  lseek does not.  */
 #ifdef HAVE_PREAD64
-      bytes = (readbuf != nullptr
-	       ? pread64 (fd, readbuf, len, memaddr)
-	       : pwrite64 (fd, writebuf, len, memaddr));
-#else
-      bytes = -1;
-      if (lseek (fd, memaddr, SEEK_SET) != -1)
+      if ((off_t) memaddr >= 0)
 	bytes = (readbuf != nullptr
-		 ? read (fd, readbuf, len)
-		 : write (fd, writebuf, len));
+		 ? pread64 (fd, readbuf, len, memaddr)
+		 : pwrite64 (fd, writebuf, len, memaddr));
+      else
 #endif
+	{
+	  bytes = -1;
+	  if (lseek (fd, memaddr, SEEK_SET) != -1)
+	    bytes = (readbuf != nullptr
+		     ? read (fd, readbuf, len)
+		     : write (fd, writebuf, len));
+	}
 
       if (bytes < 0)
 	return errno;

base-commit: c0c3bb70f2f13e07295041cdf24a4d2997fe99a4
-- 
2.34.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525)
  2023-07-06 16:47         ` Pedro Alves
  2023-07-06 17:00           ` Pedro Alves
@ 2023-07-07 15:18           ` Tom Tromey
  1 sibling, 0 replies; 9+ messages in thread
From: Tom Tromey @ 2023-07-07 15:18 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, Andrew Burgess, gdb-patches, Matt Turner

> As for the text in the wiki, how about something like below.  (Note
> this tweaks the existing example to use "cond" instead of "i", since
> we don't do implicit bool conversions, and "i" looks like an int.)

Looks good to me.

> Oh the irony:
>  https://inbox.sourceware.org/gdb-patches/4F1011EA.1030509@redhat.com/
> :-)

A bikeshed never really has a final color.

Tom

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2023-07-07 15:18 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-05 13:41 [PATCH] Linux: Avoid pread64/pwrite64 for high memory addresses (PR gdb/30525) Pedro Alves
2023-07-05 14:45 ` Matt Turner
2023-07-05 17:59 ` Andrew Burgess
2023-07-06 13:43   ` Pedro Alves
2023-07-06 13:54     ` Pedro Alves
2023-07-06 15:25       ` Tom Tromey
2023-07-06 16:47         ` Pedro Alves
2023-07-06 17:00           ` Pedro Alves
2023-07-07 15:18           ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).