From: Tom de Vries <tdevries@suse.de>
To: Simon Marchi <simark@simark.ca>, Tom Tromey <tom@tromey.com>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH] [gdb/python] Throw MemoryError in inferior.read_memory if malloc fails
Date: Wed, 17 Apr 2024 10:29:08 +0200 [thread overview]
Message-ID: <5dda7d2f-82c9-4154-86fa-8bedeb962f0e@suse.de> (raw)
In-Reply-To: <40c3c863-1369-4f4f-bef4-f2d35357c8eb@simark.ca>
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
prev parent reply other threads:[~2024-04-17 8:28 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-11 10:52 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 message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5dda7d2f-82c9-4154-86fa-8bedeb962f0e@suse.de \
--to=tdevries@suse.de \
--cc=gdb-patches@sourceware.org \
--cc=simark@simark.ca \
--cc=tom@tromey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).