Skip to content

fix(ffmpeg-ipc): harden IpcFrameProvider/IpcSampleProvider (leak guard, size validation, prefetch, EOF clamp)#2012

Merged
yuto-trd merged 7 commits into
mainfrom
yuto-trd/ipc-provider-hardening
Jun 25, 2026
Merged

fix(ffmpeg-ipc): harden IpcFrameProvider/IpcSampleProvider (leak guard, size validation, prefetch, EOF clamp)#2012
yuto-trd merged 7 commits into
mainfrom
yuto-trd/ipc-provider-hardening

Conversation

@yuto-trd

@yuto-trd yuto-trd commented Jun 24, 2026

Copy link
Copy Markdown
Member

Description

Hardens the MIT-side IPC client providers (IpcFrameProvider / IpcSampleProvider) against malformed/oversized worker responses and faulted prefetch, mirroring the guards each already had on its sibling. All findings come from a code review of the IPC same-frame-reuse work and are tracked on the AI Review board (project #9).

  • Frame-side Bitmap leak (board item: BuildBitmap leak): IpcFrameProvider.BuildBitmap allocated the native-backed Bitmap and then called SharedMemoryBuffer.Read with no try/catch. The DataLength guard validates against the destination bitmap size, but Read separately bounds-checks against the shared-buffer capacity; a frame larger than the shared buffer throws ArgumentOutOfRangeException after allocation and leaked the bitmap. Now wrapped in try { … } catch { bmp.Dispose(); throw; }, matching IpcSampleProvider.FetchChunk.
  • Sample-side wild-write (board item: Harden IpcSampleProvider): FetchChunk copied sampleInfo.DataLength bytes into a Pcm<Stereo32BitFloat> (capacity NumSamples * 8) without validating the reported size. An oversized/negative DataLength overran the native allocation. Added a NumSamples >= 0 guard and a DataLength == NumSamples * 8 check before the read, and the copy now uses the validated expected length — the same shape as the frame-side guard.
  • Sample-side faulted-prefetch pinning (board item: Harden IpcSampleProvider): the prefetch-hit branch of EnsureChunkLoaded awaited _prefetchTask before clearing it, so a faulted prefetch pinned the provider to a re-throwing task forever. Now clears the field before awaiting, like the stale-drain branch and IpcFrameProvider.RenderFrame.
  • Remove unreachable defensive guards (board item: Remove unreachable defensive code): IpcConnection.SendAndReceiveAsync already surfaces a closed connection as IOException and an error response as FFmpegWorkerException, so the downstream ?? throw new IOException(...) and if (response.Error != null) throw new InvalidOperationException(...) guards in both providers were unreachable. Removed them, leaving a comment documenting the contract; only the live CancelEncode response still needs handling.
  • Sample EOF clamp: IpcSampleProvider.Sample now clamps the request to the real timeline (SampleCount). The encoder issues the final Sample with frame.NbSamples, which can straddle EOF; an unclamped length either sliced a zero-length next chunk when SampleCount aligns to a chunk boundary (ArgumentOutOfRangeException) or copied out-of-range data when it didn't. The consumer tolerates a short final Pcm, so clamping (rather than zero-padding) keeps the encoder correct.

Affected areas

  • Beutl.Engine (rendering / scene / track)
  • Beutl.ProjectSystem (project / document persistence)
  • UI (Beutl.Editor, Beutl.Editor.Components, Beutl.Controls)
  • Beutl.Extensibility (plugin abstractions)
  • Beutl.NodeGraph (node editor)
  • Beutl.FFmpegIpc / Beutl.FFmpegWorker (media IPC boundary)
  • Beutl.Api (server API client)
  • Build / CI / docs only

Breaking changes

None. All changes are internal to Beutl.FFmpegIpc (the providers are internal sealed) and behavior-preserving for valid worker responses — they only add guards on malformed/oversized input and tighten timeline-edge handling.

Test plan

All in tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs, driven by a fake host over a real NamedPipe. New tests added on top of the existing frame-side suite:

  • RenderFrame_WhenFrameExceedsBufferCapacity_ThrowsFromReadAndDoesNotLeak — 32x32 frame > 4096-byte buffer surfaces the Read capacity overflow as a throw (bitmap disposed in the catch).
  • Sample_WhenWorkerReportsMismatchedDataLength_Throws ([TestCase] over/under-sized) — DataLength disagreeing with NumSamples * 8 is rejected before the read.
  • Sample_WhenHostErrors_ThrowsFFmpegWorkerException — an injected worker error surfaces as FFmpegWorkerException (proves the dropped guard did not change the error type).
  • Sample_WhenPrefetchFaults_RecoversOnRetry — a faulted sample prefetch is not pinned; a retry recovers.
  • Sample_WhenRequestStraddlesAlignedEof_ClampsWithoutThrowing — a request past an aligned EOF clamps to the real remaining samples.

Result: 成功! 失敗: 0、合格: 16、スキップ: 0、合計: 16 (net10.0). dotnet format --verify-no-changes clean on the three changed files.

Fixed issues / References

  • Project board: b-editor/projects/9 ("IpcFrameProvider.BuildBitmap leaks the Bitmap when Read throws after allocation")
  • Project board: b-editor/projects/9 ("Harden IpcSampleProvider: validate sample DataLength and fix faulted-prefetch pinning")
  • Project board: b-editor/projects/9 ("Remove unreachable defensive code in IpcFrameProvider/IpcSampleProvider")

Summary by CodeRabbit

  • Bug Fixes
    • Improved frame request/response handling: cancellations now consistently throw, errors surface via the underlying call path, and frame render counts no longer advance on failure.
    • Prevented shared-memory/bitmap disposal issues by ensuring bitmaps are cleaned up when reads fail.
    • Strengthened sample validation (including exact byte-length capacity checks) and improved EOF handling by returning the requested length with a zero-filled silence tail.
  • Tests
    • Added/expanded contract tests for oversized frame reads, mismatched sample data-length, host/worker error propagation, prefetch retry recovery, and EOF straddle behavior.

yuto-trd added 5 commits June 24, 2026 16:38
BuildBitmap allocated the native Bitmap and then called SharedMemoryBuffer.Read
with no try/catch. Read bounds-checks against the shared-buffer Capacity (the
DataLength==Width*Height*8 guard does not), so a frame exceeding Capacity threw
ArgumentOutOfRangeException after allocation, leaking the native bitmap. Wrap the
Read in try/catch that disposes the bitmap before propagating, mirroring
IpcSampleProvider.FetchChunk.

Also remove provably-dead defensive code in RenderFrame: SendAndReceiveAsync never
returns null (it throws IOException on a closed connection) and converts error
responses to FFmpegWorkerException before returning, so the `?? throw IOException`
and the `if (response.Error != null) throw InvalidOperationException` were
unreachable. The CancelEncode check and the GetPayload null guard stay (both are
live).

Refs: Project #9 "AI Review" item "IpcFrameProvider.BuildBitmap leaks Bitmap on Read throw"
The encoder requests its final audio chunk with frame.NbSamples, which can run
past SampleCount near the timeline end. When SampleCount aligns to a chunk
boundary the cross-boundary path loaded an empty next chunk and sliced a
0-length span, throwing ArgumentOutOfRangeException; with a non-aligned EOF it
silently copied out-of-range data. Clamp the effective length to the real
remaining samples (Math.Max(0, SampleCount - offset)) and return a short final
Pcm (offset >= SampleCount yields an empty Pcm). The consumer
(FFmpegEncodingController.GetAudioFrame) copies only NumSamples * SampleSize
bytes and advances NextPts by the requested NbSamples regardless, so a clamped
Pcm keeps the encoder correct.

Refs: Project #9 "AI Review" item "IpcSampleProvider.Sample throws past end of timeline"
…ead guards

Three hardening fixes to IpcSampleProvider, all in the sample-load path:

- FetchChunk used the worker-reported DataLength as the Read length without
  validating it against the native Pcm capacity (NumSamples * 8), so an oversized
  DataLength overran the allocation (native heap corruption). Add a
  Stereo32BitFloatBytesPerSample const, reject a negative NumSamples and any
  DataLength that disagrees with NumSamples * 8, and Read using the validated
  length. Mirrors IpcFrameProvider's RgbaF16BytesPerPixel guard.

- EnsureChunkLoaded's prefetch-hit branch awaited _prefetchTask before clearing
  it, so a faulted prefetch pinned the provider to a re-throwing task forever.
  Reorder to clear-before-await, matching the stale-drain branch and
  IpcFrameProvider.RenderFrame.

- Remove provably-dead defensive code in FetchChunk: SendAndReceiveAsync never
  returns null (throws IOException on a closed connection) and converts error
  responses to FFmpegWorkerException before returning, so the `?? throw
  IOException` and `if (response.Error != null) throw InvalidOperationException`
  were unreachable. The CancelEncode check and GetPayload null guard stay.

Refs: Project #9 "AI Review" item "IpcSampleProvider FetchChunk wild-write and pinned prefetch"
Add NUnit coverage for the IpcFrameProvider / IpcSampleProvider hardening:

- RenderFrame_WhenFrameExceedsBufferCapacity_ThrowsFromReadAndDoesNotLeak: a
  32x32 frame (8192 B > 4096 B buffer) passes the DataLength guard but overflows
  SharedMemoryBuffer.Read; pins that the overflow surfaces as a throw (the fix's
  catch disposes the bitmap so it can't leak).
- Sample_WhenWorkerReportsMismatchedDataLength_Throws: an over/under-sized
  DataLength vs NumSamples is rejected before the Read.
- Sample_WhenHostErrors_ThrowsFFmpegWorkerException: an injected worker error
  surfaces as FFmpegWorkerException, not InvalidOperationException.
- Sample_WhenPrefetchFaults_RecoversOnRetry: a faulted sample prefetch is not
  pinned; a retry recovers.
- Sample_WhenRequestStraddlesAlignedEof_ClampsWithoutThrowing: a request past an
  aligned EOF clamps to the real samples instead of throwing.

Adds a sample-side fake-host harness mirroring the existing frame-side helpers.

Refs: Project #9 "AI Review" item "Harden Beutl.FFmpegIpc providers"
…etch drain

Address review nits on the IPC provider hardening branch:
- Move the Stereo32BitFloatBytesPerSample test constant up beside the other
  per-format size constants instead of declaring it mid-file.
- Make the EnsureChunkLoaded stale-drain comment explicit that the field is
  cleared before the await, so a faulted stale prefetch propagates once and
  does not pin the provider.

Refs: Project #9 "AI Review" items 264/267
Copilot AI review requested due to automatic review settings June 24, 2026 23:56
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d12c3af0-e90e-41a5-aed2-4f3611fc1366

📥 Commits

Reviewing files that changed from the base of the PR and between 0c61d20 and 1eb94e1.

📒 Files selected for processing (2)
  • src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs
  • tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs
💤 Files with no reviewable changes (1)
  • src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs

📝 Walkthrough

Walkthrough

Frame IPC now treats non-cancel worker failures as exceptions and disposes bitmaps on read failure. Sample IPC now clamps requests to available data, clears prefetched tasks before awaiting, validates worker-reported sizes, and preserves zero-filled tails past EOF. Contract tests cover these paths.

Changes

IPC frame and sample validation

Layer / File(s) Summary
Frame render and bitmap cleanup
src/Beutl.FFmpegIpc/Providers/IpcFrameProvider.cs, tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs
RenderFrame now depends on SendAndReceiveAsync exceptions for non-cancel failures, BuildBitmap disposes the bitmap on shared-memory read failure, and the frame contract test exercises oversized reads.
Sample request bounds
src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs, tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs
Sample clamps requests to the remaining timeline and returns a zero-filled Pcm<Stereo32BitFloat> at or after EOF, with contract coverage for EOF overlap and signature preservation.
Prefetch state and retry
src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs, tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs
EnsureChunkLoaded clears _prefetchTask before awaiting it, and the sample tests drive a faulted prefetch followed by a successful retry.
Chunk validation and worker errors
src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs, tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs
FetchChunk validates ProvideSampleMessage data and copy sizes before reading shared memory, and the new sample harnesses cover mismatched lengths and worker error responses.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • b-editor/beutl#1934 — Adds IPC cancel-path contract coverage that aligns with the updated CancelEncode handling here.
  • b-editor/beutl#1988 — Touches IpcFrameProvider.RenderFrame and BuildBitmap, the same frame flow updated here.

Poem

I hopped through pipes by moonlit glow,
Clamped little samples row by row.
If reads went splat, I stayed quite neat,
And zeros padded EOF’s seat.
🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main hardening changes to IpcFrameProvider and IpcSampleProvider.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch yuto-trd/ipc-provider-hardening

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the FFmpeg IPC client providers (IpcFrameProvider / IpcSampleProvider) against malformed worker responses and prefetch failures, and extends the IPC provider contract tests to cover the new guards and regressions around EOF/prefetch behavior.

Changes:

  • Add leak-guards and size validation when reading frame/sample payloads from shared memory.
  • Fix faulted-prefetch “pinning” by clearing the prefetch task field before awaiting.
  • Expand IpcProviderContractTests to cover oversized frames, mismatched sample lengths, injected errors, prefetch fault recovery, and EOF behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs Adds sample-side host harness and new contract tests for IPC providers (validation, prefetch fault recovery, EOF).
src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs Adds sample DataLength/NumSamples validation, fixes prefetch task pinning, and introduces EOF clamp logic.
src/Beutl.FFmpegIpc/Providers/IpcFrameProvider.cs Removes unreachable null/error guards and adds a dispose-on-failure leak guard when reading into a newly allocated Bitmap.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs (1)

344-371: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test asserts the throw but not the "DoesNotLeak" guarantee.

RenderFrame_WhenFrameExceedsBufferCapacity_ThrowsFromReadAndDoesNotLeak only verifies the ArgumentOutOfRangeException surfaces; it does not actually observe that BuildBitmap's catch disposed the freshly allocated Bitmap. The name implies a no-leak assertion that isn't made, so a regression that drops the bmp.Dispose() would still pass. If disposal can be observed (e.g., via a test seam or a disposal hook on Bitmap), consider asserting it; otherwise a comment noting the limitation would avoid a false sense of coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs` around lines 344 -
371, The test
RenderFrame_WhenFrameExceedsBufferCapacity_ThrowsFromReadAndDoesNotLeak
currently only checks that IpcFrameProvider.RenderFrame throws
ArgumentOutOfRangeException and does not verify the no-leak part of the
scenario. Update this test to also observe that the Bitmap allocated inside
BuildBitmap is disposed when SharedMemoryBuffer.Read throws, using an available
test seam or disposal hook on Bitmap if possible; otherwise adjust the test name
or add a clarifying comment so the coverage claim matches what is actually
asserted.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs`:
- Around line 344-371: The test
RenderFrame_WhenFrameExceedsBufferCapacity_ThrowsFromReadAndDoesNotLeak
currently only checks that IpcFrameProvider.RenderFrame throws
ArgumentOutOfRangeException and does not verify the no-leak part of the
scenario. Update this test to also observe that the Bitmap allocated inside
BuildBitmap is disposed when SharedMemoryBuffer.Read throws, using an available
test seam or disposal hook on Bitmap if possible; otherwise adjust the test name
or add a clarifying comment so the coverage claim matches what is actually
asserted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ebdd75d2-a806-42d3-99a7-ccbce8fa45f3

📥 Commits

Reviewing files that changed from the base of the PR and between a24a93c and 9778773.

📒 Files selected for processing (3)
  • src/Beutl.FFmpegIpc/Providers/IpcFrameProvider.cs
  • src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs
  • tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9778773022

ℹ️ 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".

Comment thread src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs Outdated
…f returning a short Pcm

The end-of-timeline handling returned a shortened Pcm when the final request
straddled EOF. FFmpegEncodingController.GetAudioFrame copies pcm.NumSamples into
a single reused, fixed-size MediaFrame but still converts/encodes frame.NbSamples
and advances by it, so a short Pcm left stale samples from the previous frame in
the tail of the final encoded audio frame. It also diverged from the
ISampleProvider convention that SampleProviderImpl already follows (return the
requested length, zero-filling past EOF).

Sample now always returns a Pcm of the requested length: the available prefix is
read via the extracted SampleExact (which never slices past EOF), and the
past-EOF tail is left as the zero-fill Pcm<T> already allocates (silence). Update
the straddle test to assert the requested length, the preserved real prefix, and
the silent tail; rename it to Sample_WhenRequestStraddlesEof_ReturnsRequestedLengthWithSilenceTail.

Also assert FramesRendered == 0 in the oversized-frame test: the native Bitmap
dispose on the throw path can't be observed in-process, but a failed render must
not count as a completed frame.

Addresses PR #2012 review feedback (Copilot, Codex P2).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs (1)

195-205: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate NumSamples against the requested chunk length.

A worker can return self-consistent metadata with the wrong length, e.g. request 4 samples but return NumSamples = 2 and DataLength = 16. That passes the current guard, but breaks _currentChunkOffset + _currentChunk.NumSamples chunk invariants and can duplicate/slice the wrong data in SampleExact.

Proposed fix
         if (sampleInfo.NumSamples < 0)
             throw new InvalidOperationException(
                 $"Sample chunk has a negative NumSamples {sampleInfo.NumSamples}.");
 
+        if (sampleInfo.NumSamples != chunkLength)
+            throw new InvalidOperationException(
+                $"Sample chunk has {sampleInfo.NumSamples} samples, but {chunkLength} were requested.");
+
         long expected = (long)sampleInfo.NumSamples * Stereo32BitFloatBytesPerSample;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs` around lines 195 - 205,
`IpcSampleProvider.SampleExact` only validates that `sampleInfo.NumSamples` and
`sampleInfo.DataLength` are self-consistent, but it does not verify that the
returned `NumSamples` matches the requested chunk length. Add a guard in this
method, near the existing `NumSamples` and `DataLength` checks, that compares
`sampleInfo.NumSamples` against the requested chunk size used for the current
read and throws if they differ. Keep the validation before constructing
`Pcm<Stereo32BitFloat>` so `_currentChunkOffset` and `_currentChunk.NumSamples`
remain consistent.
🧹 Nitpick comments (1)
tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs (1)

616-618: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a self-consistent wrong-NumSamples case.

These cases only reject DataLength != NumSamples * 8. Please also cover a worker response like NumSamples = 2, DataLength = 16 for a request of 4 samples, which currently passes the size check but violates the chunk contract. As per coding guidelines, “New logic must ship with a NUnit test under tests/ in the matching test project.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs` around lines 616 -
618, The current Sample_WhenWorkerReportsMismatchedDataLength_Throws test only
covers raw size mismatches, so add a self-consistent wrong-NumSamples case in
IpcProviderContractTests that matches the byte count but violates the chunk
contract. Update the existing test or add a new NUnit case around the same
Sample_WhenWorkerReportsMismatchedDataLength_Throws path to simulate a 4-sample
request with a worker response like NumSamples = 2 and DataLength = 16, and
assert the contract violation is rejected by the relevant IpcProvider
sample-handling logic.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs`:
- Around line 195-205: `IpcSampleProvider.SampleExact` only validates that
`sampleInfo.NumSamples` and `sampleInfo.DataLength` are self-consistent, but it
does not verify that the returned `NumSamples` matches the requested chunk
length. Add a guard in this method, near the existing `NumSamples` and
`DataLength` checks, that compares `sampleInfo.NumSamples` against the requested
chunk size used for the current read and throws if they differ. Keep the
validation before constructing `Pcm<Stereo32BitFloat>` so `_currentChunkOffset`
and `_currentChunk.NumSamples` remain consistent.

---

Nitpick comments:
In `@tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs`:
- Around line 616-618: The current
Sample_WhenWorkerReportsMismatchedDataLength_Throws test only covers raw size
mismatches, so add a self-consistent wrong-NumSamples case in
IpcProviderContractTests that matches the byte count but violates the chunk
contract. Update the existing test or add a new NUnit case around the same
Sample_WhenWorkerReportsMismatchedDataLength_Throws path to simulate a 4-sample
request with a worker response like NumSamples = 2 and DataLength = 16, and
assert the contract violation is rejected by the relevant IpcProvider
sample-handling logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a551f14-6a6e-48d8-99f9-d70cead5b9fa

📥 Commits

Reviewing files that changed from the base of the PR and between 9778773 and 0c61d20.

📒 Files selected for processing (2)
  • src/Beutl.FFmpegIpc/Providers/IpcSampleProvider.cs
  • tests/Beutl.FFmpegIpc.Tests/IpcProviderContractTests.cs

Drop the inline "// Final frame straddles EOF: fill the available prefix..."
note, which restated the three lines below it (the EOF zero-fill strategy is
already documented at the top of Sample), and the test-only cross-reference
phrases ("mirrors the frame-side helpers above", "Mirrors the same-named const
in IpcSampleProvider"). Comment text only; no code change.
@github-actions

Copy link
Copy Markdown
Contributor

No TODO comments were found.

@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage

Package Line Rate Branch Rate Complexity Health
Beutl.Api 9% 4% 1171
Beutl.Configuration 41% 22% 350
Beutl.Controls 0% 0% 5513
Beutl.Core 62% 56% 3069
Beutl.Editor 78% 70% 1742
Beutl.Editor.Components 1% 0% 8552
Beutl.Embedding.MediaFoundation 6% 8% 1376
Beutl.Engine 63% 52% 18123
Beutl.Engine.SourceGenerators 59% 44% 540
Beutl.Extensibility 38% 59% 112
Beutl.Extensions.AVFoundation 5% 12% 246
Beutl.Extensions.FFmpeg 6% 7% 853
Beutl.FFmpegIpc 25% 32% 807
Beutl.Language 7% 50% 1349
Beutl.NodeGraph 24% 15% 2474
Beutl.ProjectSystem 58% 43% 1061
Beutl.Threading 100% 90% 137
Beutl.Utilities 94% 87% 358
Summary 35% (43548 / 124655) 31% (10848 / 35490) 47833

Minimum allowed line rate is 0%

@yuto-trd yuto-trd merged commit 8b16880 into main Jun 25, 2026
4 checks passed
@yuto-trd yuto-trd deleted the yuto-trd/ipc-provider-hardening branch June 25, 2026 05:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants