blob: 56fb96b97401d2d5d53558f320ca3841aca4c395 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "midis/seq_handler.h"
#include <string>
#include <base/bind.h>
#include <base/logging.h>
#include "midis/device.h"
namespace {
const int kFakeOutputPort = 0;
}
namespace midis {
// We don't have a real device whose callbacks we can run, so instead,
// we just create FakeCallbacks which contains stubs.
class FakeCallbacks {
public:
void AddDevice(std::unique_ptr<Device> device) {}
void RemoveDevice(uint32_t card_num, uint32_t device_num) {}
void HandleReceiveData(uint32_t card_id,
uint32_t device_id,
uint32_t port_id,
const char* buffer,
size_t buf_len) {}
bool IsDevicePresent(uint32_t card_num, uint32_t device_num) {
// Unused in the fuzzer, so doesn't matter.
return true;
}
bool IsPortPresent(uint32_t card_num, uint32_t device_num, uint32_t port_id) {
// Unused in the fuzzer, so doesn't matter.
return true;
}
};
// Running a fuzz test on the SeqHandler requires us to set certain
// private variables inside SeqHandler. To allow this to happen,
// we encapsulate the SeqHandler inside a FuzzerRunner class,
// and make FuzzerRunner a friend of SeqHandler.
class SeqHandlerFuzzer {
public:
void SetUpSeqHandler() {
seq_handler_ = std::make_unique<SeqHandler>(
base::Bind(&FakeCallbacks::AddDevice, base::Unretained(&callbacks_)),
base::Bind(&FakeCallbacks::RemoveDevice, base::Unretained(&callbacks_)),
base::Bind(&FakeCallbacks::HandleReceiveData,
base::Unretained(&callbacks_)),
base::Bind(&FakeCallbacks::IsDevicePresent,
base::Unretained(&callbacks_)),
base::Bind(&FakeCallbacks::IsPortPresent,
base::Unretained(&callbacks_)));
seq_handler_->decoder_ = midis::SeqHandler::CreateMidiEvent(0);
}
bool SetUpOutputPort() {
snd_seq_t* tmp_seq = nullptr;
int err = snd_seq_open(&tmp_seq, "hw", SND_SEQ_OPEN_OUTPUT, 0);
if (err != 0) {
LOG(ERROR) << "snd_seq_open fails: " << snd_strerror(err);
return false;
}
SeqHandler::ScopedSeqPtr out_client(tmp_seq);
tmp_seq = nullptr;
seq_handler_->out_client_ = std::move(out_client);
seq_handler_->out_client_id_ =
snd_seq_client_id(seq_handler_->out_client_.get());
return true;
}
// Send arbitrary data to ProcessMidiEvent() and see what happens.
void ProcessMidiEvent(const uint8_t* data, size_t size) {
snd_seq_event_t event;
size_t bytes_to_copy = sizeof(snd_seq_event_t);
if (size < bytes_to_copy) {
bytes_to_copy = size;
}
memcpy(&event, data, bytes_to_copy);
seq_handler_->ProcessMidiEvent(&event);
}
void SendMidiData(const uint8_t* data, size_t size) {
// We don't have a real output port, so we just supply a value.
// This ALSA seq interface should fail gracefully.
seq_handler_->SendMidiData(kFakeOutputPort, data, size);
}
private:
std::unique_ptr<SeqHandler> seq_handler_;
FakeCallbacks callbacks_;
};
struct Environment {
Environment() { logging::SetMinLogLevel(logging::LOGGING_ERROR); }
};
Environment* env = new Environment();
} // namespace midis
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
midis::SeqHandlerFuzzer fuzzer;
fuzzer.SetUpSeqHandler();
if (!fuzzer.SetUpOutputPort()) {
abort();
}
fuzzer.ProcessMidiEvent(data, size);
fuzzer.SendMidiData(data, size);
return 0;
}