From e3bea19784481202ff82422e2007e618e7924014 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 30 Jul 2026 21:49:15 +1000 Subject: [PATCH] GDBServer: Expand watchpoint ranges Interpret Z2/Z3/Z4 kind values as byte lengths and represent each watched byte with an owned CPU breakpoint. Validate MIPS execute-breakpoint kinds, reject zero, overflowing, or excessively large watch ranges, and avoid invasive changes to the CPU memory-access dispatcher. --- src/core/gdb_server.cpp | 45 +++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/core/gdb_server.cpp b/src/core/gdb_server.cpp index 08dad62b2..4a3517e42 100644 --- a/src/core/gdb_server.cpp +++ b/src/core/gdb_server.cpp @@ -19,6 +19,7 @@ #include "util/sockets.h" +#include #include #include @@ -31,6 +32,7 @@ static constexpr u8 GDB_SIGNAL_TRAP = 5; static constexpr u32 MAX_PACKET_SIZE = 16384; static constexpr u32 MAX_FRAMED_PACKET_SIZE = MAX_PACKET_SIZE + 4; static constexpr u32 MAX_MEMORY_READ_SIZE = MAX_PACKET_SIZE / 2; +static constexpr u32 MAX_WATCHPOINT_LENGTH = 256; namespace { @@ -509,6 +511,23 @@ bool GDBServer::Cmd$z(ClientSocket* client, std::string_view data) } const GDBBreakpointType type = static_cast(bptype.value()); + if (type == GDBBreakpointType::Software || type == GDBBreakpointType::Hardware) + { + if (bpkind.value() != sizeof(CPU::Instruction)) + { + ERROR_LOG("Unsupported breakpoint kind {}", bpkind.value()); + client->SendReplyWithAck("E01"); + return true; + } + } + else if (bpkind.value() == 0 || bpkind.value() > MAX_WATCHPOINT_LENGTH || + bpaddr.value() > (std::numeric_limits::max() - (bpkind.value() - 1))) + { + ERROR_LOG("Invalid watchpoint length {} at 0x{:08X}", bpkind.value(), bpaddr.value()); + client->SendReplyWithAck("E01"); + return true; + } + if constexpr (add_breakpoint) client->AddBreakpoint(type, bpaddr.value(), bpkind.value()); else @@ -681,16 +700,21 @@ void GDBServer::ClientSocket::AddBreakpoint(GDBBreakpointType type, VirtualMemor break; case GDBBreakpointType::Write: - acquire_breakpoint(CPU::BreakpointType::Write, address); + for (u32 offset = 0; offset < kind; offset++) + acquire_breakpoint(CPU::BreakpointType::Write, address + offset); break; case GDBBreakpointType::Read: - acquire_breakpoint(CPU::BreakpointType::Read, address); + for (u32 offset = 0; offset < kind; offset++) + acquire_breakpoint(CPU::BreakpointType::Read, address + offset); break; case GDBBreakpointType::Access: - acquire_breakpoint(CPU::BreakpointType::Read, address); - acquire_breakpoint(CPU::BreakpointType::Write, address); + for (u32 offset = 0; offset < kind; offset++) + { + acquire_breakpoint(CPU::BreakpointType::Read, address + offset); + acquire_breakpoint(CPU::BreakpointType::Write, address + offset); + } break; } @@ -725,16 +749,21 @@ void GDBServer::ClientSocket::RemoveBreakpoint(GDBBreakpointType type, VirtualMe break; case GDBBreakpointType::Write: - release_breakpoint(CPU::BreakpointType::Write, address); + for (u32 offset = 0; offset < kind; offset++) + release_breakpoint(CPU::BreakpointType::Write, address + offset); break; case GDBBreakpointType::Read: - release_breakpoint(CPU::BreakpointType::Read, address); + for (u32 offset = 0; offset < kind; offset++) + release_breakpoint(CPU::BreakpointType::Read, address + offset); break; case GDBBreakpointType::Access: - release_breakpoint(CPU::BreakpointType::Read, address); - release_breakpoint(CPU::BreakpointType::Write, address); + for (u32 offset = 0; offset < kind; offset++) + { + release_breakpoint(CPU::BreakpointType::Read, address + offset); + release_breakpoint(CPU::BreakpointType::Write, address + offset); + } break; }