From 4303dcdcf68af4d71d777ffff75cbd4f17a8877d Mon Sep 17 00:00:00 2001 From: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Date: Mon, 12 Oct 2026 21:11:53 -0700 Subject: [PATCH] fix(native): name the retained pwntools process group in cleanup resources (#2281) When a pwntools or pwndbg stage cannot confirm that its owned process group stopped, the cleanup_incomplete result listed only the run ID and runtime directory, although a descendant can still be live. Add the retained supervisor's pid and process-group coordinates, as OwnedCommand reports for a non-transferred cleanup failure. Fixes #2274 Co-authored-by: Claude Opus 5.5 --- src/native/pwntools/PwntoolsDecoder.ts | 7 +++++++ tests/process-global/pwntoolsStageRetirement.test.ts | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/src/native/pwntools/PwntoolsDecoder.ts b/src/native/pwntools/PwntoolsDecoder.ts index 3a28edbfa..d8cad3d2e 100644 --- a/src/native/pwntools/PwntoolsDecoder.ts +++ b/src/native/pwntools/PwntoolsDecoder.ts @@ -518,6 +518,13 @@ export class PwntoolsDecoder< [ pending.process.launch.ownership?.runId ?? "owned-decoder", ...(pending.root === undefined ? [] : [pending.root.path]), + // The retained group may still be live; name it as OwnedCommand does. + ...(pending.process.launch.ownership === undefined + ? [] + : [ + `pid:${pending.process.launch.ownership.leaderPid}`, + `process-group:${pending.process.launch.ownership.processGroupId}`, + ]), ], { reason: stopped.reason, diff --git a/tests/process-global/pwntoolsStageRetirement.test.ts b/tests/process-global/pwntoolsStageRetirement.test.ts index bcdaf858f..6ec062535 100644 --- a/tests/process-global/pwntoolsStageRetirement.test.ts +++ b/tests/process-global/pwntoolsStageRetirement.test.ts @@ -261,6 +261,14 @@ it.runIf(supportedHost).each([ if (result.ok) throw new Error("Expected unresolved process ownership"); const projected = projectAnalysisError(result.error); expect(projected.code).toBe("cleanup_incomplete"); + expect(projected).toMatchObject({ + details: { + resources: expect.arrayContaining([ + expect.stringMatching(/^pid:\d+$/u), + expect.stringMatching(/^process-group:\d+$/u), + ]), + }, + }); if (stage === "debugger") expect(projected).toMatchObject({ details: {