Skip to content

Fix null pointer dereferences after dynamic_cast (rewritten)#2020

Open
jminor wants to merge 6 commits intoAcademySoftwareFoundation:mainfrom
jminor:fix-null-ptr-deref-no-llm
Open

Fix null pointer dereferences after dynamic_cast (rewritten)#2020
jminor wants to merge 6 commits intoAcademySoftwareFoundation:mainfrom
jminor:fix-null-ptr-deref-no-llm

Conversation

@jminor
Copy link
Copy Markdown
Collaborator

@jminor jminor commented May 1, 2026

Certain invalid OTIO object graphs may cause crashes due to code that assumes a dynamic_cast always returns a valid object. This PR introduces tests which demonstrate the issue and a fix to prevent the crash in those cases.

I made sure to write the tests first, ensure that they caused the crash (SEGFAULT) and then made the fix to each of the affected functions. I introduced a new ErrorStatus::CANNOT_CLONE_ITEM for this specific case. The code may also return ErrorStatus::TYPE_MISMATCH in a related edge case, but I was not able to create a scenario which triggers that case.

Note that the discovery of this issue was assisted by Claude Code Opus 4.6 and Opus 4.7, but all of the code in this PR was written by a human (me). This PR replaces #2017 which had LLM-generated code in it.

jminor added 6 commits May 1, 2026 00:19
Signed-off-by: Joshua Minor <jminor@users.noreply.github.com>
Signed-off-by: Joshua Minor <jminor@users.noreply.github.com>
Signed-off-by: Joshua Minor <jminor@users.noreply.github.com>
Signed-off-by: Joshua Minor <jminor@users.noreply.github.com>
Signed-off-by: Joshua Minor <jminor@users.noreply.github.com>
Signed-off-by: Joshua Minor <jminor@users.noreply.github.com>
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.26%. Comparing base (c51d995) to head (40478d6).

Files with missing lines Patch % Lines
src/opentimelineio/algo/editAlgorithm.cpp 0.00% 42 Missing ⚠️
src/opentimelineio/errorStatus.cpp 0.00% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2020      +/-   ##
==========================================
- Coverage   84.50%   84.26%   -0.25%     
==========================================
  Files         181      181              
  Lines       13223    13261      +38     
  Branches     1202     1218      +16     
==========================================
  Hits        11174    11174              
- Misses       1866     1904      +38     
  Partials      183      183              
Flag Coverage Δ
py-unittests 84.26% <0.00%> (-0.25%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/opentimelineio/errorStatus.h 100.00% <ø> (ø)
src/opentimelineio/errorStatus.cpp 51.61% <0.00%> (-1.73%) ⬇️
src/opentimelineio/algo/editAlgorithm.cpp 0.00% <0.00%> (ø)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c51d995...40478d6. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Collaborator

@meshula meshula left a comment

Choose a reason for hiding this comment

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

This looks more rigorous than the initial fix.

Copy link
Copy Markdown
Contributor

@darbyjohnston darbyjohnston left a comment

Choose a reason for hiding this comment

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

Thanks, I think this is clearer about what is being tested than the previous PR. When we are happy with this one, try asking Claude to compare the two and see what it thinks about the changes.

if (!cloned_item)
{
if (error_status)
*error_status = ErrorStatus::CANNOT_CLONE_ITEM;
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.

I think this error status should be set in clone(), so it is set only once and not at each call site. Something like:

auto cloned_item = items.front()->clone(error_status);
if (!cloned_item || (error_status && is_error(error_status))
    return;
SerializableObject*
SerializableObject::clone(ErrorStatus* error_status) const
{
    ...
    SerializableObject*r = e._root.type() == typeid(SerializableObject::Retainer<>)
               ? std::any_cast<SerializableObject::Retainer<>&>(e._root)
                     .take_value()
               : nullptr;
    if (!r && error_status)
        *error_status = ErrorStatus::CANNOT_CLONE_ITEM;
    return r;
}

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.

4 participants