fix(gdb): keep inferior-tty verification for read-only queries (#5539)
* fix(gdb): keep TTY verification across read-only inferior-tty inspection The console command path cleared `inferiorTtyVerified` before dispatching any command whose text merely mentioned `inferior-tty`, so the read-only `show inferior-tty` query permanently revoked a persistent session's verified output sink; no in-session path ever restored the flag, and a later close of a live session failed detach verification with capability_unavailable. Exempt the exact read-only query forms (`show inferior-tty` and the MI `-inferior-tty-show`) from the output-mutation predicate; every other command mentioning the TTY setting keeps revoking, so mutating commands still fail closed and detach keeps re-reading the live TTY before detaching. Cover the query forms, composite commands, and the inspect-then-close detach path with regression tests. Fixes #5099 * test(gdb): classify show inferior-tty in the one-shot table and rename the TTY classification table - Add a 'show inferior-tty' row to the 'classifies one-shot GDB commands' table: the read-only query no longer invalidates the detach proof, a user-visible one-shot behavior change. - Rename the 'classifies read-only inferior TTY queries as non-mutating' table to 'classifies inferior TTY commands' since two of its rows are mutation cases. - Assert via the existing recorded-commands array that the inspected 'show inferior-tty' text is actually dispatched, and add an execute-path assertion that the MI '-inferior-tty-show' form leaves the verified-sink flag intact (defensive exemption: REA's own MI calls do not route through this gate). --------- Co-authored-by: akushonkamen <akushonkamen@users.noreply.github.com>
This commit is contained in:
1 parent
fbfb1b768c
commit
0714eba505
2 files changed
+82
-3
No files matched your search
@@ -2429,9 +2429,15 @@ export const gdbOneShotCommandMayInvalidateDetachProof = (
|
||||
|
||||
/** @internal Classify commands that can invalidate the configured output sink. */
|
||||
export const commandMayChangeInferiorOutput = (command: string): boolean => {
|
||||
// These complete built-in inspections cannot change output routing. Keep
|
||||
// compound commands and abbreviations under the conservative rules below.
|
||||
if (/^[ \t]*(?:show|help)[ \t]+inferior-tty[ \t]*$/iu.test(command))
|
||||
// These exact read-only TTY inspections ("show inferior-tty", "help
|
||||
// inferior-tty", MI "-inferior-tty-show") cannot reconfigure the output
|
||||
// sink, so they must not revoke the startup verification. Keep compound
|
||||
// commands and abbreviations under the conservative rules below
|
||||
// (fail-closed).
|
||||
if (
|
||||
/^[ \t]*(?:show|help)[ \t]+inferior-tty[ \t]*$/iu.test(command) ||
|
||||
/^[ \t]*-inferior-tty-show[ \t]*$/iu.test(command)
|
||||
)
|
||||
return false;
|
||||
const setting = /^\s*(?:set|with)\s+(\S+)/iu.exec(command)?.[1];
|
||||
const maySetInferiorTty =
|
||||
|
||||
@@ -177,10 +177,22 @@ it.each([
|
||||
["so commands.gdb", true],
|
||||
["de", true],
|
||||
["p 1; python print(2)", true],
|
||||
["show inferior-tty", false],
|
||||
] as const)("classifies one-shot GDB commands: %s", (command, expected) => {
|
||||
expect(gdbOneShotCommandMayInvalidateDetachProof(command)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["show inferior-tty", false],
|
||||
[" show inferior-tty ", false],
|
||||
["-inferior-tty-show", false],
|
||||
["set inferior-tty /tmp/output", true],
|
||||
["show inferior-t; set inferior-tty /tmp/output", true],
|
||||
["show inferior-tty; define redirect = run > /tmp/output", true],
|
||||
] as const)("classifies inferior TTY commands: %s", (command, expected) => {
|
||||
expect(commandMayChangeInferiorOutput(command)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[new Error("provider failure"), "provider failure"],
|
||||
["non-error provider failure", "non-error provider failure"],
|
||||
@@ -2784,6 +2796,67 @@ it.each([
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps the verified sink across the read-only show inferior-tty inspection", async () => {
|
||||
const tty = "/dev/pts/42";
|
||||
const commands: string[] = [];
|
||||
let detached = false;
|
||||
const fake = createFakeSpawner((_child, token, command) => {
|
||||
commands.push(command);
|
||||
if (command.includes("inferior-tty-show"))
|
||||
fake.children[0]?.stdout.write(
|
||||
`${token}^done,inferior_tty_terminal=${JSON.stringify(tty)}\n`,
|
||||
);
|
||||
else if (command.includes("list-thread-groups"))
|
||||
fake.children[0]?.stdout.write(
|
||||
detached
|
||||
? `${token}^done,groups=[]\n`
|
||||
: `${token}^done,groups=[{id="i1",pid="123"}]\n`,
|
||||
);
|
||||
else {
|
||||
if (command.includes("target-detach")) detached = true;
|
||||
fake.children[0]?.stdout.write(`${token}^done\n`);
|
||||
}
|
||||
});
|
||||
const manager = managerWithFakeSpawner(fake.spawnProcess);
|
||||
const started = await expectGdbSessionStarted(manager, undefined, {
|
||||
inferiorTty: tty,
|
||||
});
|
||||
|
||||
const inspected = await manager.execute(
|
||||
started.value.session_id,
|
||||
"show inferior-tty",
|
||||
);
|
||||
|
||||
expect(inspected.ok).toBe(true);
|
||||
expect(manager.status(started.value.session_id)).toMatchObject({
|
||||
inferior_tty: tty,
|
||||
inferior_tty_verified: true,
|
||||
});
|
||||
expect(commands).toContain('-interpreter-exec console "show inferior-tty"');
|
||||
|
||||
// The MI form is a defensive exemption: REA's own MI calls do not route
|
||||
// through this user-command gate, and the console interpreter may reject
|
||||
// the text outright. The gate must still leave the verified flag alone.
|
||||
const inspectedMi = await manager.execute(
|
||||
started.value.session_id,
|
||||
"-inferior-tty-show",
|
||||
);
|
||||
|
||||
expect(inspectedMi.ok).toBe(true);
|
||||
expect(manager.status(started.value.session_id)?.inferior_tty_verified).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
const closed = await manager.close(started.value.session_id);
|
||||
|
||||
expect(closed).toMatchObject({
|
||||
ok: true,
|
||||
value: { target_state: "unknown" },
|
||||
});
|
||||
expect(commands).toContain("-target-detach");
|
||||
expect(fake.children[0]?.killSignals).toEqual(["SIGTERM"]);
|
||||
});
|
||||
|
||||
it("revokes the verified sink after an abbreviated with inferior-tty run command", async () => {
|
||||
const tty = "/dev/pts/42";
|
||||
const commands: string[] = [];
|
||||
|
||||
Reference in new issue
Block a user