Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 98cb7909 authored by Fang Zhaohui's avatar Fang Zhaohui
Browse files

Fix sanitizer overflow crash in mp4 extractor.



mp4 extractor experienced a sanitizer overflow error due to an
overflow when calculating the actual seek position based on the
track playback offset.
The problematic video has an abnormally high offset value.

Referencing media3 to implement rescaleTime: ensure that precision
is not lost during the calculation and to handle overflow cases.

Bug: 397619415
Test: atest MediaExtractorTest

Change-Id: Ieb7ad96dde6d345a306083bb60aa754aaff4f97e
Signed-off-by: default avatarFang Zhaohui <fangzhaohui@xiaomi.corp-partner.google.com>
parent a14c8449
Loading
Loading
Loading
Loading
+91 −11
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <algorithm>
#include <map>
#include <memory>
#include <numeric>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -191,6 +192,8 @@ private:
    size_t getNALLengthSizeFromAvcCsd(const uint8_t *data, const size_t size) const;
    size_t getNALLengthSizeFromHevcCsd(const uint8_t *data, const size_t size) const;

    int64_t rescaleTime(int64_t value, int64_t scale, int64_t originScale) const;

    struct TrackFragmentHeaderInfo {
        enum Flags {
            kBaseDataOffsetPresent         = 0x01,
@@ -6230,6 +6233,63 @@ size_t MPEG4Source::getNALLengthSizeFromHevcCsd(const uint8_t *data, const size_
    return 1 + (data[14 + 7] & 3);
}

int64_t MPEG4Source::rescaleTime(int64_t value, int64_t scale, int64_t originScale) const {
    // Rescale time: calculate value * scale / originScale
    if (value == 0 || scale == 0) {
        return 0;
    }

    CHECK(value > 0);
    CHECK(scale > 0);
    CHECK(originScale > 0);

    if (originScale >= scale && (originScale % scale) == 0) {
        int64_t factor = originScale / scale;
        return value / factor;
    } else if (originScale < scale && (scale % originScale) == 0) {
        int64_t factor = scale / originScale;
        if (__builtin_mul_overflow(value, factor, &value)) {
            return std::numeric_limits<int64_t>::max();
        }
        return value;
    } else if (originScale >= value && (originScale % value) == 0) {
        int64_t factor = originScale / value;
        return scale / factor;
    } else if (originScale < value && (value % originScale) == 0) {
        int64_t factor = value / originScale;
        if (__builtin_mul_overflow(scale, factor, &value)) {
            return std::numeric_limits<int64_t>::max();
        }
        return value;
    } else {
        int64_t rescaleValue;
        if (!__builtin_mul_overflow(value, scale, &rescaleValue)) {
            return rescaleValue / originScale;
        } else {
            // Divide the max gcd before calc scale/originScale
            int64_t gcdOfScaleAndOriginScale = std::gcd(scale, originScale);
            int64_t simpleScale = scale / gcdOfScaleAndOriginScale;
            int64_t simpleOriginScale = originScale / gcdOfScaleAndOriginScale;
            // Divide the max gcd before calc value/simpleOriginScale
            int64_t gcdOfValueAndSimpleOriginScale = std::gcd(value, simpleOriginScale);
            int64_t simpleValue = value / gcdOfValueAndSimpleOriginScale;
            simpleOriginScale /= gcdOfValueAndSimpleOriginScale;

            if (!__builtin_mul_overflow(simpleValue, simpleScale, &simpleValue)) {
                return simpleValue / simpleOriginScale;
            } else {
                // Fallback using long double to calculate the rescale value
                long double rescale = (long double)value / originScale * scale;
                if (rescale > std::numeric_limits<int64_t>::max()) {
                    return std::numeric_limits<int64_t>::max();
                }

                return rescale;
            }
        }
    }
}

media_status_t MPEG4Source::read(
        MediaBufferHelper **out, const ReadOptions *options) {
    Mutex::Autolock autoLock(mLock);
@@ -6294,16 +6354,26 @@ media_status_t MPEG4Source::read(
            if( mode != ReadOptions::SEEK_FRAME_INDEX) {
                int64_t elstInitialEmptyEditUs = 0, elstShiftStartUs = 0;
                if (mElstInitialEmptyEditTicks > 0) {
                    elstInitialEmptyEditUs = ((long double)mElstInitialEmptyEditTicks * 1000000) /
                                             mTimescale;
                    elstInitialEmptyEditUs = rescaleTime(mElstInitialEmptyEditTicks, 1000000,
                            mTimescale);

                    /* Sample's composition time from ctts/stts entries are non-negative(>=0).
                     * Hence, lower bound on seekTimeUs is 0.
                     */
                    seekTimeUs = std::max(seekTimeUs - elstInitialEmptyEditUs, (int64_t)0);
                    if (__builtin_sub_overflow(seekTimeUs, elstInitialEmptyEditUs,
                            &seekTimeUs) || seekTimeUs < 0) {
                        ALOGW("seekTimeUs:%" PRId64 " would be a bogus value, set to 0",
                                seekTimeUs);
                        seekTimeUs = 0;
                    }
                }
                if (mElstShiftStartTicks > 0) {
                    elstShiftStartUs = ((long double)mElstShiftStartTicks * 1000000) / mTimescale;
                    seekTimeUs += elstShiftStartUs;
                    elstShiftStartUs = rescaleTime(mElstShiftStartTicks, 1000000, mTimescale);

                    if (__builtin_add_overflow(seekTimeUs, elstShiftStartUs, &seekTimeUs)) {
                        ALOGW("seek + elst shift start would be overflow, round to max");
                        seekTimeUs = std::numeric_limits<int64_t>::max();
                    }
                }
                ALOGV("shifted seekTimeUs:%" PRId64 ", elstInitialEmptyEditUs:%" PRIu64
                      ", elstShiftStartUs:%" PRIu64, seekTimeUs, elstInitialEmptyEditUs,
@@ -6741,16 +6811,26 @@ media_status_t MPEG4Source::fragmentedRead(
        ALOGV("seekTimeUs:%" PRId64, seekTimeUs);
        int64_t elstInitialEmptyEditUs = 0, elstShiftStartUs = 0;
        if (mElstInitialEmptyEditTicks > 0) {
            elstInitialEmptyEditUs = ((long double)mElstInitialEmptyEditTicks * 1000000) /
                                     mTimescale;
            elstInitialEmptyEditUs = rescaleTime(mElstInitialEmptyEditTicks, 1000000,
                    mTimescale);

            /* Sample's composition time from ctts/stts entries are non-negative(>=0).
             * Hence, lower bound on seekTimeUs is 0.
             */
            seekTimeUs = std::max(seekTimeUs - elstInitialEmptyEditUs, (int64_t)0);
            if (__builtin_sub_overflow(seekTimeUs, elstInitialEmptyEditUs,
                    &seekTimeUs) || seekTimeUs < 0) {
                ALOGW("seekTimeUs:%" PRId64 " would be a bogus value, set to 0",
                        seekTimeUs);
                seekTimeUs = 0;
            }
        }
        if (mElstShiftStartTicks > 0) {
            elstShiftStartUs = ((long double)mElstShiftStartTicks * 1000000) / mTimescale;
            seekTimeUs += elstShiftStartUs;
            elstShiftStartUs = rescaleTime(mElstShiftStartTicks, 1000000, mTimescale);

            if (__builtin_add_overflow(seekTimeUs, elstShiftStartUs, &seekTimeUs)) {
                ALOGW("seek + elst shift start would be overflow, round to max");
                seekTimeUs = std::numeric_limits<int64_t>::max();
            }
        }
        ALOGV("shifted seekTimeUs:%" PRId64 ", elstInitialEmptyEditUs:%" PRIu64
              ", elstShiftStartUs:%" PRIu64, seekTimeUs, elstInitialEmptyEditUs,