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

Commit 11ac2dfe authored by Max Spector's avatar Max Spector Committed by Gerrit Code Review
Browse files

Merge changes I92f232e9,I955b4ef3

* changes:
  Added cc_defaults for decoder fuzzers
  Added mpeg4_enc_fuzzer and h263_enc_fuzzer
parents b44ae1f1 811108a2
Loading
Loading
Loading
Loading
+57 −18
Original line number Diff line number Diff line
@@ -18,25 +18,24 @@
 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
 */

cc_fuzz {
    name: "mpeg4_dec_fuzzer",
cc_defaults {
    name: "mpeg4_h263_dec_fuzz_defaults",

    host_supported: true,

    srcs: [
        "mpeg4_h263_dec_fuzzer.cpp",
    ],

    static_libs: [
        "libstagefright_m4vh263dec",
        "liblog",
    ],

    cflags: [
        "-DOSCL_IMPORT_REF=",
        "-DMPEG4",
    ],
    target: {
        darwin: {
            enabled: false,
        },
    },

    fuzz_config: {
        cc: [
            "android-media-fuzzing-reports@google.com",
@@ -45,24 +44,46 @@ cc_fuzz {
    },
}

cc_fuzz {
    name: "mpeg4_dec_fuzzer",

    defaults: [
        "mpeg4_h263_dec_fuzz_defaults",
    ],

    cflags: [
        "-DMPEG4",
    ],
}

cc_fuzz {
    name: "h263_dec_fuzzer",

    defaults: [
        "mpeg4_h263_dec_fuzz_defaults",
    ],
}

cc_defaults {
    name: "mpeg4_h263_enc_fuzz_defaults",

    host_supported: true,
    srcs: [
        "mpeg4_h263_dec_fuzzer.cpp",

    srcs: ["mpeg4_h263_enc_fuzzer.cpp"],

    shared_libs: [
        "libutils",
        "liblog",
    ],

    static_libs: [
        "libstagefright_m4vh263dec",
        "liblog",
        "libstagefright_m4vh263enc",
    ],

    cflags: [
        "-DOSCL_IMPORT_REF=",
        "-Wall",
        "-Werror",
    ],
    target: {
        darwin: {
            enabled: false,
        },
    },
    fuzz_config: {
        cc: [
            "android-media-fuzzing-reports@google.com",
@@ -70,3 +91,21 @@ cc_fuzz {
        componentid: 155276,
    },
}

cc_fuzz {
    name: "mpeg4_enc_fuzzer",

    defaults: [
        "mpeg4_h263_enc_fuzz_defaults",
    ],

    cflags: ["-DMPEG4"],
}

cc_fuzz {
    name: "h263_enc_fuzzer",

    defaults: [
        "mpeg4_h263_enc_fuzz_defaults",
    ],
}
+101 −0
Original line number Diff line number Diff line
@@ -52,6 +52,107 @@ To run on host
  $ $ANDROID_HOST_OUT/fuzz/x86_64/h263_dec_fuzzer/h263_dec_fuzzer CORPUS_DIR
```

# Fuzzer for libstagefright_m4vh263enc encoder

## Plugin Design Considerations
The fuzzer plugin for MPEG4/H263 is designed based on the understanding of the
codec and tries to achieve the following:

##### Maximize code coverage
The configuration parameters are not hardcoded, but instead selected based on
incoming data. This ensures more code paths are reached by the fuzzer.

MPEG4/H263 supports the following parameters:
1. Frame Width (parameter name: `encWidth`)
2. Frame Height (parameter name: `encHeight`)
3. Rate control mode (parameter name: `rcType`)
4. Number of bytes per packet (parameter name: `packetSize`)
5. Qp for I-Vop(parameter name: `iQuant`)
6. Qp for P-Vop (parameter name: `pQuant`)
7. Enable RVLC mode (parameter name: `rvlcEnable`)
8. Quantization mode (parameter name: `quantType`)
9. Disable frame skipping (parameter name: `noFrameSkipped`)
10. Enable scene change detection (parameter name: `sceneDetect`)
11. Number of intra MBs in P-frame(parameter name: `numIntraMB`)
12. Search range of ME (parameter name: `searchRange`)
13. Enable 8x8 ME and MC (parameter name: `mv8x8Enable`)
14. Enable AC prediction (parameter name: `useACPred`)
15. Threshold for intra DC VLC (parameter name: `intraDCVlcTh`)
16. Encoding Mode (parameter name: `encMode`)

| Parameter| Valid Values| Configured Value|
|------------- |-------------| ----- |
| `rcType` | 0. `CONSTANT_Q` 1. `CBR_1` 2. `VBR_1` 3. `CBR_2` 4. `VBR_2` 5. `CBR_LOWDELAY` | All the bits of 6th byte of data modulus 6 |
| `packetSize` | In the range `0 to 255` | All the bits of 7th byte of data |
| `iQuant` | In the range `1 to 31` | All the bits of 8th byte of data |
| `pQuant` | In the range `1 to 31` | All the bits of 9th byte of data |
| `rvlcEnable` | 0. `PV_OFF` 1. `PV_ON` | bit 0 of 10th byte of data |
| `quantType` | 0. `0` 1. `1` | bit 0 of 11th byte of data |
| `noFrameSkipped` | 0. `PV_OFF` 1. `PV_ON` | bit 0 of 12th byte of data |
| `sceneDetect` | 0. `PV_OFF` 1. `PV_ON` | bit 0 of 13th byte of data |
| `numIntraMB` | In the range `0 to 7` | bit 0, 1 and 2 of 14th byte of data |
| `searchRange` | In the range `0 to 31` | bit 0, 1, 2, 3 and 4 of 15th byte of data |
| `mv8x8Enable` | 0. `PV_OFF` 1. `PV_ON` | bit 0 of 16th byte of data |
| `useACPred` | 0. `PV_OFF` 1. `PV_ON` | bit 0 of 17th byte of data |
| `intraDCVlcTh` | In the range `0 to 7` | bit 0, 1 and 2 of 18th byte of data |

Following parameters are only for mpeg4_enc_fuzzer

| Parameter| Valid Values| Configured Value|
|------------- |-------------| ----- |
| `encWidth` | In the range `0 to 10239` | All the bits of 1st and 2nd byte of data |
| `encHeight` | In the range `0 to 10239` | All the bits of 3rd and 4th byte of data |
| `encMode` | 0. `H263_MODE` 1. `H263_MODE_WITH_ERR_RES` 2. `DATA_PARTITIONING_MODE` 3. `COMBINE_MODE_NO_ERR_RES` 4. `COMBINE_MODE_WITH_ERR_RES` | All the bits of 19th byte of data modulus 5 |

Following parameters are only for h263_enc_fuzzer

| Parameter| Valid Values| Configured Value|
|------------- |-------------| ----- |
| `encWidth` | 0. `128` 1. `176` 2. `352` 3. `704` 4. `1408` | All the bits of 1st byte of data modulus 5|
| `encHeight` | 0. `96` 1. `144` 2. `288` 3. `576` 4. `1152 ` | All the bits of 3rd byte of data modulus 5|
| `encMode` | 0. `SHORT_HEADER` 1. `SHORT_HEADER_WITH_ERR_RES` | All the bits of 19th byte of data modulus 2 |

This also ensures that the plugin is always deterministic for any given input.

##### Maximize utilization of input data
The plugin feeds the entire input data to the codec using a loop.
If the encode operation was successful, the input is advanced by the frame size.
If the encode operation was un-successful, the input is still advanced by frame size so
that the fuzzer can proceed to feed the next frame.

This ensures that the plugin tolerates any kind of input (empty, huge,
malformed, etc) and doesnt `exit()` on any input and thereby increasing the
chance of identifying vulnerabilities.

## Build

This describes steps to build mpeg4_enc_fuzzer and h263_enc_fuzzer binary.

### Android

#### Steps to build
Build the fuzzer
```
  $ mm -j$(nproc) mpeg4_enc_fuzzer
  $ mm -j$(nproc) h263_enc_fuzzer
```

#### Steps to run
Create a directory CORPUS_DIR and copy some yuv files to that folder
Push this directory to device.

To run on device
```
  $ adb sync data
  $ adb shell /data/fuzz/arm64/m4v_h263_enc_fuzzer/m4v_h263_enc_fuzzer CORPUS_DIR
  $ adb shell /data/fuzz/arm64/h263_enc_fuzzer/h263_enc_fuzzer CORPUS_DIR
```
To run on host
```
  $ $ANDROID_HOST_OUT/fuzz/x86_64/mpeg4_enc_fuzzer/mpeg4_enc_fuzzer CORPUS_DIR
  $ $ANDROID_HOST_OUT/fuzz/x86_64/h263_enc_fuzzer/h263_enc_fuzzer CORPUS_DIR
```

## References:
 * http://llvm.org/docs/LibFuzzer.html
 * https://github.com/google/oss-fuzz
+190 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <algorithm>
#include "mp4enc_api.h"

constexpr int8_t kIDRFrameRefreshIntervalInSec = 1;
constexpr MP4RateControlType krcType[] = {CONSTANT_Q, CBR_1, VBR_1, CBR_2, VBR_2, CBR_LOWDELAY};
#ifdef MPEG4
constexpr MP4EncodingMode kEncodingMode[] = {SHORT_HEADER, SHORT_HEADER_WITH_ERR_RES,
                                             DATA_PARTITIONING_MODE, COMBINE_MODE_NO_ERR_RES,
                                             COMBINE_MODE_WITH_ERR_RES};
constexpr size_t kMaxWidth = 10240;
constexpr size_t kMaxHeight = 10240;
#else
constexpr MP4EncodingMode kEncodingMode[] = {H263_MODE, H263_MODE_WITH_ERR_RES};
constexpr int kWidth[] = {128, 176, 352, 704, 1408};
constexpr int kHeight[] = {96, 144, 288, 576, 1152};
constexpr size_t kWidthNum = std::size(kWidth);
constexpr size_t kHeightNum = std::size(kHeight);
#endif

constexpr size_t krcTypeNum = std::size(krcType);
constexpr size_t kEncodingModeNum = std::size(kEncodingMode);
constexpr size_t kMaxQP = 51;

enum {
    IDX_WD_BYTE_1,
    IDX_WD_BYTE_2,
    IDX_HT_BYTE_1,
    IDX_HT_BYTE_2,
    IDX_FRAME_RATE,
    IDX_RC_TYPE,
    IDX_PACKET_SIZE,
    IDX_I_FRAME_QP,
    IDX_P_FRAME_QP,
    IDX_ENABLE_RVLC,
    IDX_QUANT_TYPE,
    IDX_NO_FRAME_SKIPPED_FLAG,
    IDX_ENABLE_SCENE_DETECT,
    IDX_NUM_INTRA_MB,
    IDX_SEARCH_RANGE,
    IDX_ENABLE_MV_8x8,
    IDX_USE_AC_PRED,
    IDX_INTRA_DC_VLC_THRESHOLD,
    IDX_ENC_MODE,
    IDX_LAST
};

class Codec {
   public:
    Codec() = default;
    ~Codec() { deInitEncoder(); }
    bool initEncoder(const uint8_t *data);
    void encodeFrames(const uint8_t *data, size_t size);
    void deInitEncoder();

   private:
    int32_t mFrameWidth = 352;
    int32_t mFrameHeight = 288;
    float mFrameRate = 25.0f;
    VideoEncOptions *mEncodeHandle = nullptr;
    VideoEncControls *mEncodeControl = nullptr;
};

bool Codec::initEncoder(const uint8_t *data) {
    mEncodeHandle = new VideoEncOptions;
    if (!mEncodeHandle) {
        return false;
    }
    memset(mEncodeHandle, 0, sizeof(VideoEncOptions));
    mEncodeControl = new VideoEncControls;
    if (!mEncodeControl) {
        return false;
    }
    memset(mEncodeControl, 0, sizeof(VideoEncControls));
    PVGetDefaultEncOption(mEncodeHandle, 0);

#ifdef MPEG4
    mFrameWidth = ((data[IDX_WD_BYTE_1] << 8) | data[IDX_WD_BYTE_2]) % kMaxWidth;
    mFrameHeight = ((data[IDX_HT_BYTE_1] << 8) | data[IDX_HT_BYTE_2]) % kMaxHeight;
#else
    mFrameWidth = kWidth[data[IDX_WD_BYTE_1] % kWidthNum];
    mFrameHeight = kHeight[data[IDX_HT_BYTE_1] % kHeightNum];
#endif
    mFrameRate = data[IDX_FRAME_RATE];
    mEncodeHandle->rcType = krcType[data[IDX_RC_TYPE] % krcTypeNum];
    mEncodeHandle->profile_level = CORE_PROFILE_LEVEL2;
    mEncodeHandle->packetSize = data[IDX_PACKET_SIZE];
    mEncodeHandle->iQuant[0] = (data[IDX_I_FRAME_QP] % kMaxQP) + 1;
    mEncodeHandle->pQuant[0] = (data[IDX_P_FRAME_QP] % kMaxQP) + 1;
    mEncodeHandle->rvlcEnable = (data[IDX_ENABLE_RVLC] & 0x01) ? PV_OFF : PV_ON;
    mEncodeHandle->quantType[0] = (data[IDX_QUANT_TYPE] & 0x01) ? 0 : 1;
    mEncodeHandle->noFrameSkipped = (data[IDX_NO_FRAME_SKIPPED_FLAG] & 0x01) ? PV_OFF : PV_ON;
    mEncodeHandle->sceneDetect = (data[IDX_ENABLE_SCENE_DETECT] & 0x01) ? PV_OFF : PV_ON;
    mEncodeHandle->numIntraMB = data[IDX_NUM_INTRA_MB] & 0x07;
    mEncodeHandle->searchRange = data[IDX_SEARCH_RANGE] & 0x1F;
    mEncodeHandle->mv8x8Enable = (data[IDX_ENABLE_MV_8x8] & 0x01) ? PV_OFF : PV_ON;
    mEncodeHandle->useACPred = (data[IDX_USE_AC_PRED] & 0x01) ? PV_OFF : PV_ON;
    mEncodeHandle->intraDCVlcTh = data[IDX_INTRA_DC_VLC_THRESHOLD] & 0x07;
    mEncodeHandle->encMode = kEncodingMode[data[IDX_ENC_MODE] % kEncodingModeNum];
    mEncodeHandle->encWidth[0] = mFrameWidth;
    mEncodeHandle->encHeight[0] = mFrameHeight;
    mEncodeHandle->encFrameRate[0] = mFrameRate;
    mEncodeHandle->tickPerSrc = mEncodeHandle->timeIncRes / mFrameRate;
    mEncodeHandle->intraPeriod = (kIDRFrameRefreshIntervalInSec * mFrameRate);
    if (!PVInitVideoEncoder(mEncodeControl, mEncodeHandle)) {
        return false;
    }
    return true;
}

void Codec::deInitEncoder() {
    if (mEncodeControl) {
        PVCleanUpVideoEncoder(mEncodeControl);
        delete mEncodeControl;
        mEncodeControl = nullptr;
    }
    if (mEncodeHandle) {
        delete mEncodeHandle;
        mEncodeHandle = nullptr;
    }
}

void Codec::encodeFrames(const uint8_t *data, size_t size) {
    size_t inputBufferSize = (mFrameWidth * mFrameHeight * 3) / 2;
    size_t outputBufferSize = inputBufferSize * 2;
    uint8_t outputBuffer[outputBufferSize];

    // Get VOL header.
    int32_t sizeOutputBuffer = outputBufferSize;
    PVGetVolHeader(mEncodeControl, outputBuffer, &sizeOutputBuffer, 0);

    size_t numFrame = 0;
    while (size > 0) {
        size_t bytesConsumed = std::min(size, inputBufferSize);
        uint8_t inputBuffer[inputBufferSize];
        memcpy(inputBuffer, data, bytesConsumed);
        if (bytesConsumed < sizeof(inputBuffer)) {
            memset(inputBuffer + bytesConsumed, data[0], sizeof(inputBuffer) - bytesConsumed);
        }
        VideoEncFrameIO videoIn{}, videoOut{};
        videoIn.height = mFrameHeight;
        videoIn.pitch = mFrameWidth;
        videoIn.timestamp = (numFrame * 1000) / mFrameRate;
        videoIn.yChan = inputBuffer;
        videoIn.uChan = videoIn.yChan + videoIn.height * videoIn.pitch;
        videoIn.vChan = videoIn.uChan + ((videoIn.height * videoIn.pitch) >> 2);
        uint32_t modTimeMs = 0;
        int32_t dataLength = outputBufferSize;
        int32_t nLayer = 0;
        PVEncodeVideoFrame(mEncodeControl, &videoIn, &videoOut, &modTimeMs, outputBuffer,
                           &dataLength, &nLayer);
        MP4HintTrack hintTrack;
        PVGetHintTrack(mEncodeControl, &hintTrack);
        PVGetOverrunBuffer(mEncodeControl);
        ++numFrame;
        data += bytesConsumed;
        size -= bytesConsumed;
    }
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    if (size < IDX_LAST) {
        return 0;
    }
    Codec *codec = new Codec();
    if (!codec) {
        return 0;
    }
    if (codec->initEncoder(data)) {
        data += IDX_LAST;
        size -= IDX_LAST;
        codec->encodeFrames(data, size);
    }
    delete codec;
    return 0;
}