Skip to content

Parse remote protocol integers without throwing - #1166

Open
xusheng6 wants to merge 2 commits into
devfrom
test_safe_int_parsing
Open

Parse remote protocol integers without throwing#1166
xusheng6 wants to merge 2 commits into
devfrom
test_safe_int_parsing

Conversation

@xusheng6

@xusheng6 xusheng6 commented Aug 3, 2026

Copy link
Copy Markdown
Member

Add RspConnector::ParseInt (based on std::from_chars) and use it instead of std::stoi/stoull when parsing data from the remote debug stub, so malformed packets can no longer crash the process. Also guard short-packet substr/index accesses in PacketToUnorderedMap.

Fixes #1164

std::stoi/stol/stoull raise std::invalid_argument or std::out_of_range
on malformed input. The RSP/GDB adapters called them directly on data
received from the remote debug stub, so a malformed packet could crash
the process with an uncaught exception. Add RspConnector::ParseInt, a
std::from_chars-based helper that returns a fallback value instead, and
use it at all unguarded call sites. Also guard the packet substr/index
accesses in PacketToUnorderedMap against short packets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@xusheng6
xusheng6 requested a review from plafosse August 3, 2026 18:44
@plafosse

plafosse commented Aug 3, 2026

Copy link
Copy Markdown
Member

Why is it ok for all these sites to just default to 0 on failure? Seems like some errors should be raised or warnings displayed when the failure conditions occur?

@xusheng6

xusheng6 commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

Why is it ok for all these sites to just default to 0 on failure? Seems like some errors should be raised or warnings displayed when the failure conditions occur?

Usually when this happens, the communication already deviated from the expected way in an irrecoverable way (e.g., the GDB stub and our adapter has a different assumption of the RSP protocol), so the actual value does not matter as long we we do not crash ourselves.

I think we can add a warning when this happens so as to assist troubleshooting, do you want that to be done?

@plafosse

plafosse commented Aug 4, 2026

Copy link
Copy Markdown
Member

This kind feels like the opposite of what you should be doing. It seems like instead of just silently accepting corrupted rsp messages you need to throw and exception and you just need to have a more principled approach to what you do when this occurs. So instead of preventing exceptions you just need to catch them in the correct locations so you can do proper teardown.

Addressing review feedback: defaulting to 0 on a parse failure means a corrupt
packet silently becomes a valid-looking answer. A bad thread id turns into thread
0, and the debugger then shows the user the wrong thread's registers with no
indication anything went wrong. For a debugger that is worse than stopping.

Split parsing in two, so the choice is made per call site rather than by one
blanket default:

- ParseInt throws RspProtocolError, naming the field, for values the protocol
  requires. It also now requires the whole field to be consumed, since trailing
  junk in an integer field is itself evidence of desynchronization.
- ParseIntOr takes an explicit fallback, for the two places where a default is
  genuinely correct: the optional PacketSize capability hint, and the advisory
  errno in a host I/O reply. Both say why at the call site.

The exception is caught in exactly one place, DebuggerController::
ExecuteAdapterAndWait, which every adapter operation already funnels through on
the worker thread. It reports the offending data and ends the session, rather
than unwinding into a worker thread with no handler and aborting the process.
RspProtocolError lives in its own header so that boundary can name it without
pulling socket.h and the platform networking headers into the controller.

Also fixes multiprocess thread ids, which never parsed: Split takes a regex, so
the "." separator matched every character and produced only empty tokens. A
"pPID.TID" thread id therefore always reached the parser as an empty string,
which crashed on dev. Escaped to "\\.".

Verified against a live Corellium stub: its real stop reply parses, malformed
packets raise a typed error naming the field, and well-formed packets including
"pPID.TID" still parse.

Refs #1164
@xusheng6

Copy link
Copy Markdown
Member Author

This kind feels like the opposite of what you should be doing. It seems like instead of just silently accepting corrupted rsp messages you need to throw and exception and you just need to have a more principled approach to what you do when this occurs. So instead of preventing exceptions you just need to catch them in the correct locations so you can do proper teardown.

I made the code to throw RspProtocolError with an explanation of the error. When the exception is caught and handled, the debugging is gracefully stopped and an LogError is used to communicate the error to the user

Would you please have a look at it again? @plafosse

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Malformed remote stub data can crash via unguarded std::stoi/stoull

2 participants