A GDB stop leaves the other guest threads running instead of stopping them (all-stop)

Đang mở
#530 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
1-2 ngày
Mức phù hợp với người mới
52/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
c
Lĩnh vực
operating-systems

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

bug

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.

Ngôn ngữ chính
C
Star
282
Fork
32
Merge trung bình
1 ngày 12 giờ
Pull request đã merge (30 ngày)
17

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của sysprog21/elfuse

Tất cả issue của sysprog21/elfuse

Issue tương tự

Thêm issue về C

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.