A GDB stop leaves the other guest threads running instead of stopping them (all-stop)
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 52/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- c
- Área
- operating-systems
Línea de trabajo
Start by reading gdb_stub_handle_stop in src/debug/gdbstub.c and the canceled-exit handler in src/syscall/proc.c, then trace how stop requests are cleared on resume. Use the supplied two-thread reproducer and raw RSP client to check whether the counter stays unchanged after the T05 stop reply; done means sibling threads remain stopped until GDB resumes.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
What happens
When one thread stops at a breakpoint under --gdb, the other guest threads keep running guest code while GDB inspects and writes state. GDB's all-stop mode, which the stub's comments describe ("Stop all other vCPUs (all-stop mode)"), expects every thread to be stopped until the next resume.
Evidence
On upstream main a20dfab, the probe below, a guest with one thread spinning on counter++ while the main thread calls bp_here(), driven by the raw RSP client below:
Z0: OK
stop reply: T05
counter while stopped: 232115109 -> 809825692 (changed)
The counter grew by 577,710,583 in the 0.5 s between two m reads after the T05 stop reply. No Linux comparison: the qemu reference VM has no gdbserver.
Cause
Checked by reading. In gdb_stub_handle_stop (src/debug/gdbstub.c) the first thread to stop sets all_stopped, clears stop_requested, and only then calls thread_interrupt_all. thread_interrupt_all only kicks every vCPU out of hv_vcpu_run. A kicked sibling's canceled-exit handler in src/syscall/proc.c enters the GDB stop only when gdb_stub_stop_requested() returns true, and that function reads stop_requested alone, which is already 0, so the sibling resumes guest code. Nothing else in the run loop checks all_stopped.
Reproduce
#include <pthread.h>
#include <stdint.h>
#include <unistd.h>
volatile uint64_t counter;
__attribute__((noinline)) void bp_here(void) { __asm__ volatile("" ::: "memory"); }
static void *spin(void *arg)
{
(void) arg;
for (;;)
counter++;
return NULL;
}
int main(void)
{
pthread_t t;
pthread_create(&t, NULL, spin, NULL);
usleep(200 * 1000);
bp_here();
return 0;
}
Build it static, start it with build/elfuse --gdb 23947 --gdb-stop-on-entry ./gdb_allstop, and run this client with the addresses of bp_here and counter from nm:
import socket, sys, time
port, bp, ctr = int(sys.argv[1]), int(sys.argv[2], 16), int(sys.argv[3], 16)
s = None
for _ in range(100):
try:
s = socket.create_connection(("127.0.0.1", port), timeout=10)
break
except OSError:
time.sleep(0.1)
buf = b""
def pkt(data):
s.sendall(("$%s#%02x" % (data, sum(data.encode()) & 0xFF)).encode())
def reply():
global buf
while True:
i = buf.find(b"$"); j = buf.find(b"#", i)
if i >= 0 and j >= 0 and len(buf) >= j + 3:
body = buf[i + 1:j].decode(); buf = buf[j + 3:]
s.sendall(b"+")
return body
buf += s.recv(4096)
def cmd(data):
pkt(data)
return reply()
cmd("?")
print("Z0:", cmd("Z0,%x,4" % bp))
print("stop reply:", cmd("c")[:3])
read = lambda: int.from_bytes(bytes.fromhex(cmd("m%x,8" % ctr)), "little")
a = read(); time.sleep(0.5); b = read()
print("counter while stopped: %d -> %d (%s)" % (a, b, "changed" if b != a else "unchanged"))
pkt("k")
Possible fix
Keep the stop request raised until GDB resumes: clear stop_requested in the resume paths (c, s, vCont, detach) rather than in gdb_stub_handle_stop, or have gdb_stub_stop_requested also return true while all_stopped is set. Kicked siblings then park in gdb_stub_handle_stop until the resume.
Origin
Follow-up from #525.
- Lenguaje dominante
- C
- Estrellas
- 282
- Forks
- 32
- Merge medio
- 1 d 12 h
- PR fusionados (30 d)
- 17
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de sysprog21/elfuse
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 80/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 3/5 1-2 días Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
Todos los issues de sysprog21/elfuse
Issues similares
-
bug intrinsic low priority wrong result
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
-
fsck: inodes above 1024 read/written at wrong disk offset; repair can destroy the root inodeAbiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
-
login
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
Los mantenedores suelen responder en 1 día
-
`table_create()` crashes when an `hccalloc()` for the option table failsPosiblemente ocupada @marinelay la tomó hoy. Abiertotype: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Outdated labels in Menu Item Visibility (`Show 'Quit RetroArch'` / `Show 'Restart RetroArch'`)Abierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 78/100
Los mantenedores suelen responder en 2 días