blob: f14f10173bc926afd24eb78131a127620d9536ba [file] [log] [blame]
/*
* Copyright (C) 2021 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 <stddef.h>
#include <stdint.h>
#include <string.h>
#include "perfetto/ext/base/hash.h"
#include "perfetto/protozero/packed_repeated_fields.h"
#include "src/protozero/filtering/filter_bytecode_parser.h"
namespace protozero {
namespace {
// This function gives a little help to the fuzzer. The bytecode is really a
// sequence of varint-encoded uint32 words, with a FNV1a checksum at the end.
// It's very unlikely that the fuzzer on its own can work out the checksum, so
// most fuzzer inputs are doomed to fail the checksum verification.
// This takes the fuzzer input and builds a more plausible bytecode.
void LoadBytecodeWithChecksum(FilterBytecodeParser* parser,
const uint8_t* data,
size_t size) {
protozero::PackedVarInt words;
perfetto::base::Hasher hasher;
for (size_t i = 0; i < size; i += sizeof(uint32_t)) {
uint32_t word = 0;
memcpy(&word, data, sizeof(uint32_t));
words.Append(word);
hasher.Update(word);
}
words.Append(static_cast<uint32_t>(hasher.digest()));
parser->Load(words.data(), words.size());
}
int FuzzBytecodeParser(const uint8_t* data, size_t size) {
FilterBytecodeParser parser;
parser.set_suppress_logs_for_fuzzer(true);
if (size > 4 && data[0] < 192) {
// 75% of the times use the LoadBytecodeWithChecksum() which helps the
// fuzzer passing the checksum verification.
LoadBytecodeWithChecksum(&parser, data + 1, size - 1);
} else {
// In the remaining 25%, pass completely arbitrary inputs.
parser.Load(data, size);
}
// Smoke testing with known problematic values
for (uint32_t msg_index = 0; msg_index < 3; ++msg_index) {
parser.Query(msg_index, 0);
parser.Query(msg_index, 1);
parser.Query(msg_index, 127);
parser.Query(msg_index, 128);
parser.Query(msg_index, 129);
parser.Query(msg_index, 65536);
parser.Query(msg_index, 65536);
parser.Query(msg_index, 1u << 28);
parser.Query(msg_index, 1u << 31);
}
// Query using the random data at the end of the random buffer.
if (size > 8) {
uint32_t msg_index = 0;
uint32_t field_id = 0;
memcpy(&msg_index, &data[size - 8], 4);
memcpy(&field_id, &data[size - 4], 4);
parser.Query(msg_index, field_id);
}
return 0;
}
} // namespace
} // namespace protozero
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return protozero::FuzzBytecodeParser(data, size);
}