Skip to content

Large positive integers in OTIO JSON file misinterpreted as negative #884

@peachey

Description

@peachey

Bug Report

Incorrect Functionality

A positive integer value greater than 2**31 - 1 in an OTIO JSON file will be read incorrectly by opentimelineio.adapters.read_from_file and may be misrepresented as a negative value due to 32-bit integer overflow.

To Reproduce

Run this python script:

import opentimelineio as otio
timeline = otio.adapters.read_from_file("test_input.otio")
otio.adapters.write_to_file(timeline, "test_output.otio")

using the input JSON OTIO file test_input.otio which is as follows:

{
    "OTIO_SCHEMA": "Timeline.1",
    "metadata": {},
    "name": "testdata",
    "global_start_time": null,
    "tracks": {
        "OTIO_SCHEMA": "Stack.1",
        "metadata": {},
        "name": "tracks",
        "source_range": null,
        "effects": [],
        "markers": [],
        "children": [
            {
                "OTIO_SCHEMA": "Track.1",
                "metadata": {},
                "name": "",
                "source_range": null,
                "effects": [],
                "markers": [],
                "children": [
                    {
                        "OTIO_SCHEMA": "Clip.1",
                        "metadata": {
                            "foobar": {
                                 "maxint32": 2147483647,
                                 "toobig": 2147483648,
                                 "verybig": 3450100000
                            }
                        },
                        "name": "black",
                        "source_range": {
                            "OTIO_SCHEMA": "TimeRange.1",
                            "duration": {
                                "OTIO_SCHEMA": "RationalTime.1",
                                "rate": 24.0,
                                "value": 24.0
                            },
                            "start_time": {
                                "OTIO_SCHEMA": "RationalTime.1",
                                "rate": 24.0,
                                "value": 1.0
                            }
                        },
                        "effects": [],
                        "markers": [],
                        "media_reference": {
                            "OTIO_SCHEMA": "ExternalReference.1",
                            "metadata": {},
                            "name": "",
                            "available_range": {
                                "OTIO_SCHEMA": "TimeRange.1",
                                "duration": {
                                    "OTIO_SCHEMA": "RationalTime.1",
                                    "rate": 24.0,
                                    "value": 240.0
                                },
                                "start_time": {
                                    "OTIO_SCHEMA": "RationalTime.1",
                                    "rate": 24.0,
                                    "value": 1.0
                                }
                            },
                            "target_url": "black.mov"
                        }
                    }
                ],
                "kind": "Video"
            }
        ]
    }
}

Then diff test_input.otio test_output.otio and you will see something like this:

<                                  "maxint32": 2147483647,
<                                  "toobig": 2147483648,
<                                  "verybig": 3450100000
---
>                                 "maxint32": 2147483647,
>                                 "toobig": -2147483648,
>                                 "verybig": -844867296

Note how the integer value of toobig turns from positive to negative, and the value of verybig is changed to a negative value that is completely different due to misrepresentation as a 32-bit integer.

Tested on:

  • Linux ubuntu 20.04 with Python 3.8.5 (also reproduced on RHEL 7 with Python 2.7)
  • OTIO version: commit 546b333
  • gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)

Expected Behavior

The expected behavior is that the contents of the test_output.otio file will be equivalent to the test_input.otio, that is, identical except for insignificant differences such as whitespace changes and ordering of keys in objects.

Additional Context

I think this is a result of line 41 of src/opentimelineio/deserialization.cpp:

    bool Uint(unsigned u) {  return store(any(int(u))); }

I have tested the following alternative code which seems to fix the bug:

    bool Uint(unsigned u) {
        if (u > INT32_MAX) {
            return store(any(int64_t(u)));
        }
        return store(any(int(u)));
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA problem, flaw, or broken functionality.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions