From 23ec19dbb9046bd00655b1c065f2400bcc3cf8da Mon Sep 17 00:00:00 2001 From: asapersson Date: Tue, 6 Jun 2017 23:41:44 -0700 Subject: [PATCH] Add fuzzer for vp9 qp parser. Return false if ReadBits fails. Prevents GetQp from returning true with a qp of zero. BUG=webrtc:7662 Review-Url: https://codereview.webrtc.org/2911013002 Cr-Commit-Position: refs/heads/master@{#18462} --- .../utility/vp9_uncompressed_header_parser.cc | 252 ++++++++++-------- .../utility/vp9_uncompressed_header_parser.h | 32 --- webrtc/test/fuzzers/BUILD.gn | 10 + webrtc/test/fuzzers/vp9_qp_parser_fuzzer.cc | 18 ++ 4 files changed, 172 insertions(+), 140 deletions(-) create mode 100644 webrtc/test/fuzzers/vp9_qp_parser_fuzzer.cc diff --git a/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc b/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc index 571eaeaba4..40256d5847 100644 --- a/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc +++ b/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc @@ -9,53 +9,77 @@ */ #include "webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h" +#include "webrtc/base/bitbuffer.h" +#include "webrtc/base/logging.h" + namespace webrtc { +#define RETURN_FALSE_IF_ERROR(x) \ + if (!(x)) { \ + return false; \ + } + namespace vp9 { namespace { -const size_t kVp9MaxProfile = 4; const size_t kVp9NumRefsPerFrame = 3; const size_t kVp9MaxRefLFDeltas = 4; const size_t kVp9MaxModeLFDeltas = 2; -} // namespace -static uint8_t VP9ReadProfile(VP9BitReader* br) { - uint8_t profile = 0; - if (br->GetBit()) - profile |= 1; - if (br->GetBit()) - profile |= 2; - if (profile > 2 && br->GetBit()) - profile += 1; - return profile; +bool Vp9ReadProfile(rtc::BitBuffer* br, uint8_t* profile) { + uint32_t high_bit; + uint32_t low_bit; + RETURN_FALSE_IF_ERROR(br->ReadBits(&low_bit, 1)); + RETURN_FALSE_IF_ERROR(br->ReadBits(&high_bit, 1)); + *profile = (high_bit << 1) + low_bit; + if (*profile > 2) { + uint32_t reserved_bit; + RETURN_FALSE_IF_ERROR(br->ReadBits(&reserved_bit, 1)); + if (reserved_bit) { + LOG(LS_WARNING) << "Failed to get QP. Unsupported bitstream profile."; + return false; + } + } + return true; } -static bool VP9ReadColorConfig(VP9BitReader* br, uint8_t profile) { +bool Vp9ReadSyncCode(rtc::BitBuffer* br) { + uint32_t sync_code; + RETURN_FALSE_IF_ERROR(br->ReadBits(&sync_code, 24)); + if (sync_code != 0x498342) { + LOG(LS_WARNING) << "Failed to get QP. Invalid sync code."; + return false; + } + return true; +} + +bool Vp9ReadColorConfig(rtc::BitBuffer* br, uint8_t profile) { if (profile == 2 || profile == 3) { // Bitdepth. - br->GetBit(); + RETURN_FALSE_IF_ERROR(br->ConsumeBits(1)); } + uint32_t color_space; + RETURN_FALSE_IF_ERROR(br->ReadBits(&color_space, 3)); - uint8_t color_space = br->GetValue(3); // SRGB is 7. if (color_space != 7) { // YUV range flag. - br->GetBit(); + RETURN_FALSE_IF_ERROR(br->ConsumeBits(1)); if (profile == 1 || profile == 3) { - // Subsampling x. - br->GetBit(); - // Subsampling y. - br->GetBit(); - // Reserved. - if (br->GetBit()) { + // 1 bit: subsampling x. + // 1 bit: subsampling y. + RETURN_FALSE_IF_ERROR(br->ConsumeBits(2)); + uint32_t reserved_bit; + RETURN_FALSE_IF_ERROR(br->ReadBits(&reserved_bit, 1)); + if (reserved_bit) { LOG(LS_WARNING) << "Failed to get QP. Reserved bit set."; return false; } } } else { if (profile == 1 || profile == 3) { - // Reserved. - if (br->GetBit()) { + uint32_t reserved_bit; + RETURN_FALSE_IF_ERROR(br->ReadBits(&reserved_bit, 1)); + if (reserved_bit) { LOG(LS_WARNING) << "Failed to get QP. Reserved bit set."; return false; } @@ -69,164 +93,176 @@ static bool VP9ReadColorConfig(VP9BitReader* br, uint8_t profile) { return true; } -static void VP9ReadFrameSize(VP9BitReader* br) { - // Frame width. - br->GetValue(16); - // Frame height. - br->GetValue(16); +bool Vp9ReadFrameSize(rtc::BitBuffer* br) { + // 2 bytes: frame width. + // 2 bytes: frame height. + return br->ConsumeBytes(4); } -static void VP9ReadRenderSize(VP9BitReader* br) { - // Scaling. - if (br->GetBit()) { - // Render width. - br->GetValue(16); - // Render height. - br->GetValue(16); +bool Vp9ReadRenderSize(rtc::BitBuffer* br) { + uint32_t bit; + RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1)); + if (bit) { + // 2 bytes: render width. + // 2 bytes: render height. + RETURN_FALSE_IF_ERROR(br->ConsumeBytes(4)); } + return true; } -static void VP9ReadFrameSizeFromRefs(VP9BitReader* br) { - int found_ref = 0; +bool Vp9ReadFrameSizeFromRefs(rtc::BitBuffer* br) { + uint32_t found_ref = 0; for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { // Size in refs. - found_ref = br->GetBit(); + RETURN_FALSE_IF_ERROR(br->ReadBits(&found_ref, 1)); if (found_ref) break; } - if (!found_ref) - VP9ReadFrameSize(br); - - VP9ReadRenderSize(br); + if (!found_ref) { + if (!Vp9ReadFrameSize(br)) { + return false; + } + } + return Vp9ReadRenderSize(br); } -static void VP9ReadInterpolationFilter(VP9BitReader* br) { - if (br->GetBit()) - return; +bool Vp9ReadInterpolationFilter(rtc::BitBuffer* br) { + uint32_t bit; + RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1)); + if (bit) + return true; - br->GetValue(2); + return br->ConsumeBits(2); } -static void VP9ReadLoopfilter(VP9BitReader* br) { - // Filter level. - br->GetValue(6); - // Sharpness level. - br->GetValue(3); - uint32_t mode_ref_delta_enabled = br->GetBit(); +bool Vp9ReadLoopfilter(rtc::BitBuffer* br) { + // 6 bits: filter level. + // 3 bits: sharpness level. + RETURN_FALSE_IF_ERROR(br->ConsumeBits(9)); + + uint32_t mode_ref_delta_enabled; + RETURN_FALSE_IF_ERROR(br->ReadBits(&mode_ref_delta_enabled, 1)); if (mode_ref_delta_enabled) { - uint32_t mode_ref_delta_update = br->GetBit(); + uint32_t mode_ref_delta_update; + RETURN_FALSE_IF_ERROR(br->ReadBits(&mode_ref_delta_update, 1)); if (mode_ref_delta_update) { + uint32_t bit; for (size_t i = 0; i < kVp9MaxRefLFDeltas; i++) { - if (br->GetBit()) - br->GetSignedValue(6); + RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1)); + if (bit) { + RETURN_FALSE_IF_ERROR(br->ConsumeBits(7)); + } } for (size_t i = 0; i < kVp9MaxModeLFDeltas; i++) { - if (br->GetBit()) - br->GetSignedValue(6); + RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1)); + if (bit) { + RETURN_FALSE_IF_ERROR(br->ConsumeBits(7)); + } } } } + return true; } +} // namespace bool GetQp(const uint8_t* buf, size_t length, int* qp) { - VP9BitReader br(buf, length); + rtc::BitBuffer br(buf, length); // Frame marker. - if (br.GetValue(2) != 0x2) { + uint32_t frame_marker; + RETURN_FALSE_IF_ERROR(br.ReadBits(&frame_marker, 2)); + if (frame_marker != 0x2) { LOG(LS_WARNING) << "Failed to get QP. Frame marker should be 2."; return false; } // Profile. - uint8_t profile = VP9ReadProfile(&br); - if (profile > kVp9MaxProfile) { - LOG(LS_WARNING) << "Failed to get QP. Unsupported bitstream profile: " - << profile; + uint8_t profile; + if (!Vp9ReadProfile(&br, &profile)) return false; - } // Show existing frame. - if (br.GetBit()) + uint32_t show_existing_frame; + RETURN_FALSE_IF_ERROR(br.ReadBits(&show_existing_frame, 1)); + if (show_existing_frame) return false; // Frame type: KEY_FRAME(0), INTER_FRAME(1). - uint8_t frame_type = br.GetBit(); - // Show frame. - uint8_t show_frame = br.GetBit(); - // Error resilient. - uint8_t error_resilient = br.GetBit(); + uint32_t frame_type; + uint32_t show_frame; + uint32_t error_resilient; + RETURN_FALSE_IF_ERROR(br.ReadBits(&frame_type, 1)); + RETURN_FALSE_IF_ERROR(br.ReadBits(&show_frame, 1)); + RETURN_FALSE_IF_ERROR(br.ReadBits(&error_resilient, 1)); if (!frame_type) { - // Sync code. - uint32_t sync_code = br.GetValue(24); - if (sync_code != 0x498342) { - LOG(LS_WARNING) << "Failed to get QP. Invalid sync code."; + if (!Vp9ReadSyncCode(&br)) return false; - } - - if (!VP9ReadColorConfig(&br, profile)) + if (!Vp9ReadColorConfig(&br, profile)) + return false; + if (!Vp9ReadFrameSize(&br)) + return false; + if (!Vp9ReadRenderSize(&br)) return false; - VP9ReadFrameSize(&br); - VP9ReadRenderSize(&br); } else { - uint8_t intra_only = 0; + uint32_t intra_only = 0; if (!show_frame) - intra_only = br.GetBit(); - + RETURN_FALSE_IF_ERROR(br.ReadBits(&intra_only, 1)); if (!error_resilient) - // Reset frame context. - br.GetValue(2); + RETURN_FALSE_IF_ERROR(br.ConsumeBits(2)); // Reset frame context. if (intra_only) { - // Sync code. - if (br.GetValue(24) != 0x498342) { - LOG(LS_WARNING) << "Failed to get QP. Invalid sync code."; + if (!Vp9ReadSyncCode(&br)) return false; - } + if (profile > 0) { - if (!VP9ReadColorConfig(&br, profile)) + if (!Vp9ReadColorConfig(&br, profile)) return false; } // Refresh frame flags. - br.GetValue(8); - - VP9ReadFrameSize(&br); - VP9ReadRenderSize(&br); + RETURN_FALSE_IF_ERROR(br.ConsumeBits(8)); + if (!Vp9ReadFrameSize(&br)) + return false; + if (!Vp9ReadRenderSize(&br)) + return false; } else { // Refresh frame flags. - br.GetValue(8); + RETURN_FALSE_IF_ERROR(br.ConsumeBits(8)); for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { - // Ref frame index. - br.GetValue(3); - // Ref frame sign biases. - br.GetBit(); + // 3 bits: Ref frame index. + // 1 bit: Ref frame sign biases. + RETURN_FALSE_IF_ERROR(br.ConsumeBits(4)); } - VP9ReadFrameSizeFromRefs(&br); + if (!Vp9ReadFrameSizeFromRefs(&br)) + return false; + // Allow high precision mv. - br.GetBit(); + RETURN_FALSE_IF_ERROR(br.ConsumeBits(1)); // Interpolation filter. - VP9ReadInterpolationFilter(&br); + if (!Vp9ReadInterpolationFilter(&br)) + return false; } } if (!error_resilient) { - // Refresh frame context. - br.GetBit(); - // Frame parallel decoding mode. - br.GetBit(); + // 1 bit: Refresh frame context. + // 1 bit: Frame parallel decoding mode. + RETURN_FALSE_IF_ERROR(br.ConsumeBits(2)); } // Frame context index. - br.GetValue(2); + RETURN_FALSE_IF_ERROR(br.ConsumeBits(2)); - VP9ReadLoopfilter(&br); + if (!Vp9ReadLoopfilter(&br)) + return false; // Base QP. - const int base_q0 = br.GetValue(8); + uint8_t base_q0; + RETURN_FALSE_IF_ERROR(br.ReadUInt8(&base_q0)); *qp = base_q0; return true; } diff --git a/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h b/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h index ef6f268e51..f529a66ec2 100644 --- a/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h +++ b/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h @@ -14,42 +14,10 @@ #include #include -#include "webrtc/base/bitbuffer.h" -#include "webrtc/base/logging.h" - namespace webrtc { namespace vp9 { -class VP9BitReader : public ::rtc::BitBuffer { - public: - VP9BitReader(const uint8_t* buffer, size_t length_) - : BitBuffer(buffer, length_) {} - - uint32_t GetBit() { - uint32_t bit = 0; - if (ReadBits(&bit, 1)) - return bit; - - LOG(LS_WARNING) << "Failed to get bit. Reached EOF."; - return 0; - } - - uint32_t GetValue(int bits) { - uint32_t value = 0; - if (ReadBits(&value, bits)) - return value; - - LOG(LS_WARNING) << "Failed to get bit. Reached EOF."; - return 0; - } - - int32_t GetSignedValue(int bits) { - const int32_t value = static_cast(GetValue(bits)); - return GetBit() ? -value : value; - } -}; - // Gets the QP, QP range: [0, 255]. // Returns true on success, false otherwise. bool GetQp(const uint8_t* buf, size_t length, int* qp); diff --git a/webrtc/test/fuzzers/BUILD.gn b/webrtc/test/fuzzers/BUILD.gn index 6e5098e1fb..0590f1c6c6 100644 --- a/webrtc/test/fuzzers/BUILD.gn +++ b/webrtc/test/fuzzers/BUILD.gn @@ -70,6 +70,16 @@ webrtc_fuzzer_test("vp8_qp_parser_fuzzer") { ] } +webrtc_fuzzer_test("vp9_qp_parser_fuzzer") { + sources = [ + "vp9_qp_parser_fuzzer.cc", + ] + deps = [ + "../../modules/video_coding:video_coding_utility", + "../../modules/video_coding/", + ] +} + webrtc_fuzzer_test("h264_bitstream_parser_fuzzer") { sources = [ "h264_bitstream_parser_fuzzer.cc", diff --git a/webrtc/test/fuzzers/vp9_qp_parser_fuzzer.cc b/webrtc/test/fuzzers/vp9_qp_parser_fuzzer.cc new file mode 100644 index 0000000000..7dbed2e93d --- /dev/null +++ b/webrtc/test/fuzzers/vp9_qp_parser_fuzzer.cc @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h" + +namespace webrtc { +void FuzzOneInput(const uint8_t* data, size_t size) { + int qp; + vp9::GetQp(data, size, &qp); +} +} // namespace webrtc