From faf5e2df0feff2ece06c8efca5c18683e26e9d0b Mon Sep 17 00:00:00 2001 From: Saxon Date: Mon, 23 Sep 2019 12:18:14 +0930 Subject: [PATCH] codec/h264/h264dec/fuzz: added fuzzer test for parseLevelPrefix function The fuzz package has been created, which will house C based helper code and sub-packages dedicated to each fuzz test. A sub packaged called fuzzParseLevelPrefix has been created to house the fuzz test for parseLevelPrefix. Emeric's C code has been isolated into some C files, and a file called fuzz.go has been created, which houses the actual fuzz test function. An initial corpus has been added with 3 inputs. --- codec/h264/h264dec/cavlc_fuzz.go | 32 +++++++++ .../fuzz/fuzzParseLevelPrefix/corpus/test1 | 1 + .../fuzz/fuzzParseLevelPrefix/corpus/test2 | 1 + .../fuzz/fuzzParseLevelPrefix/corpus/test3 | 1 + .../h264dec/fuzz/fuzzParseLevelPrefix/fuzz.go | 69 ++++++++++++++++++ .../fuzzParseLevelPrefix/parse_level_prefix.c | 59 +++++++++++++++ .../fuzzParseLevelPrefix/parse_level_prefix.h | 11 +++ codec/h264/h264dec/fuzz/helpers.c | 72 +++++++++++++++++++ codec/h264/h264dec/fuzz/helpers.h | 59 +++++++++++++++ 9 files changed, 305 insertions(+) create mode 100644 codec/h264/h264dec/cavlc_fuzz.go create mode 100644 codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test1 create mode 100644 codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test2 create mode 100644 codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test3 create mode 100644 codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/fuzz.go create mode 100644 codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/parse_level_prefix.c create mode 100644 codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/parse_level_prefix.h create mode 100644 codec/h264/h264dec/fuzz/helpers.c create mode 100644 codec/h264/h264dec/fuzz/helpers.h diff --git a/codec/h264/h264dec/cavlc_fuzz.go b/codec/h264/h264dec/cavlc_fuzz.go new file mode 100644 index 00000000..d8d4bacf --- /dev/null +++ b/codec/h264/h264dec/cavlc_fuzz.go @@ -0,0 +1,32 @@ +/* +DESCRIPTION + cavlc_fuzz.go provides exported wrappers for unexported CAVLC functions such + that the fuzz package can access these functions for testing. + +AUTHORS + Saxon A. Nelson-Milton + +LICENSE + Copyright (C) 2019 the Australian Ocean Lab (AusOcean). + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ + +package h264dec + +import "bitbucket.org/ausocean/av/codec/h264/h264dec/bits" + +func FuzzParseLevelPrefix(br *bits.BitReader) (int, error) { + return parseLevelPrefix(br) +} diff --git a/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test1 b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test1 new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test1 @@ -0,0 +1 @@ +1 diff --git a/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test2 b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test2 new file mode 100644 index 00000000..8a0f05e1 --- /dev/null +++ b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test2 @@ -0,0 +1 @@ +01 diff --git a/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test3 b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test3 new file mode 100644 index 00000000..5325a8df --- /dev/null +++ b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/corpus/test3 @@ -0,0 +1 @@ +001 diff --git a/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/fuzz.go b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/fuzz.go new file mode 100644 index 00000000..3910ac58 --- /dev/null +++ b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/fuzz.go @@ -0,0 +1,69 @@ +/* +DESCRIPTION + fuzz.go provides a function with the required signature such that it may be + accessed by go-fuzz. The function will compare the output of C level_prefix + parser with a go version. + +AUTHORS + Saxon A. Nelson-Milton + +LICENSE + Copyright (C) 2019 the Australian Ocean Lab (AusOcean). + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ + +// +build gofuzz + +package fuzzParseLevelPrefix + +/* +#include "../helpers.c" +#include "parse_level_prefix.h" +*/ +import "C" +import ( + "bytes" + "fmt" + "unsafe" + + "bitbucket.org/ausocean/av/codec/h264/h264dec" + "bitbucket.org/ausocean/av/codec/h264/h264dec/bits" +) + +func Fuzz(data []byte) int { + // Create C based BitReader based on data. + cbr := C.new_BitReader((*C.char)(unsafe.Pointer(&data[0])), C.int(len(data))) + + // Get the level_prefix from the C code. If got is < 0, then the C code + // doesn't like it and we shouldn't have that input in the corpus, so return -1. + got := C.read_levelprefix(cbr) + if got < 0 { + return -1 + } + + // Get the level_prefix from the go code. If the C code was okay with this + // input, but the go code isn't then that's bad, so panic - want crasher info. + want, err := h264dec.FuzzParseLevelPrefix(bits.NewBitReader(bytes.NewReader(data))) + if err != nil { + panic(fmt.Sprintf("error from go parseLevelPrefix: %v", err)) + } + + // Compare the result of the C and go code. If they are not the same then + // panic - our go code is not doing what it should. + if int(want) != int(got) { + panic(fmt.Sprintf("did not get expected results for input: %v\nGot: %v\nWant: %v\n", data, got, want)) + } + return 1 +} diff --git a/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/parse_level_prefix.c b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/parse_level_prefix.c new file mode 100644 index 00000000..9c517554 --- /dev/null +++ b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/parse_level_prefix.c @@ -0,0 +1,59 @@ +/* +DESCRIPTION + parse_level_prefix.c contains a function that will parse the level_prefix + when performaing CAVLC decoding; extracted from Emeric Grange's h264 decoder + contained in MiniVideo (https://github.com/emericg/MiniVideo). This is used + to generate input and output data for testing purposes. + +AUTHORS + Emeric Grange + Saxon A. Nelson-Milton + +LICENSE + COPYRIGHT (C) 2018 Emeric Grange - All Rights Reserved + + This file is part of MiniVideo. + + MiniVideo is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + MiniVideo is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with MiniVideo. If not, see . +*/ + +#include "parse_level_prefix.h" +#include "../helpers.h" + +/*! + * \brief Parsing process for level_prefix. + * \param *dc The current DecodingContext. + * \return leadingZeroBits. + * + * From 'ITU-T H.264' recommendation: + * 9.2.2.1 Parsing process for level_prefix + * + * The parsing process for this syntax element consists in reading the bits + * starting at the current location in the bitstream up to and including the + * first non-zero bit, and counting the number of leading bits that are equal to 0. + * + * level_prefix and level_suffix specify the value of a non-zero transform coefficient level. + * The range of level_prefix and level_suffix is specified in subclause 9.2.2. + */ +int read_levelprefix(struct BitReader *br){ + int leadingZeroBits = -1; + int b = 0; + for (b = 0; !b; leadingZeroBits++){ + b = read_bits(br,1); + if( br->err != 0 ){ + return -1; + } + } + return leadingZeroBits; +} diff --git a/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/parse_level_prefix.h b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/parse_level_prefix.h new file mode 100644 index 00000000..e4bcc7f2 --- /dev/null +++ b/codec/h264/h264dec/fuzz/fuzzParseLevelPrefix/parse_level_prefix.h @@ -0,0 +1,11 @@ +#ifndef PARSE_LEVEL_PREFIX_H +#define PARSE_LEVEL_PREFIX_H + +#include +#include + +#include "../helpers.h" + +int read_levelprefix(struct BitReader *br); + +#endif // PARSE_LEVEL_PREFIX_H diff --git a/codec/h264/h264dec/fuzz/helpers.c b/codec/h264/h264dec/fuzz/helpers.c new file mode 100644 index 00000000..dd9a107a --- /dev/null +++ b/codec/h264/h264dec/fuzz/helpers.c @@ -0,0 +1,72 @@ +/* +DESCRIPTION + helpers.c provides C helper functions for interfacing with C code used for + testing in this package. + +AUTHORS + Saxon A. Nelson-Milton + +LICENSE + Copyright (C) 2019 the Australian Ocean Lab (AusOcean). + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ + +#include "helpers.h" + +typedef struct BitReader BitReader; +typedef struct Reader Reader; + +BitReader* new_BitReader(char* d, int l){ + Reader* r = (Reader*)malloc(sizeof(Reader)); + r->data = d; + r->curr = 0; + r->len = l; + r->err = 0; + + BitReader* br = (BitReader*)malloc(sizeof(BitReader)); + br->r = r; + br->n = 0; + br->bits = 0; + br->nRead = 0; + br->err = 0; + return br; +} + +char next_byte(Reader *r) { + if(r->curr >= r->len){ + r->err = 1; + } + char next = r->data[r->curr]; + r->curr++; + return next; +} + +uint64_t read_bits(BitReader *br, int n) { + while( n > br->bits ){ + char b = next_byte(br->r); + if(br->r->err != 0){ + br->err = 1; + return 0; + } + br->nRead++; + br->n <<= 8; + br->n |= (uint64_t)b; + br->bits += 8; + } + + uint64_t r = (br->n >> (br->bits-n)) & ((1 << n) - 1); + br->bits -= n; + return r; +} diff --git a/codec/h264/h264dec/fuzz/helpers.h b/codec/h264/h264dec/fuzz/helpers.h new file mode 100644 index 00000000..4f9e2c01 --- /dev/null +++ b/codec/h264/h264dec/fuzz/helpers.h @@ -0,0 +1,59 @@ +/* +DESCRIPTION + helpers.h defines some structs and function signatures that will help to + interface with C code used in our fuzz testing. + +AUTHORS + Saxon A. Nelson-Milton + +LICENSE + Copyright (C) 2019 the Australian Ocean Lab (AusOcean). + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ + +#ifndef HELPERS_H +#define HELPERS_H + +#include +#include + +struct Reader { + char* data; + int curr; + int len; + int err; +}; + +struct BitReader { + struct Reader* r; + uint64_t n; + int bits; + int nRead; + int err; +}; + +// new_BitReader returns a new instance of a BitReader given the backing array +// d and the length of the data l. +struct BitReader* new_BitReader(char*, int); + +// next_byte provides the next byte from a Reader r and advances it's byte index. +char next_byte(struct Reader*); + +// read_bits intends to emulate the BitReader.ReadBits function defined in the +// bits package. This is used when a bit reader is required to obtain bytes from +// a stream in the C test code. +uint64_t read_bits(struct BitReader*, int); + +#endif // HELPERS_H