NativeInstruments / ni-media

NI Media is a C++ library for reading and writing audio streams.
MIT License
235 stars 34 forks source link

Fix potential integer overflow at end of stream in media_foundation_helper #74

Closed wro-ableton closed 4 months ago

wro-ableton commented 4 months ago

If in SyncronousByteStream::Read, m_source.read() reaches the end of stream, boost::iostreams::file_descriptor returns -1 as an end of stream indicator. Previously, by casting the returned value to ULONG, an integer overflow would occur. In the attached file, the observed behaviour then would be that the calling code in WMF would continue to attempt to read past the EOF indefinitely or until some unexpected state is reached.

To fix this, the end of stream indicator (-1) is caught and handled as "0 bytes read".

See the attached TestMp3.zip for a mp3 which triggers this edge case.

See Issue https://github.com/NativeInstruments/ni-media/issues/73

codecov[bot] commented 4 months ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 83.10%. Comparing base (89eb1e3) to head (27b6639).

Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #74 +/- ## ======================================= Coverage 83.10% 83.10% ======================================= Files 82 82 Lines 2024 2024 ======================================= Hits 1682 1682 Misses 342 342 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

ni-mheppner commented 4 months ago

Looks good to me.

wro-ableton commented 4 months ago

If you all are fine with the change, feel free to merge. We'd definitely appreciate a quick fix. 😁

ni-mheppner commented 4 months ago

Actually, are we sure that -1 is the only negative number a boost::iostreams::file_descriptor will ever return? According to the documentation it should be, but maybe checking the return code against negative numbers in general might be more robust.

wro-ableton commented 4 months ago

I was wondering about that as well. It might be a bit defensive but maybe we could also catch any negative and translate the -1 end of sequence to 0, otherwise return either E_UNEXPECTED or E_FAIL. Any preference?

try
{
    const auto result =  m_source.read( reinterpret_cast<char*>( buffer ), toRead );
    if (result < -1) 
    {
        return E_FAIL;
    }
    *read = static_cast<ULONG>( result == -1 ? 0 : result );
    return S_OK;
}
catch ( const std::system_error& )
{
    return E_UNEXPECTED;
}
catch ( ... )
{
    return E_FAIL;
}

Alternatively, one could simply catch any "<0" cases with the following. S

*read = static_cast<ULONG>( result < 0 ? 0 : result );
ni-tsmit commented 4 months ago

Looks good to me too, if read() promises non-negative or -1 as return value then I think a check against a define with endOfSequence suffices and tells the clearest story.

wro-ableton commented 4 months ago

See fixup. So static constexpr auto endOfSequence =-1; but without the defensive return?