Skip to content

addref to obs_source_t* before getProperties#1701

Merged
sandboxcoder merged 2 commits into
stagingfrom
rno/browser-src-getprops
May 21, 2026
Merged

addref to obs_source_t* before getProperties#1701
sandboxcoder merged 2 commits into
stagingfrom
rno/browser-src-getprops

Conversation

@sandboxcoder
Copy link
Copy Markdown
Contributor

@sandboxcoder sandboxcoder commented May 20, 2026

Description

  • add concurrency test. Truth be told, I could not get the crash to trigger (even w/o the fix). But this scenario is the only way I could see this happening where a NULL pointer somehow gets inside of strchr even though we already checked for NULL. The theory is the browser_source is released by another thread while we are getting properties looking at the call stack from sentry.
  • addref to obs_source_t* before calling getProperties to handle the rare race where the browser_source was destroyed right after the null check on the url but before we could get the path. Typically, obs adds a ref but externally we cannot invoke obs_source_addref() directly. So instead we will use OBSSource

Motivation and Context

Fix for a crash report on sentry. The most likely root cause is a race condition: the browser source is released (its private data freed) between the null check at line 254 and the actual obs_source_properties call at line 261, so
browser_source_get_properties sees a partially-freed struct where the string field is null (see slobs repo- obs-browser-plugin.cpp).

How Has This Been Tested?

Adding test case that will validate this code.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

  • The code has been tested.
  • All commit messages are properly formatted and commits squashed where appropriate.
  • I have included updates to all appropriate documentation.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request addresses a rare crash when retrieving properties from a source (notably browser_source) by holding a strong reference to the obs_source_t during GetProperties, and adds a regression test intended to exercise the release-vs-properties race.

Changes:

  • Update Source::GetProperties to use an OBSSource strong reference when looking up the source by id.
  • Add a Mocha test that races input.properties against input.release() across multiple iterations to detect crashes.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
obs-studio-server/source/osn-source.cpp Uses a strong source reference during GetProperties to reduce lifetime/race issues.
tests/osn-tests/src/test_osn_source.ts Adds a concurrency-focused regression test for BrowserSource properties retrieval vs release.

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

Comment thread tests/osn-tests/src/test_osn_source.ts
Comment thread obs-studio-server/source/osn-source.cpp Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread tests/osn-tests/src/test_osn_source.ts Outdated
Comment thread obs-studio-server/source/osn-source.cpp
* add concurrency test
* addref to obs_source_t* before calling getProperties to handle the rare
race where the browser_source was destroyed right after the null check
on the url but before we could get the path. Typically, obs adds a ref
but externally we cannot invoke obs_source_addref() directly. So instead
we will use OBSSource
* update skipSource() so these tests can be run locally on Darwin
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread obs-studio-server/source/osn-source.cpp
@sandboxcoder sandboxcoder marked this pull request as ready for review May 20, 2026 22:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment on lines +253 to +256
// Attempt to find the source asked to load. Get a strong reference to help
// guarantee the source is not destroyed while we are attempting to retrieve
// properties.
OBSSource src = osn::Source::Manager::GetInstance().find(args[0].value_union.ui64);
Copy link
Copy Markdown
Contributor Author

@sandboxcoder sandboxcoder May 21, 2026

Choose a reason for hiding this comment

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

good suggestion. However,osn::Source::Manager is a generic Singleton container. This suggestion means the ref counting belongs at the container level where we increment the ref count upon assignment (actually, the suggestion actually means just add a new findStrongRef function)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually I do not think I need to add this since OBSSource should implicitly handle this.

Comment on lines +594 to +622
it('Get properties of browser source while releasing concurrently does not crash', async function() {
if (obs.skipSource(EOBSInputTypes.BrowserSource)) { this.skip(); } // Skip if browser source is not supported
const iterations = 20;
const promises: Promise<void>[] = [];

for (let i = 0; i < iterations; i++) {
const input = osn.InputFactory.create(EOBSInputTypes.BrowserSource, 'browser_concurrent_' + i);
expect(input).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateInput, EOBSInputTypes.BrowserSource));

// Race getProperties against release on separate async ticks
const getProps = new Promise<void>(resolve => {
setImmediate(() => {
try { input.properties; } catch (_) {}
resolve();
});
});

const releaseSource = new Promise<void>(resolve => {
setImmediate(() => {
try { input.release(); } catch (_) {}
resolve();
});
});

promises.push(getProps, releaseSource);
}

// If the race condition is present, one of these will crash the OBS server process
await Promise.all(promises);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is true, it appears this is mostly a stress test in its' current form. Would likely need to convert this into a C++ Catch2 test

@sandboxcoder sandboxcoder merged commit 1b43d46 into staging May 21, 2026
23 checks passed
@sandboxcoder sandboxcoder deleted the rno/browser-src-getprops branch May 21, 2026 02:17
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.

3 participants