feat(pool): MemoryPool view borrow — owner.Memory[.Span] used after Dispose → OWN002 (POOL002)#73
Conversation
…ispose → OWN002 (POOL002) #72 tracked the MemoryPool OWNER lifecycle (acquire/release: POOL001 leak, POOL003 double-dispose). This adds the VIEW borrow: `owner.Memory` / `owner.Memory.Span` of a System.Buffers.IMemoryOwner<T> is a borrow of the owner — exactly like `buf.AsSpan()` is of a Rent'd array. ViewOwner now resolves it (by the IMemoryOwner<T>.Memory property symbol), so a view-local read after `owner.Dispose()` is a use of the owner after release -> OWN002 (POOL002), and a returned Memory view dangles. Reuses the whole borrow lattice — no core changes, extractor + corpus only. Corpus: memorypool-view-after-dispose (before: `Memory<byte> v = owner.Memory; owner.Dispose(); Consume(v.Span)` -> OWN002; after: a `using` owner read while alive -> silent) — the MemoryPool twin of arraypool-span-view-after-return. Benchmark recall floor 16 -> 17. P-007 status + ci.yml comment updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughExtends the Roslyn extractor's ChangesMemoryPool view-after-dispose detection (OWN002)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8284055bb1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (e is MemberAccessExpressionSyntax mem | ||
| && mem.Name.Identifier.Text == "Memory" | ||
| && mem.Expression is IdentifierNameSyntax mo | ||
| && model.GetSymbolInfo(mem).Symbol is IPropertySymbol { ContainingType: { Name: "IMemoryOwner" } ict } | ||
| && IsInNamespace(ict, "System", "Buffers")) |
There was a problem hiding this comment.
Track using-owned MemoryPool views on escape
When the owner is declared with a using declaration and the method returns its view (using IMemoryOwner<byte> owner = ...; return owner.Memory;), the candidate pass skips using locals entirely, so this new owner.Memory mapping never sees owner in tracked and emits no post-dispose use. That return value is still delivered after the implicit dispose at scope exit, so the dangling Memory<T> escape that this change says it handles remains silent for the idiomatic using cleanup form.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch — the claim overstated. This PR delivers the use-after-explicit-Dispose POOL002 (the corpus case: var v = owner.Memory; owner.Dispose(); Use(v) → OWN002), which is solid. You're right that the dangling-Memory escape via the idiomatic using owner; return owner.Memory; is silent: the candidate pass skips using locals (they're correctly auto-disposed → not leak candidates), so owner never enters tracked and the returned view isn't mapped.
Catching that needs modelling the implicit scope-exit dispose as a release threaded on the return path (so the returned view's caller-use lands after it, like the ArrayPool try/finally Memory escape in #70) — a separate, non-trivial slice, since it changes how using locals are handled. I've scoped the claim in notes.md accordingly and noted it as the follow-up rather than leave it overstated. Thanks.
Generated by Claude Code
…ose (Codex review) Codex P2: this PR catches a view-local read after an explicit owner.Dispose() (OWN002, the corpus case) — but the dangling-Memory ESCAPE via the idiomatic `using owner; return owner.Memory;` is NOT caught, because `using` locals are skipped as non-leak candidates so the owner never enters `tracked`. Correct notes.md to not overstate: the returned-view dangle from a `using` owner (modelling the implicit scope-exit dispose as a release on the return path) is a follow-up, like the ArrayPool try/finally Memory escape. The use-after-explicit-Dispose catch and the corpus case are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
What
#72 tracked the MemoryPool owner lifecycle (acquire/release → POOL001 leak, POOL003 double-dispose). This adds the view borrow:
owner.Memory/owner.Memory.Spanof aSystem.Buffers.IMemoryOwner<T>is a borrow of the owner — exactly likebuf.AsSpan()is of aRented array.How
ViewOwnernow resolvesowner.Memory(andowner.Memory.Span) to the owner local, recognised by the resolvedIMemoryOwner<T>.Memoryproperty symbol (System.Buffers) — not by name, so a look-alike.Memoryisn't mistaken for it. The existing borrow machinery does the rest: a use of the view lowers to a use of the owner, so a view-local read after an explicitowner.Dispose()is a use-after-release → OWN002.The inline form (
Consume(owner.Memory.Span)) already worked via the descendantowner; this closes the view-local gap (var v = owner.Memory; owner.Dispose(); Use(v)).Zero core changes — it reuses the borrow lattice and OWN002. Extractor + corpus only.
Validation
memorypool-view-after-dispose(the MemoryPool twin ofarraypool-span-view-after-return) → OWN002;after.cs(ausingowner read while alive) silent.Scope
Delivers the use-after-explicit-
DisposePOOL002 catch. The dangling-Memoryescape via the idiomaticusing owner; return owner.Memory;form is not yet caught (Codex review):usinglocals are skipped as non-leak candidates, so catching it needs the implicit scope-exit dispose modelled as a release on the return path (like the ArrayPool try/finallyMemoryescape in #70) — a follow-up. A POOL005 view stored in a FIELD is the other remaining backlog item.🤖 Generated with Claude Code
https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
Summary by CodeRabbit
IMemoryOwnerborrows fromMemory/Span.IMemoryOwnerlifetime usage to avoid reading released pooled memory.