A GDB stop leaves the other guest threads running instead of stopping them (all-stop)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 52/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- c
調査の方向性
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.
索引モデルが issue の本文から書いたものです。
説明
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.
- 主要言語
- C
- スター
- 282
- フォーク
- 32
- 平均マージ
- 1日 12時間
- マージ済み PR(30日)
- 17
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
sysprog21/elfuse のほかの issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 80/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
sysprog21/elfuse の issue をすべて見る
似ている issue
-
polish
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
ChupathingyCE/chupathingyce#1366 ·
メンテナーはふだん 1 日以内に返信
-
slice track:dev
難易度 2/5 1〜3時間 初心者へのやさしさ 64/100
Life-Framework/Life-Framework#1831 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 80/100
-
bug intrinsic low priority wrong result
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信