ref: 8f64c7d58b283eb86f67d6c044704533b4b2f490
parent: c725122a471acff0f24b9bb337a9e80e662a4b8b
author: David Arroyo <david@arroyo.cc>
date: Sun Mar 15 15:00:02 EDT 2026
sys/src/cmd/gdbfs/gdb.c gdbfs: fix chanfree/chanclose race Gdbfs keeps communication w/ gdbserver in a separate proc, gdbproc. 9P request handlers gain access to gdbproc by sending a channel to it, which carries the request and response. There is a race between closing these short-lived channels, which is done from gdbproc, and freeing them, which is done from the 9P request handler. If chanfree runs before chanclose, chanclose will segfault at best, or close the newly allocated channel at the same address. Fix this by only calling chanfree in gdbproc, and guarding that chanfree with a recv which will block until the request handler calls chanclose().
--- a/sys/src/cmd/gdbfs/gdb.c
+++ b/sys/src/cmd/gdbfs/gdb.c
@@ -11,7 +11,6 @@
#include "dat.h"
static char Ebadctl[] = "bad process or channel control request";
-static char Erunning[] = "target is running";
static char hex[] = "0123456789abcdef";
/* /proc/$pid/[k]regs holds a Ureg structure for the target platform.
@@ -195,8 +194,12 @@
chanfree(rc);
return nil;
}
- if(sendp(rc, str) < 0)
- sysfatal("cmdstr: sendp %s failed", str);+ if(sendp(rc, str) < 0){+ werrstr("interrupted");+ free(str);
+ chanclose(rc);
+ return nil;
+ }
/* ack */
if(recv(rc, &err) < 0){@@ -207,7 +210,7 @@
if(err != nil){ werrstr("%s", err);free(err);
- chanfree(rc);
+ chanclose(rc);
return nil;
}
return rc;
@@ -249,7 +252,7 @@
chanclose(rc);
return nil;
}
- chanfree(rc);
+ chanclose(rc);
if(*rsp == 'E'){ werrstr("%s", rsp);@@ -280,11 +283,10 @@
R ← G ack (nil) or err (char*)
if !err:
R ← G reply (char*) or err ("E...")- R ← G close
+ R → G close()
- R may free the channel after the final message.
- R may close the channel if it is interrupted.
- G may free the channel if send() or recv() fail.
+ R closes channel when it goes out of scope
+ G frees channel after receiving close
*/
static void
gdbproc(void *)
@@ -295,17 +297,14 @@
memset(sum, 0, sizeof sum);
while(rc = recvp(gdb.c)){- if(recv(rc, &req) < 0){- chanfree(rc);
- continue;
- }
+ if(recv(rc, &req) < 0)
+ goto next;
+
tries = 5;
do{ dbg("→ %s\n", req);- if(fprint(gdb.wfd, "%s", req) < 0){- chanprint(rc, "E.%r");
- goto next;
- }
+ if(fprint(gdb.wfd, "%s", req) < 0)
+ sysfatal("gdbproc send req: %r");c = Bgetc(gdb.rb);
dbg("← %c\n", c);} while(c == '-' && tries --> 0);
@@ -324,10 +323,9 @@
/* Skip notification packets. These are unsolicited,
and must not contain the start-of-packet marker '$' */
if(Brdline(gdb.rb, '$') == nil
- || Blinelen(gdb.rb) > 0 && fprint(gdb.wfd, "+") < 0)
+ || Blinelen(gdb.rb) > 1 && fprint(gdb.wfd, "+") < 0)
{- chanprint(rc, "E.%r");
- goto next;
+ sysfatal("gdbproc scan '$': %r");}
rsp = Brdstr(gdb.rb, '#', 1);
@@ -347,12 +345,11 @@
free(rsp);
rsp = smprint("E.ack %r");}
- if(sendp(rc, rsp) < 0){- chanfree(rc);
+ if(sendp(rc, rsp) < 0)
free(rsp);
- continue;
- }
-next: chanclose(rc);
+next:
+ recv(rc, nil);
+ chanfree(rc);
}
chanclose(gdb.c);
}
--
⑨