[GH-ISSUE #4926] [Bug] Kernel limit descriptor close failures lose retry ownership and block analysis recovery #3055

Closed
opened 2026-10-31 00:35:38 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @morluto on GitHub (Oct 18, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4926

Before submitting

  • Searched open and closed issues for related reports, including /proc/self/limits and LinuxResourceLimits.
  • Included only reduced fixture data and repository source references.

Area

Evidence, artifacts, or reconstruction

Observed behavior

A failed close of the Linux kernel limits descriptor loses its typed cleanup capability. ArtifactAnalysisResources treats the resulting generic failure as unrecoverable: after close permission is restored, another analysis remains blocked and shutdown still reports incomplete cleanup.

Controlled production-boundary reproduction on macOS; this is not a native Linux run. The independently proposed repair in #4863 was excluded from the unrelated JavaScript fix and is tracked here separately.

Reproduction

At source 93196449a, add a fixture redirecting only /proc/self/limits open to an authored ordinary file with valid limit rows. Keep the real descriptor/read, and deny its close until an explicit barrier is released.

  1. Call readLinuxResourceLimits(configured, scope) with a completed or malformed read.
  2. Observe generic ArtifactReaderFailure rather than a physical outcome and retry lease. The standalone path instead throws the close error and discards the earlier outcome.
  3. Call through ArtifactAnalysisResources.run/readLimits, restore close, and retry admission: it still fails because an unresolved fallback lease was manufactured.

Executed reduced suite: five new cases failed and five existing controls passed. The recovery case also reports incomplete teardown, consistent with retained unresolved fallback ownership.

Expected behavior

Use the shared descriptor owner. Retain the exact retry lease and completed value or primary failure; failed retry blocks admission; successful retry resolves the original cleanup marker and readmits work. See docs/tool-design.md#own-the-lifecycle.

Impact and scope

Source-derived affected consumers: packet/signal analysis through ArtifactAnalysisResources. Controlled descriptor and recovery failure reproduced; naturally occurring close-denial frequency and native Linux behavior are NOT_ENOUGH_INFO (not measured here).

REA version

6.5.0 source 93196449ad

Runtime and platform

Node 24.18.0, macOS 26.3 arm64; /proc/self/limits redirected to a real authored file at the production open seam.

Target and artifact context

An inert kernel-report fixture; no packet capture, signal producer or target executable launched.

Interface

Other: production read/resource-admission boundary

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

  • src/process/LinuxResourceLimits.ts:68: handwritten finally throws ArtifactReaderFailure.withCleanup without binding its retained descriptor lease.
  • src/process/ArtifactAnalysisResources.ts:286: capability-less cleanup intentionally falls back to an unresolved lease; a later physical close cannot resolve it.
  • Regression expectation: ArtifactFileCleanupFailure; received generic cleanup failure for scoped reads, ordinary close error for standalone reads.
  • Recovery expectation: { ok: true, value: configured }; received { ok: false, error: ProviderAdapterError } after restoring close.

Typed outcome or recovery expected

ArtifactFileCleanupFailure with exact fileCleanupFailure.outcome and cleanupLease, retained by the caller scope where present.

Analysis or suspected cause

Source-derived: the generic wrapper discards provenance and cleanup authority needed by its consumer. Reusing withArtifactFileHandle removes the handwritten lifetime and preserves the existing fail-closed consumer policy.

Originally created by @morluto on GitHub (Oct 18, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4926 ### Before submitting - [x] Searched open and closed issues for related reports, including `/proc/self/limits` and `LinuxResourceLimits`. - [x] Included only reduced fixture data and repository source references. ### Area Evidence, artifacts, or reconstruction ### Observed behavior A failed close of the Linux kernel limits descriptor loses its typed cleanup capability. `ArtifactAnalysisResources` treats the resulting generic failure as unrecoverable: after close permission is restored, another analysis remains blocked and shutdown still reports incomplete cleanup. Controlled production-boundary reproduction on macOS; this is not a native Linux run. The independently proposed repair in #4863 was excluded from the unrelated JavaScript fix and is tracked here separately. ### Reproduction At source `93196449a`, add a fixture redirecting only `/proc/self/limits` open to an authored ordinary file with valid limit rows. Keep the real descriptor/read, and deny its close until an explicit barrier is released. 1. Call `readLinuxResourceLimits(configured, scope)` with a completed or malformed read. 2. Observe generic `ArtifactReaderFailure` rather than a physical outcome and retry lease. The standalone path instead throws the close error and discards the earlier outcome. 3. Call through `ArtifactAnalysisResources.run`/`readLimits`, restore close, and retry admission: it still fails because an unresolved fallback lease was manufactured. Executed reduced suite: five new cases failed and five existing controls passed. The recovery case also reports incomplete teardown, consistent with retained unresolved fallback ownership. ### Expected behavior Use the shared descriptor owner. Retain the exact retry lease and completed value or primary failure; failed retry blocks admission; successful retry resolves the original cleanup marker and readmits work. See `docs/tool-design.md#own-the-lifecycle`. ### Impact and scope Source-derived affected consumers: packet/signal analysis through `ArtifactAnalysisResources`. Controlled descriptor and recovery failure reproduced; naturally occurring close-denial frequency and native Linux behavior are NOT_ENOUGH_INFO (not measured here). ### REA version 6.5.0 source 93196449adfb5e887a1b5e3a00afa69abe76a0e2 ### Runtime and platform Node 24.18.0, macOS 26.3 arm64; `/proc/self/limits` redirected to a real authored file at the production open seam. ### Target and artifact context An inert kernel-report fixture; no packet capture, signal producer or target executable launched. ### Interface Other: production read/resource-admission boundary ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence - `src/process/LinuxResourceLimits.ts:68`: handwritten `finally` throws `ArtifactReaderFailure.withCleanup` without binding its retained descriptor lease. - `src/process/ArtifactAnalysisResources.ts:286`: capability-less cleanup intentionally falls back to an unresolved lease; a later physical close cannot resolve it. - Regression expectation: `ArtifactFileCleanupFailure`; received generic cleanup failure for scoped reads, ordinary close error for standalone reads. - Recovery expectation: `{ ok: true, value: configured }`; received `{ ok: false, error: ProviderAdapterError }` after restoring close. ### Typed outcome or recovery expected `ArtifactFileCleanupFailure` with exact `fileCleanupFailure.outcome` and `cleanupLease`, retained by the caller scope where present. ### Analysis or suspected cause Source-derived: the generic wrapper discards provenance and cleanup authority needed by its consumer. Reusing `withArtifactFileHandle` removes the handwritten lifetime and preserves the existing fail-closed consumer policy.
bsakaguchi 2026-10-31 00:35:38 +00:00
  • closed this issue
  • added the
    triage
    bug
    labels
Author
Owner

@morluto commented on GitHub (Oct 18, 2026):

Fixed in 2183c9598 on main.

readLinuxResourceLimits now uses withArtifactFileHandle, which owns the exact descriptor, physical read outcome, failed close and retry lease. The handwritten close/wrapper path was removed. Existing consumer fail-closed behavior remains: admission stays blocked while close is denied, then successful retry resolves the original error's cleanup marker and permits the next read. Standalone reads also retain an actionable lease.

Adapted the separate resource-owner repair from @Abdooo2235's #4863, with coauthor credit; added standalone and admission-recovery coverage.

Validation executed on macOS 26.3 arm64 / Node 24.18.0:

  • Before repair: all five added cases failed; five pre-existing controls passed. The consumer recovery case also retained an unrecoverable teardown failure.
  • After repair: 41 tests passed across four executed files; 14 native Linux descriptor checks were correctly skipped in two additional files.
  • Controlled production open seam retains real authored-file descriptors, reads and closes. Successful and malformed reads, scoped and standalone ownership, denied retry, exact lease/physical outcome retention, blocked replacement opens and successful readmission are covered.
  • Existing mixed-child, cancellation/finalization and shared resource tests passed. check:fast, rebuilt focused artifacts, docs:check (five conformance tests), normal commit and push hooks passed. Commit hooks did not change tested content.

Native Linux /proc/prlimit, real TShark and real sigrok were not run on this Mac. Controlled descriptor tests do not qualify those native workflows. No performance claim; token/CPU/process-family-memory effects were not measured. Heavy CPU stress and Flameox remain stopped.

<!-- gh-comment-id:6243745037 --> @morluto commented on GitHub (Oct 18, 2026): Fixed in 2183c9598 on main. `readLinuxResourceLimits` now uses `withArtifactFileHandle`, which owns the exact descriptor, physical read outcome, failed close and retry lease. The handwritten close/wrapper path was removed. Existing consumer fail-closed behavior remains: admission stays blocked while close is denied, then successful retry resolves the original error's cleanup marker and permits the next read. Standalone reads also retain an actionable lease. Adapted the separate resource-owner repair from @Abdooo2235's #4863, with coauthor credit; added standalone and admission-recovery coverage. Validation executed on macOS 26.3 arm64 / Node 24.18.0: - Before repair: all five added cases failed; five pre-existing controls passed. The consumer recovery case also retained an unrecoverable teardown failure. - After repair: 41 tests passed across four executed files; 14 native Linux descriptor checks were correctly skipped in two additional files. - Controlled production open seam retains real authored-file descriptors, reads and closes. Successful and malformed reads, scoped and standalone ownership, denied retry, exact lease/physical outcome retention, blocked replacement opens and successful readmission are covered. - Existing mixed-child, cancellation/finalization and shared resource tests passed. check:fast, rebuilt focused artifacts, docs:check (five conformance tests), normal commit and push hooks passed. Commit hooks did not change tested content. Native Linux `/proc`/prlimit, real TShark and real sigrok were not run on this Mac. Controlled descriptor tests do not qualify those native workflows. No performance claim; token/CPU/process-family-memory effects were not measured. Heavy CPU stress and Flameox remain stopped.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3055
No description provided.