Skip to content

Commit 9691fe6

Browse files
committed
Fix for null pointer deref after dynamic_cast. Test now passes "green".
Assisted-by: Claude Code / Opus 4.7 Note: Claude Code Opus 4.6 was used to find this bug, and Opus 4.7 was used to fix it.
1 parent e9db591 commit 9691fe6

1 file changed

Lines changed: 43 additions & 3 deletions

File tree

src/opentimelineio/algo/editAlgorithm.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,18 @@ overwrite(
182182
if (!isEqual(second_duration.value(), 0.0))
183183
{
184184
auto second_item = dynamic_cast<Item*>(items.front()->clone());
185-
trimmed_range = second_item->trimmed_range();
186-
source_range = TimeRange(
185+
if (!second_item)
186+
{
187+
// clone() failed or returned an object that is not an Item.
188+
// Bail out before dereferencing nullptr (CWE-476).
189+
if (error_status)
190+
*error_status = ErrorStatus(
191+
ErrorStatus::INTERNAL_ERROR,
192+
"failed to clone item for overwrite split");
193+
return;
194+
}
195+
trimmed_range = second_item->trimmed_range();
196+
source_range = TimeRange(
187197
trimmed_range.start_time() + first_duration
188198
+ range.duration(),
189199
second_duration);
@@ -363,6 +373,16 @@ insert(
363373
if (!isEqual(second_source_range.duration().value(), 0.0))
364374
{
365375
auto second_item = dynamic_cast<Item*>(item->clone());
376+
if (!second_item)
377+
{
378+
// clone() failed or returned an object that is not an Item.
379+
// Bail out before dereferencing nullptr (CWE-476).
380+
if (error_status)
381+
*error_status = ErrorStatus(
382+
ErrorStatus::INTERNAL_ERROR,
383+
"failed to clone item for insert split");
384+
return;
385+
}
366386
second_item->set_source_range(second_source_range);
367387
composition->insert_child(insert_index + 1, second_item);
368388
}
@@ -530,7 +550,17 @@ slice(
530550
item->set_source_range(first_source_range);
531551

532552
// Clone the item for the second slice.
533-
auto second_item = dynamic_cast<Item*>(item->clone());
553+
auto second_item = dynamic_cast<Item*>(item->clone());
554+
if (!second_item)
555+
{
556+
// clone() failed or returned an object that is not an Item.
557+
// Bail out before dereferencing nullptr (CWE-476).
558+
if (error_status)
559+
*error_status = ErrorStatus(
560+
ErrorStatus::INTERNAL_ERROR,
561+
"failed to clone item for slice");
562+
return;
563+
}
534564
const TimeRange second_source_range(
535565
first_source_range.start_time() + first_source_range.duration(),
536566
range.duration() - first_source_range.duration());
@@ -794,6 +824,16 @@ fill(
794824
RationalTime start_time = clip_range.start_time();
795825
const RationalTime gap_start_time = gap_range.start_time();
796826
auto track_item = dynamic_cast<Item*>(item->clone());
827+
if (!track_item)
828+
{
829+
// clone() failed or did not return an Item; bail out safely
830+
// instead of dereferencing nullptr (CWE-476).
831+
if (error_status)
832+
*error_status = ErrorStatus(
833+
ErrorStatus::INTERNAL_ERROR,
834+
"failed to clone item for fill");
835+
return;
836+
}
797837

798838
// Check if start time is less than gap's start time (trim it if so)
799839
if (start_time < gap_start_time)

0 commit comments

Comments
 (0)