* [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails
@ 2024-04-11 10:52 Tom de Vries
2024-04-11 16:07 ` Tom Tromey
0 siblings, 1 reply; 7+ messages in thread
From: Tom de Vries @ 2024-04-11 10:52 UTC (permalink / raw)
To: gdb-patches
PR python/31631 reports a gdb internal error when doing:
...
(gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
utils.c:709: internal-error: virtual memory exhausted.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
...
Fix this by throwing a python MemoryError, such that we have instead:
...
(gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
Python Exception <class 'MemoryError'>:
Error occurred in Python.
(gdb)
...
Likewise for DAP.
Tested on x86_64-linux.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31631
---
gdb/python/lib/gdb/dap/memory.py | 9 ++++++++-
gdb/python/py-inferior.c | 11 ++++++++++-
gdb/testsuite/gdb.dap/memory.exp | 5 +++++
gdb/testsuite/gdb.python/py-inferior.exp | 6 ++++++
4 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/gdb/python/lib/gdb/dap/memory.py b/gdb/python/lib/gdb/dap/memory.py
index dd62b0e7ba6..0fd5c2128e5 100644
--- a/gdb/python/lib/gdb/dap/memory.py
+++ b/gdb/python/lib/gdb/dap/memory.py
@@ -18,13 +18,20 @@ import base64
import gdb
from .server import capability, request
+from .startup import DAPException
@request("readMemory")
@capability("supportsReadMemoryRequest")
def read_memory(*, memoryReference: str, offset: int = 0, count: int, **extra):
addr = int(memoryReference, 0) + offset
- buf = gdb.selected_inferior().read_memory(addr, count)
+ oom = False
+ try:
+ buf = gdb.selected_inferior().read_memory(addr, count)
+ except MemoryError:
+ oom = True
+ if oom:
+ raise DAPException("Out of memory")
return {
"address": hex(addr),
"data": base64.b64encode(buf).decode("ASCII"),
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 795ac655ddd..e825e649533 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -562,7 +562,16 @@ infpy_read_memory (PyObject *self, PyObject *args, PyObject *kw)
scoped_restore_current_inferior_for_memory restore_inferior
(inf->inferior);
- buffer.reset ((gdb_byte *) xmalloc (length));
+ /* We used to use xmalloc, which does this trick to avoid malloc
+ returning a nullptr for a valid reason. Keep doing the same. */
+ if (length == 0)
+ length = 1;
+
+ void *p = malloc (length);
+ if (p == nullptr)
+ return PyErr_NoMemory ();
+
+ buffer.reset ((gdb_byte *) p);
read_memory (addr, buffer.get (), length);
}
diff --git a/gdb/testsuite/gdb.dap/memory.exp b/gdb/testsuite/gdb.dap/memory.exp
index 2e911f4dc77..4e2e361289a 100644
--- a/gdb/testsuite/gdb.dap/memory.exp
+++ b/gdb/testsuite/gdb.dap/memory.exp
@@ -55,6 +55,11 @@ set obj [dap_check_request_and_response "evaluate global pointer" \
evaluate {o expression [s thirty_two_p]}]
set addr [dict get [lindex $obj 0] body memoryReference]
+set obj [dap_request_and_response \
+ readMemory [format {o memoryReference [s %s] count [i 18446744073709551615]} $addr]]
+set response [lindex $obj 0]
+gdb_assert { [dict get $response success] == "false" } "read memory, count to big"
+
set obj [dap_check_request_and_response "read memory" \
readMemory [format {o memoryReference [s %s] count [i 4]} $addr]]
diff --git a/gdb/testsuite/gdb.python/py-inferior.exp b/gdb/testsuite/gdb.python/py-inferior.exp
index c14f2d2796c..4c19e259159 100644
--- a/gdb/testsuite/gdb.python/py-inferior.exp
+++ b/gdb/testsuite/gdb.python/py-inferior.exp
@@ -34,6 +34,12 @@ switch [get_endianness] {
big { set python_pack_char ">" }
}
+gdb_test \
+ "python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)" \
+ [multi_line \
+ [string_to_regexp "Python Exception <class 'MemoryError'>: "] \
+ [string_to_regexp "Error occurred in Python."]]
+
# Test memory read operations without execution.
gdb_py_test_silent_cmd "python addr = gdb.lookup_global_symbol ('int8_global').value().address" \
base-commit: af925905211930677751678183f43c1bda13e013
--
2.35.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails
2024-04-11 10:52 [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails Tom de Vries
@ 2024-04-11 16:07 ` Tom Tromey
2024-04-12 7:09 ` Tom de Vries
0 siblings, 1 reply; 7+ messages in thread
From: Tom Tromey @ 2024-04-11 16:07 UTC (permalink / raw)
To: Tom de Vries; +Cc: gdb-patches
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
Tom> PR python/31631 reports a gdb internal error when doing:
Tom> ...
Tom> (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
Tom> utils.c:709: internal-error: virtual memory exhausted.
Tom> A problem internal to GDB has been detected,
Tom> further debugging may prove unreliable.
Tom> ...
Tom> Fix this by throwing a python MemoryError, such that we have instead:
Tom> ...
Tom> (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
Tom> Python Exception <class 'MemoryError'>:
Tom> Error occurred in Python.
Tom> (gdb)
Tom> ...
I tend to think you will regret opening this door, because I imagine
there are a large number of ways to crash gdb by passing nonsensical
values to Python APIs.
Tom> @request("readMemory")
Tom> @capability("supportsReadMemoryRequest")
Tom> def read_memory(*, memoryReference: str, offset: int = 0, count: int, **extra):
Tom> addr = int(memoryReference, 0) + offset
Tom> - buf = gdb.selected_inferior().read_memory(addr, count)
Tom> + oom = False
Tom> + try:
Tom> + buf = gdb.selected_inferior().read_memory(addr, count)
Tom> + except MemoryError:
Tom> + oom = True
Tom> + if oom:
Tom> + raise DAPException("Out of memory")
This should probably chain the memory error in the except block and
re-throw. See https://peps.python.org/pep-3134/
However I don't really understand why this is needed. Isn't the
exception already propagated back to the server thread?
Tom> + /* We used to use xmalloc, which does this trick to avoid malloc
Tom> + returning a nullptr for a valid reason. Keep doing the same. */
Tom> + if (length == 0)
Tom> + length = 1;
This is most likely a workaround for vendor implementations of malloc
that return NULL for malloc(0). See
https://www.gnu.org/software/gnulib/manual/html_node/malloc.html
However, here this is not necessary, because a 0-length memory read is
meaningless, and so this case can simply be reported as an error.
Tom
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails
2024-04-11 16:07 ` Tom Tromey
@ 2024-04-12 7:09 ` Tom de Vries
2024-04-15 16:16 ` Tom Tromey
2024-04-16 19:10 ` Simon Marchi
0 siblings, 2 replies; 7+ messages in thread
From: Tom de Vries @ 2024-04-12 7:09 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 4874 bytes --]
On 4/11/24 18:07, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
>
> Tom> PR python/31631 reports a gdb internal error when doing:
> Tom> ...
> Tom> (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
> Tom> utils.c:709: internal-error: virtual memory exhausted.
> Tom> A problem internal to GDB has been detected,
> Tom> further debugging may prove unreliable.
> Tom> ...
>
> Tom> Fix this by throwing a python MemoryError, such that we have instead:
> Tom> ...
> Tom> (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
> Tom> Python Exception <class 'MemoryError'>:
> Tom> Error occurred in Python.
> Tom> (gdb)
> Tom> ...
>
> I tend to think you will regret opening this door, because I imagine
> there are a large number of ways to crash gdb by passing nonsensical
> values to Python APIs.
>
Hm, ok then let's hope I don't regret it.
> Tom> @request("readMemory")
> Tom> @capability("supportsReadMemoryRequest")
> Tom> def read_memory(*, memoryReference: str, offset: int = 0, count: int, **extra):
> Tom> addr = int(memoryReference, 0) + offset
> Tom> - buf = gdb.selected_inferior().read_memory(addr, count)
> Tom> + oom = False
> Tom> + try:
> Tom> + buf = gdb.selected_inferior().read_memory(addr, count)
> Tom> + except MemoryError:
> Tom> + oom = True
> Tom> + if oom:
> Tom> + raise DAPException("Out of memory")
>
> This should probably chain the memory error in the except block and
> re-throw. See https://peps.python.org/pep-3134/
>
Ack, that's what I had initially, but I ran into an error that I can no
longer reproduce ... so, I'm not sure what happened there. Anyway, fixed.
> However I don't really understand why this is needed. Isn't the
> exception already propagated back to the server thread?
>
Without it I run into:
...
FAIL: gdb.dap/memory.exp: exceptions in log file
...
because in the dap log we have:
...
READ: <<<{"seq": 7, "type": "request", "command": "readMemory",
"arguments": {"memoryReference": "0x402010", "count":
18446744073709551615}}>>>
Traceback (most recent call last):
File
"/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py",
line 157, in _handle_command
body = _commands[params["command"]](**args)
File
"/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py",
line 300, in check
return func(*args, **kwargs)
File
"/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py",
line 360, in sync_call
return send_gdb_with_response(lambda: func(**args))
File
"/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py",
line 514, in send_gdb_with_response
raise val
File
"/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py",
line 470, in __call__
val = self.fn()
File
"/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py",
line 360, in <lambda>
return send_gdb_with_response(lambda: func(**args))
File
"/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/startup.py",
line 113, in ensure_gdb_thread
return func(*args, **kwargs)
File
"/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/memory.py",
line 28, in read_memory
buf = gdb.selected_inferior().read_memory(addr, count)
MemoryError
WROTE: <<<{"request_seq": 7, "type": "response", "command":
"readMemory", "success": false, "message": ""}>>>
...
So that needs fixing here:
...
diff --git a/gdb/python/lib/gdb/dap/server.py
b/gdb/python/lib/gdb/dap/server.py
index 7eb87177710..8408232eceb 100644
--- a/gdb/python/lib/gdb/dap/server.py
+++ b/gdb/python/lib/gdb/dap/server.py
@@ -173,6 +173,9 @@ class Server:
log_stack(LogLevel.FULL)
result["success"] = False
result["message"] = str(e)
+ except MemoryError as e:
+ result["success"] = False
+ result["message"] = str(e)
except BaseException as e:
...
But I couldn't come up with a good rationale as to why I should handle
MemoryError differently from BaseException, so I decided to catch it
locally and turn it into a DAPException.
> Tom> + /* We used to use xmalloc, which does this trick to avoid malloc
> Tom> + returning a nullptr for a valid reason. Keep doing the same. */
> Tom> + if (length == 0)
> Tom> + length = 1;
>
> This is most likely a workaround for vendor implementations of malloc
> that return NULL for malloc(0). See
> https://www.gnu.org/software/gnulib/manual/html_node/malloc.html
>
> However, here this is not necessary, because a 0-length memory read is
> meaningless, and so this case can simply be reported as an error.
I went for backward compatibility here, but ok, fixed. Updated version
attached.
Thanks,
- Tom
[-- Attachment #2: 0001-gdb-python-Throw-MemoryError-in-inferior.read_memory.patch --]
[-- Type: text/x-patch, Size: 4490 bytes --]
From 96f62bf65fe8a43175820f99c890daf184533d5e Mon Sep 17 00:00:00 2001
From: Tom de Vries <tdevries@suse.de>
Date: Thu, 11 Apr 2024 10:42:39 +0200
Subject: [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if
malloc fails
PR python/31631 reports a gdb internal error when doing:
...
(gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
utils.c:709: internal-error: virtual memory exhausted.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
...
Fix this by throwing a python MemoryError, such that we have instead:
...
(gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
Python Exception <class 'MemoryError'>:
Error occurred in Python.
(gdb)
...
Likewise for DAP.
Tested on x86_64-linux.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31631
---
gdb/python/lib/gdb/dap/memory.py | 6 +++++-
gdb/python/py-inferior.c | 14 ++++++++++++--
gdb/testsuite/gdb.dap/memory.exp | 5 +++++
gdb/testsuite/gdb.python/py-inferior.exp | 6 ++++++
4 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/gdb/python/lib/gdb/dap/memory.py b/gdb/python/lib/gdb/dap/memory.py
index dd62b0e7ba6..48aac8f4d7c 100644
--- a/gdb/python/lib/gdb/dap/memory.py
+++ b/gdb/python/lib/gdb/dap/memory.py
@@ -18,13 +18,17 @@ import base64
import gdb
from .server import capability, request
+from .startup import DAPException
@request("readMemory")
@capability("supportsReadMemoryRequest")
def read_memory(*, memoryReference: str, offset: int = 0, count: int, **extra):
addr = int(memoryReference, 0) + offset
- buf = gdb.selected_inferior().read_memory(addr, count)
+ try:
+ buf = gdb.selected_inferior().read_memory(addr, count)
+ except MemoryError:
+ raise DAPException("Out of memory")
return {
"address": hex(addr),
"data": base64.b64encode(buf).decode("ASCII"),
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 795ac655ddd..a1042ee72ac 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -555,6 +555,18 @@ infpy_read_memory (PyObject *self, PyObject *args, PyObject *kw)
|| get_addr_from_python (length_obj, &length) < 0)
return NULL;
+ if (length == 0)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ _("Argument 'count' should be greater than zero"));
+ return NULL;
+ }
+
+ void *p = malloc (length);
+ if (p == nullptr)
+ return PyErr_NoMemory ();
+ buffer.reset ((gdb_byte *) p);
+
try
{
/* Use this scoped-restore because we want to be able to read
@@ -562,8 +574,6 @@ infpy_read_memory (PyObject *self, PyObject *args, PyObject *kw)
scoped_restore_current_inferior_for_memory restore_inferior
(inf->inferior);
- buffer.reset ((gdb_byte *) xmalloc (length));
-
read_memory (addr, buffer.get (), length);
}
catch (const gdb_exception &except)
diff --git a/gdb/testsuite/gdb.dap/memory.exp b/gdb/testsuite/gdb.dap/memory.exp
index 2e911f4dc77..4e2e361289a 100644
--- a/gdb/testsuite/gdb.dap/memory.exp
+++ b/gdb/testsuite/gdb.dap/memory.exp
@@ -55,6 +55,11 @@ set obj [dap_check_request_and_response "evaluate global pointer" \
evaluate {o expression [s thirty_two_p]}]
set addr [dict get [lindex $obj 0] body memoryReference]
+set obj [dap_request_and_response \
+ readMemory [format {o memoryReference [s %s] count [i 18446744073709551615]} $addr]]
+set response [lindex $obj 0]
+gdb_assert { [dict get $response success] == "false" } "read memory, count to big"
+
set obj [dap_check_request_and_response "read memory" \
readMemory [format {o memoryReference [s %s] count [i 4]} $addr]]
diff --git a/gdb/testsuite/gdb.python/py-inferior.exp b/gdb/testsuite/gdb.python/py-inferior.exp
index c14f2d2796c..4c19e259159 100644
--- a/gdb/testsuite/gdb.python/py-inferior.exp
+++ b/gdb/testsuite/gdb.python/py-inferior.exp
@@ -34,6 +34,12 @@ switch [get_endianness] {
big { set python_pack_char ">" }
}
+gdb_test \
+ "python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)" \
+ [multi_line \
+ [string_to_regexp "Python Exception <class 'MemoryError'>: "] \
+ [string_to_regexp "Error occurred in Python."]]
+
# Test memory read operations without execution.
gdb_py_test_silent_cmd "python addr = gdb.lookup_global_symbol ('int8_global').value().address" \
base-commit: 8bad8d5133f3f91c287e63f8046f040fc4d881e8
--
2.35.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails
2024-04-12 7:09 ` Tom de Vries
@ 2024-04-15 16:16 ` Tom Tromey
2024-04-16 13:55 ` Tom de Vries
2024-04-16 19:10 ` Simon Marchi
1 sibling, 1 reply; 7+ messages in thread
From: Tom Tromey @ 2024-04-15 16:16 UTC (permalink / raw)
To: Tom de Vries; +Cc: Tom Tromey, gdb-patches
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
Tom> But I couldn't come up with a good rationale as to why I should handle
Tom> MemoryError differently from BaseException, so I decided to catch it
Tom> locally and turn it into a DAPException.
Yeah, makes sense.
Tom> + try:
Tom> + buf = gdb.selected_inferior().read_memory(addr, count)
Tom> + except MemoryError:
Tom> + raise DAPException("Out of memory")
Here I meant something like:
except MemoryError as e:
raise DAPException(...) from e
There's an example of this in startup.py.
Tom
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails
2024-04-15 16:16 ` Tom Tromey
@ 2024-04-16 13:55 ` Tom de Vries
0 siblings, 0 replies; 7+ messages in thread
From: Tom de Vries @ 2024-04-16 13:55 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 4/15/24 18:16, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
>
> Tom> But I couldn't come up with a good rationale as to why I should handle
> Tom> MemoryError differently from BaseException, so I decided to catch it
> Tom> locally and turn it into a DAPException.
>
> Yeah, makes sense.
>
> Tom> + try:
> Tom> + buf = gdb.selected_inferior().read_memory(addr, count)
> Tom> + except MemoryError:
> Tom> + raise DAPException("Out of memory")
>
> Here I meant something like:
>
> except MemoryError as e:
> raise DAPException(...) from e
>
> There's an example of this in startup.py.
I see, I didn't know that possibility.
Anyway, AFAICT it doesn't seem to make a difference, it just makes the
chaining explicit, which I prefer.
Pushed with that change.
Thanks,
- Tom
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails
2024-04-12 7:09 ` Tom de Vries
2024-04-15 16:16 ` Tom Tromey
@ 2024-04-16 19:10 ` Simon Marchi
2024-04-17 8:29 ` Tom de Vries
1 sibling, 1 reply; 7+ messages in thread
From: Simon Marchi @ 2024-04-16 19:10 UTC (permalink / raw)
To: Tom de Vries, Tom Tromey; +Cc: gdb-patches
On 4/12/24 3:09 AM, Tom de Vries wrote:
> On 4/11/24 18:07, Tom Tromey wrote:
>>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
>>
>> Tom> PR python/31631 reports a gdb internal error when doing:
>> Tom> ...
>> Tom> (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
>> Tom> utils.c:709: internal-error: virtual memory exhausted.
>> Tom> A problem internal to GDB has been detected,
>> Tom> further debugging may prove unreliable.
>> Tom> ...
>>
>> Tom> Fix this by throwing a python MemoryError, such that we have instead:
>> Tom> ...
>> Tom> (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
>> Tom> Python Exception <class 'MemoryError'>:
>> Tom> Error occurred in Python.
>> Tom> (gdb)
>> Tom> ...
>>
>> I tend to think you will regret opening this door, because I imagine
>> there are a large number of ways to crash gdb by passing nonsensical
>> values to Python APIs.
>>
>
> Hm, ok then let's hope I don't regret it.
>
>> Tom> @request("readMemory")
>> Tom> @capability("supportsReadMemoryRequest")
>> Tom> def read_memory(*, memoryReference: str, offset: int = 0, count: int, **extra):
>> Tom> addr = int(memoryReference, 0) + offset
>> Tom> - buf = gdb.selected_inferior().read_memory(addr, count)
>> Tom> + oom = False
>> Tom> + try:
>> Tom> + buf = gdb.selected_inferior().read_memory(addr, count)
>> Tom> + except MemoryError:
>> Tom> + oom = True
>> Tom> + if oom:
>> Tom> + raise DAPException("Out of memory")
>>
>> This should probably chain the memory error in the except block and
>> re-throw. See https://peps.python.org/pep-3134/
>>
>
> Ack, that's what I had initially, but I ran into an error that I can no longer reproduce ... so, I'm not sure what happened there. Anyway, fixed.
>
>> However I don't really understand why this is needed. Isn't the
>> exception already propagated back to the server thread?
>>
>
> Without it I run into:
> ...
> FAIL: gdb.dap/memory.exp: exceptions in log file
> ...
> because in the dap log we have:
> ...
> READ: <<<{"seq": 7, "type": "request", "command": "readMemory", "arguments": {"memoryReference": "0x402010", "count": 18446744073709551615}}>>>
> Traceback (most recent call last):
> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 157, in _handle_command
> body = _commands[params["command"]](**args)
> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 300, in check
> return func(*args, **kwargs)
> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 360, in sync_call
> return send_gdb_with_response(lambda: func(**args))
> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 514, in send_gdb_with_response
> raise val
> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 470, in __call__
> val = self.fn()
> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 360, in <lambda>
> return send_gdb_with_response(lambda: func(**args))
> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/startup.py", line 113, in ensure_gdb_thread
> return func(*args, **kwargs)
> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/memory.py", line 28, in read_memory
> buf = gdb.selected_inferior().read_memory(addr, count)
> MemoryError
> WROTE: <<<{"request_seq": 7, "type": "response", "command": "readMemory", "success": false, "message": ""}>>>
> ...
>
> So that needs fixing here:
> ...
> diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py
> index 7eb87177710..8408232eceb 100644
> --- a/gdb/python/lib/gdb/dap/server.py
> +++ b/gdb/python/lib/gdb/dap/server.py
> @@ -173,6 +173,9 @@ class Server:
> log_stack(LogLevel.FULL)
> result["success"] = False
> result["message"] = str(e)
> + except MemoryError as e:
> + result["success"] = False
> + result["message"] = str(e)
> except BaseException as e:
> ...
>
> But I couldn't come up with a good rationale as to why I should handle MemoryError differently from BaseException, so I decided to catch it locally and turn it into a DAPException.
>
>> Tom> + /* We used to use xmalloc, which does this trick to avoid malloc
>> Tom> + returning a nullptr for a valid reason. Keep doing the same. */
>> Tom> + if (length == 0)
>> Tom> + length = 1;
>>
>> This is most likely a workaround for vendor implementations of malloc
>> that return NULL for malloc(0). See
>> https://www.gnu.org/software/gnulib/manual/html_node/malloc.html
>>
>> However, here this is not necessary, because a 0-length memory read is
>> meaningless, and so this case can simply be reported as an error.
>
> I went for backward compatibility here, but ok, fixed. Updated version attached.
>
> Thanks,
> - Tom
In an Asan-enabled build, I get:
74 (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)^M
75 =================================================================^M
76 ^[[1m^[[31m==791047==ERROR: AddressSanitizer: requested allocation size 0xffffffffffffffff (0x800 after adjustments for alignment, red zones etc.) exceeds maximum supported size of 0x100 00000000 (thread T0)^M
77 ^[[1m^[[0m #0 0x7f02c58d49cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69^M
78 #1 0x55cc5937f257 in infpy_read_memory /home/smarchi/src/binutils-gdb/gdb/python/py-inferior.c:565^M
79 #2 0x7f02c4ea9709 in method_vectorcall_VARARGS_KEYWORDS ../Objects/descrobject.c:364^M
80 ^M
81 ==791047==HINT: if you don't care about these errors you may set allocator_may_return_null=1^M
82 SUMMARY: AddressSanitizer: allocation-size-too-big ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 in __interceptor_malloc^M
Simon
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails
2024-04-16 19:10 ` Simon Marchi
@ 2024-04-17 8:29 ` Tom de Vries
0 siblings, 0 replies; 7+ messages in thread
From: Tom de Vries @ 2024-04-17 8:29 UTC (permalink / raw)
To: Simon Marchi, Tom Tromey; +Cc: gdb-patches
On 4/16/24 21:10, Simon Marchi wrote:
> On 4/12/24 3:09 AM, Tom de Vries wrote:
>> On 4/11/24 18:07, Tom Tromey wrote:
>>>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
>>>
>>> Tom> PR python/31631 reports a gdb internal error when doing:
>>> Tom> ...
>>> Tom> (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
>>> Tom> utils.c:709: internal-error: virtual memory exhausted.
>>> Tom> A problem internal to GDB has been detected,
>>> Tom> further debugging may prove unreliable.
>>> Tom> ...
>>>
>>> Tom> Fix this by throwing a python MemoryError, such that we have instead:
>>> Tom> ...
>>> Tom> (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)
>>> Tom> Python Exception <class 'MemoryError'>:
>>> Tom> Error occurred in Python.
>>> Tom> (gdb)
>>> Tom> ...
>>>
>>> I tend to think you will regret opening this door, because I imagine
>>> there are a large number of ways to crash gdb by passing nonsensical
>>> values to Python APIs.
>>>
>>
>> Hm, ok then let's hope I don't regret it.
>>
>>> Tom> @request("readMemory")
>>> Tom> @capability("supportsReadMemoryRequest")
>>> Tom> def read_memory(*, memoryReference: str, offset: int = 0, count: int, **extra):
>>> Tom> addr = int(memoryReference, 0) + offset
>>> Tom> - buf = gdb.selected_inferior().read_memory(addr, count)
>>> Tom> + oom = False
>>> Tom> + try:
>>> Tom> + buf = gdb.selected_inferior().read_memory(addr, count)
>>> Tom> + except MemoryError:
>>> Tom> + oom = True
>>> Tom> + if oom:
>>> Tom> + raise DAPException("Out of memory")
>>>
>>> This should probably chain the memory error in the except block and
>>> re-throw. See https://peps.python.org/pep-3134/
>>>
>>
>> Ack, that's what I had initially, but I ran into an error that I can no longer reproduce ... so, I'm not sure what happened there. Anyway, fixed.
>>
>>> However I don't really understand why this is needed. Isn't the
>>> exception already propagated back to the server thread?
>>>
>>
>> Without it I run into:
>> ...
>> FAIL: gdb.dap/memory.exp: exceptions in log file
>> ...
>> because in the dap log we have:
>> ...
>> READ: <<<{"seq": 7, "type": "request", "command": "readMemory", "arguments": {"memoryReference": "0x402010", "count": 18446744073709551615}}>>>
>> Traceback (most recent call last):
>> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 157, in _handle_command
>> body = _commands[params["command"]](**args)
>> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 300, in check
>> return func(*args, **kwargs)
>> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 360, in sync_call
>> return send_gdb_with_response(lambda: func(**args))
>> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 514, in send_gdb_with_response
>> raise val
>> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 470, in __call__
>> val = self.fn()
>> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/server.py", line 360, in <lambda>
>> return send_gdb_with_response(lambda: func(**args))
>> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/startup.py", line 113, in ensure_gdb_thread
>> return func(*args, **kwargs)
>> File "/data/vries/gdb/leap-15-5/build/gdb/data-directory/python/gdb/dap/memory.py", line 28, in read_memory
>> buf = gdb.selected_inferior().read_memory(addr, count)
>> MemoryError
>> WROTE: <<<{"request_seq": 7, "type": "response", "command": "readMemory", "success": false, "message": ""}>>>
>> ...
>>
>> So that needs fixing here:
>> ...
>> diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py
>> index 7eb87177710..8408232eceb 100644
>> --- a/gdb/python/lib/gdb/dap/server.py
>> +++ b/gdb/python/lib/gdb/dap/server.py
>> @@ -173,6 +173,9 @@ class Server:
>> log_stack(LogLevel.FULL)
>> result["success"] = False
>> result["message"] = str(e)
>> + except MemoryError as e:
>> + result["success"] = False
>> + result["message"] = str(e)
>> except BaseException as e:
>> ...
>>
>> But I couldn't come up with a good rationale as to why I should handle MemoryError differently from BaseException, so I decided to catch it locally and turn it into a DAPException.
>>
>>> Tom> + /* We used to use xmalloc, which does this trick to avoid malloc
>>> Tom> + returning a nullptr for a valid reason. Keep doing the same. */
>>> Tom> + if (length == 0)
>>> Tom> + length = 1;
>>>
>>> This is most likely a workaround for vendor implementations of malloc
>>> that return NULL for malloc(0). See
>>> https://www.gnu.org/software/gnulib/manual/html_node/malloc.html
>>>
>>> However, here this is not necessary, because a 0-length memory read is
>>> meaningless, and so this case can simply be reported as an error.
>>
>> I went for backward compatibility here, but ok, fixed. Updated version attached.
>>
>> Thanks,
>> - Tom
>
> In an Asan-enabled build, I get:
>
>
> 74 (gdb) python gdb.selected_inferior().read_memory (0, 0xffffffffffffffff)^M
> 75 =================================================================^M
> 76 ^[[1m^[[31m==791047==ERROR: AddressSanitizer: requested allocation size 0xffffffffffffffff (0x800 after adjustments for alignment, red zones etc.) exceeds maximum supported size of 0x100 00000000 (thread T0)^M
> 77 ^[[1m^[[0m #0 0x7f02c58d49cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69^M
> 78 #1 0x55cc5937f257 in infpy_read_memory /home/smarchi/src/binutils-gdb/gdb/python/py-inferior.c:565^M
> 79 #2 0x7f02c4ea9709 in method_vectorcall_VARARGS_KEYWORDS ../Objects/descrobject.c:364^M
> 80 ^M
> 81 ==791047==HINT: if you don't care about these errors you may set allocator_may_return_null=1^M
> 82 SUMMARY: AddressSanitizer: allocation-size-too-big ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 in __interceptor_malloc^M
>
Hi Simon,
thanks for the report.
I've submitted a fix here (
https://sourceware.org/pipermail/gdb-patches/2024-April/208178.html ).
Thanks,
- Tom
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2024-04-17 8:28 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-11 10:52 [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails Tom de Vries
2024-04-11 16:07 ` Tom Tromey
2024-04-12 7:09 ` Tom de Vries
2024-04-15 16:16 ` Tom Tromey
2024-04-16 13:55 ` Tom de Vries
2024-04-16 19:10 ` Simon Marchi
2024-04-17 8:29 ` Tom de Vries
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).