Skip to content

Bound buffered SDK output capture across language surfaces - #1627

Open
Safet Hrbinic (safmswork) wants to merge 1 commit into
microsoft:mainfrom
safmswork:fix/bounded-sdk-output-capture
Open

Safet Hrbinic (safmswork) wants to merge 1 commit into
microsoft:mainfrom
safmswork:fix/bounded-sdk-output-capture

Conversation

@safmswork

@safmswork Safet Hrbinic (safmswork) commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

📖 Description

Problem: we have APIs that will launch an executable in a container, collect all the output the executable makes to STDOUT, then return that output once the executable is done. Some examples of these APIs:
Rust SDK: run ,  run_in_container ,  wait_with_output 
Node SDK:  run ,  runInContainer 
C SDK: mxc_run_json

The problem is that there is currently no limits to the STDOUT output gathered. This has the potential to overrun the buffers and memory of the process who called this API to launch the container. This is a potential security risk.

The solution: retain at most 1 MiB per output stream while continuing to drain excess output. It's important to continue to drain the output even though we aren't giving it back to the API caller, so that STDOUT doesn't get full and cause the process in the container to wait excessively or even error out. If this output limit get's hit, we will report truncation through existing warnings.

This change also adds regression coverage and SDK documentation.

NOTE: A decision needs to be made here, whether to make the fix in the V1 of the APIs, or make a V2 and do the change there. Personally I'm leaning towards doing it in the V1 even though this could be considered a breaking change, but I think the security aspect overrides that. But I definitely would like to solicit other opinions here.

🔗 References

Bug 1628

🔍 Validation

✅ Checklist

  • Signed the Contributor License Agreement
  • Linked to an issue
  • For features or changes requiring a spec: linked the issue with maintainer-applied feature-accepted
  • For implementation: linked the required approved and merged design/API spec, or explained why no spec is required
  • Feature issue remains open unless this PR completes all agreed implementation, validation, and documentation
  • Updated documentation (if applicable)
  • Updated Copilot instructions (if build, architecture, or conventions changed)
  • If this PR changes Cargo.lock, the dependency-feed-check check passes (see pull request builds)

📋 Issue Type

  • Bug fix
  • Feature
  • Task
Microsoft Reviewers: Open in CodeFlow

Retain at most 1 MiB per output stream while continuing to drain excess output. Report truncation through existing warnings and add regression coverage and SDK documentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a2e7e097-c92f-44be-9d0f-1d6985c965cc
Copilot AI balanced review requested due to automatic review settings October 31, 2026 02:09

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.

🟡 Changes recommended

The supported V1 buffered execution APIs now discard output beyond 1 MiB per stream, breaking existing Rust, .NET, and Node callers that require complete responses even when workloads exit successfully.

1 open issue
What changed in this PR

This PR bounds buffered output capture across MXC’s Rust, Node, and .NET SDKs while continuing to drain excess output so workloads can finish.

Changes:

  • Retain the first 1 MiB per stream and report truncation through existing warnings.
  • Add regression coverage for capture limits, draining, read errors, and workload outcomes.
  • Document the limits and add C ABI tests to the macOS build job.
File Description
src/​mxc-sdk/​tests/​streaming.rs Adds Seatbelt bounded-capture coverage.
src/​mxc-sdk/​tests/​streaming_bubblewrap.rs Adds Bubblewrap bounded-capture coverage.
src/​mxc-sdk/​src/​sandbox.rs Bounds Rust capture and adds warnings and unit tests.
src/​mxc-sdk/​README.md Documents Rust capture limits and streaming alternatives.
src/​ffi/​mxc_ffi/​tests/​ffi.rs Tests bounded capture and NUL conversion through the C ABI.
src/​ffi/​mxc_ffi/​src/​lib.rs Documents C ABI capture limits.
sdk/​node/​tests/​unit/​state-aware.test.ts Tests capture boundaries, draining, and read errors.
sdk/​node/​tests/​integration/​isolation-session-state-aware.test.ts Adds lifecycle capture-limit integration coverage.
sdk/​node/​src/​v1/​types.ts Documents captured output limits on result fields.
sdk/​node/​src/​v1/​lifecycle.ts Bounds lifecycle capture and reports truncation.
sdk/​node/​README.md Explains Node capture limits and streaming alternatives.
sdk/​dotnet/​README.md Explains .NET capture limits and warning locations.
sdk/​dotnet/​Microsoft.Mxc.Sdk/​V1/​MxcProcess.cs Bounds managed capture and exposes truncation warnings.
sdk/​dotnet/​Microsoft.Mxc.Sdk/​V1/​ExecutionResult.cs Updates output and warning documentation.
sdk/​dotnet/​Microsoft.Mxc.Sdk.Tests/​V1/​MxcLifecycleE2ETests.cs Tests lifecycle capture limits and warnings.
sdk/​dotnet/​Microsoft.Mxc.Sdk.Tests/​V1/​MxcContainerIsolationSessionE2ETests.cs Tests native and managed buffered capture.
sdk/​dotnet/​Microsoft.Mxc.Sdk.Tests/​MxcProcessTests.cs Tests boundaries, draining, cancellation, and read failures.
docs/​development/​build-and-test/​ci-validation-infrastructure.md Describes capture-limit validation coverage.
docs/​api-reference/​rust/​v1/​types.md Documents Rust result retention limits.
docs/​api-reference/​node/​v1/​types.md Documents Node buffered output limits.
docs/​api-reference/​dotnet/​v1/​types.md Documents .NET result and process capture limits.
.github/​workflows/​Build.MacOS.Job.yml Adds the C ABI test suite to macOS validation.

Review effort: Balanced

Comment thread src/mxc-sdk/src/sandbox.rs Resolved
@safmswork
Safet Hrbinic (safmswork) marked this pull request as ready for review October 31, 2026 03:36
@safmswork
Safet Hrbinic (safmswork) requested a review from a team October 31, 2026 03:36
@safmswork
Safet Hrbinic (safmswork) requested a review from a team as a code owner October 31, 2026 03:36
@bbonaby Branden Bonaby (bbonaby) linked an issue Nov 3, 2026 that may be closed by this pull request
@safmswork

Copy link
Copy Markdown
Contributor Author

Copilot resolve the merge conflicts in this pull request

Copilot AI balanced review requested due to automatic review settings November 6, 2026 00:21

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.

🟡 Changes recommended

The output-limit documentation still incorrectly promises an unconditional 1 MiB cap, omitting MicroVM’s separate 64 MiB combined stdout/stderr limit, overflow errors, and lack of streaming support.

1 open issue
1 resolved since last review

Review effort: Balanced

Comment on lines +369 to +370
/// Retains at most 1 MiB per stream before text conversion, drains excess
/// output, and reports truncation in `warnings_json_utf8`.
@safmswork
Safet Hrbinic (safmswork) force-pushed the fix/bounded-sdk-output-capture branch from 1b3bc44 to f2e7eaa Compare November 6, 2026 00:30
Copilot AI balanced review requested due to automatic review settings November 6, 2026 00:30

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.

🟡 Changes recommended

WSLc buffered execution still permits unbounded callback-side output queues, allowing fast producers to exhaust host memory despite the 1 MiB result limit.

2 open issues

Review effort: Balanced

Comment on lines +431 to +433
let retained = read.min(MAX_CAPTURED_OUTPUT_BYTES - buf.len());
buf.extend_from_slice(&chunk[..retained]);
truncated |= retained < read;
@MGudgin

Copy link
Copy Markdown
Member

I understand the concern about unbounded output consuming the caller’s memory. However, the demonstrated risk is memory exhaustion, not buffer corruption or a sandbox escape. That deserves consideration, but it does not by itself justify breaking the existing V1 API contract.

These APIs previously returned complete output. Imposing a fixed 1 MiB limit means legitimate workloads can now complete successfully while returning incomplete results—for example, a truncated JSON document. A warning does not prevent existing callers from breaking, and the rationale for choosing 1 MiB has not been established.

I would prefer to preserve V1 behavior and introduce bounded capture as an explicit choice, using a new versioned API where necessary. An unconditional V1 change needs a stronger explanation of the threat, why existing safeguards are insufficient, and why the compatibility cost is justified.

Separately, the Node implementation allocates 2 MiB per execution even when no output is produced. Storage should grow with actual output, up to the chosen limit, rather than imposing that overhead on every caller.

This branch has not been deployed

No deployments
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.

Unbounded SDK stdout/stderr capture

3 participants