Skip to content

Fix source/output lifecycle and correctness bugs#46

Merged
kixelated merged 2 commits into
mainfrom
claude/fix-source-output-lifecycle
Jul 1, 2026
Merged

Fix source/output lifecycle and correctness bugs#46
kixelated merged 2 commits into
mainfrom
claude/fix-source-output-lifecycle

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Four safe, self-contained fixes surfaced by a CodeRabbit review of the in-tree vendoring in moq-dev/moq#1977. Scoped deliberately to the changes that are correct-by-inspection and don't need a live-OBS run to validate; the more invasive lifecycle work is listed under Deferred.

Fixes

  • moq-source: frame timestamp µs → ns. obs_source_frame.timestamp is nanoseconds, but libmoq delivers timestamp_us in microseconds. We were assigning it directly, so timestamps were 1000× too small, skewing async A/V pacing. (timestamp_us is u64, so * 1000 stays in 64-bit.)
  • moq-source: redact the relay URL in logs. The reconnect log printed the full URL, which can carry credentials or a token in userinfo/query. OBS logs are frequently shared for support, so reduce it to scheme://host[:port]. The broadcast name is still logged separately.
  • moq-source: leak ctx instead of freeing it on the teardown timeout. If the 2s wait times out with refs > 0, a libmoq subscription callback still references ctx (its mutex/cond/refs). Destroying the primitives and bfree(ctx) anyway is a use-after-free when that callback fires. Leaking on this abnormal path is strictly safer than a UAF.
  • moq-output: close the session when moq_origin_publish fails. The session has already connected at that point, so returning without Stop(false) leaves a live background session; a retry on the same output then reuses the stale handle. Stop(false) closes it and its terminal callback releases the outstanding-session reference.

Deferred (need a live-OBS + relay run to validate)

Flagged by the same review but intentionally out of scope here, since getting them wrong risks hangs or new UAFs:

  • moq-output teardown UAF on timeout. Unlike moq-source, OBS owns the delete, so it can't just leak; the real fix is to move the callback state (mutex/cv/counter) into an independently-owned block that outlives MoQOutput. That's a refactor worth testing.
  • moq-output terminal callback should stop the output. A mid-stream relay close should propagate to obs_output_signal_stop so OBS/UI reflect it, without double-signalling the user-initiated Stop() path. Needs care around re-entrancy from the libmoq thread.
  • moq-source decode drain loop. avcodec_receive_frame should loop until EAGAIN (and send_packet EAGAIN should drain-then-resend) so reordered/B-frame output isn't dropped. This wraps a long processing body, so it wants a B-frame stream to validate.

Test plan

  • clang-format clean.
  • Not run against a live OBS build in this environment. Each fix is correct-by-inspection; a smoke test (publish + subscribe, check A/V sync and logs) before merge would be good.

(Written by Claude Opus 4.8)

Four safe, self-contained fixes (surfaced by a CodeRabbit review of the
in-tree vendoring in moq-dev/moq#1977):

- moq-source: OBS frame.timestamp is nanoseconds but libmoq delivers
  microseconds, so playback timestamps were 1000x too small, skewing
  async A/V pacing. Multiply by 1000.
- moq-source: redact the relay URL before logging it. URLs can carry
  credentials/tokens and OBS logs are often shared for support.
- moq-source: on the teardown timeout path, leak ctx instead of freeing
  it. If refs is still nonzero a libmoq callback still references ctx
  (mutex/cond/refs), so destroying and freeing it is a use-after-free.
  Leaking on this abnormal path is strictly safer.
- moq-output: close the session when moq_origin_publish fails. The
  session already connected, so returning without closing leaves a live
  background session and a retry on the same output reuses the stale
  handle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@sourcery-ai sourcery-ai 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.

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kixelated, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b2dcef5e-62f7-4336-ac3e-7225387313e1

📥 Commits

Reviewing files that changed from the base of the PR and between c98dfa5 and b4b1ccc.

📒 Files selected for processing (1)
  • src/moq-source.cpp

Walkthrough

This change modifies MoQ output and source handling in OBS. In moq-output.cpp, a failed publish call now triggers session stop cleanup before returning. In moq-source.cpp, teardown timeout handling avoids freeing context under contention, a new function redacts credentials from URLs before logging, the reconnect log message uses redacted URLs, and video frame timestamps are converted from microseconds to nanoseconds.

Changes

File Change Summary
src/moq-output.cpp Calls Stop(false) on publish failure before returning false
src/moq-source.cpp Adds <string> header; refines destroy teardown timeout logic with timed_out flag and early return to avoid use-after-free; adds redact_url() helper; applies redaction to reconnect log; converts frame timestamps from microseconds to nanoseconds

Sequence Diagram(s)

sequenceDiagram
  participant MoQOutput
  participant Session

  MoQOutput->>Session: moq_origin_publish()
  Session-->>MoQOutput: result < 0
  MoQOutput->>MoQOutput: Stop(false)
  MoQOutput-->>MoQOutput: return false
Loading

Related PRs: None identified.

Suggested labels: bugfix, security, source

Suggested reviewers: None identified.

🐰 A hop, a stop, a session closed,
URLs redacted, secrets composed,
Timestamps stretched from micro to nano,
No ghost callbacks left in limbo,
This rabbit reviews with careful paws. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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
Title check ✅ Passed The title accurately summarizes the main lifecycle and correctness fixes in moq-source and moq-output.
Description check ✅ Passed The description matches the implemented fixes and is clearly related to the changeset.
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.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/fix-source-output-lifecycle

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@src/moq-source.cpp`:
- Around line 282-285: The userinfo stripping logic in the authority parsing
path is using the first `@`, which can leave part of a password in logs for URLs
handled by `moq::Source` parsing. Update the `authority` cleanup in the same
block to use the last `@` instead, so everything before the final authority
delimiter is removed before logging or further processing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3a704d74-5472-4545-8ccf-0ded3071207d

📥 Commits

Reviewing files that changed from the base of the PR and between bb3b128 and c98dfa5.

📒 Files selected for processing (2)
  • src/moq-output.cpp
  • src/moq-source.cpp

Comment thread src/moq-source.cpp Outdated
find('@') would leave part of a password behind for a URL like
moq://user:p@ss@host; rfind('@') removes everything up to the final
authority delimiter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated kixelated merged commit 323d597 into main Jul 1, 2026
7 checks passed
kixelated added a commit to moq-dev/moq that referenced this pull request Jul 1, 2026
Pull moq-dev/obs#46 (323d597) into the vendored copy: frame timestamp
µs->ns, relay-URL redaction in logs, leak-not-free on the source teardown
timeout, and close-session-on-publish-failure. Keeps cpp/obs byte-identical
to upstream main (plus the SPDX header).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant