Prashant Malani | 519dd74 | 2018-03-15 22:01:50 -0700 | [diff] [blame] | 1 | // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | // |
| 5 | #include "midis/seq_handler.h" |
| 6 | |
| 7 | #include <string> |
| 8 | |
| 9 | #include <base/bind.h> |
Prashant Malani | e6e6b27 | 2018-05-02 10:16:30 -0700 | [diff] [blame] | 10 | #include <base/logging.h> |
Prashant Malani | 519dd74 | 2018-03-15 22:01:50 -0700 | [diff] [blame] | 11 | |
| 12 | #include "midis/device.h" |
| 13 | |
Prashant Malani | e6e6b27 | 2018-05-02 10:16:30 -0700 | [diff] [blame] | 14 | namespace { |
| 15 | |
| 16 | const int kFakeOutputPort = 0; |
| 17 | } |
| 18 | |
Prashant Malani | 519dd74 | 2018-03-15 22:01:50 -0700 | [diff] [blame] | 19 | namespace midis { |
| 20 | |
| 21 | // We don't have a real device whose callbacks we can run, so instead, |
| 22 | // we just create FakeCallbacks which contains stubs. |
| 23 | class FakeCallbacks { |
| 24 | public: |
| 25 | void AddDevice(std::unique_ptr<Device> device) {} |
| 26 | void RemoveDevice(uint32_t card_num, uint32_t device_num) {} |
| 27 | void HandleReceiveData(uint32_t card_id, |
| 28 | uint32_t device_id, |
| 29 | uint32_t port_id, |
| 30 | const char* buffer, |
| 31 | size_t buf_len) {} |
| 32 | bool IsDevicePresent(uint32_t card_num, uint32_t device_num) { |
| 33 | // Unused in the fuzzer, so doesn't matter. |
| 34 | return true; |
| 35 | } |
| 36 | bool IsPortPresent(uint32_t card_num, uint32_t device_num, uint32_t port_id) { |
| 37 | // Unused in the fuzzer, so doesn't matter. |
| 38 | return true; |
| 39 | } |
| 40 | }; |
| 41 | |
| 42 | // Running a fuzz test on the SeqHandler requires us to set certain |
| 43 | // private variables inside SeqHandler. To allow this to happen, |
| 44 | // we encapsulate the SeqHandler inside a FuzzerRunner class, |
| 45 | // and make FuzzerRunner a friend of SeqHandler. |
| 46 | class SeqHandlerFuzzer { |
| 47 | public: |
| 48 | void SetUpSeqHandler() { |
| 49 | seq_handler_ = std::make_unique<SeqHandler>( |
| 50 | base::Bind(&FakeCallbacks::AddDevice, base::Unretained(&callbacks_)), |
| 51 | base::Bind(&FakeCallbacks::RemoveDevice, base::Unretained(&callbacks_)), |
| 52 | base::Bind(&FakeCallbacks::HandleReceiveData, |
| 53 | base::Unretained(&callbacks_)), |
| 54 | base::Bind(&FakeCallbacks::IsDevicePresent, |
| 55 | base::Unretained(&callbacks_)), |
| 56 | base::Bind(&FakeCallbacks::IsPortPresent, |
| 57 | base::Unretained(&callbacks_))); |
| 58 | |
| 59 | seq_handler_->decoder_ = midis::SeqHandler::CreateMidiEvent(0); |
| 60 | } |
| 61 | |
Prashant Malani | e6e6b27 | 2018-05-02 10:16:30 -0700 | [diff] [blame] | 62 | bool SetUpOutputPort() { |
| 63 | snd_seq_t* tmp_seq = nullptr; |
| 64 | |
| 65 | int err = snd_seq_open(&tmp_seq, "hw", SND_SEQ_OPEN_OUTPUT, 0); |
| 66 | if (err != 0) { |
| 67 | LOG(ERROR) << "snd_seq_open fails: " << snd_strerror(err); |
| 68 | return false; |
| 69 | } |
| 70 | |
| 71 | SeqHandler::ScopedSeqPtr out_client(tmp_seq); |
| 72 | tmp_seq = nullptr; |
| 73 | seq_handler_->out_client_ = std::move(out_client); |
| 74 | seq_handler_->out_client_id_ = |
| 75 | snd_seq_client_id(seq_handler_->out_client_.get()); |
| 76 | return true; |
| 77 | } |
| 78 | |
Prashant Malani | 519dd74 | 2018-03-15 22:01:50 -0700 | [diff] [blame] | 79 | // Send arbitrary data to ProcessMidiEvent() and see what happens. |
| 80 | void ProcessMidiEvent(const uint8_t* data, size_t size) { |
| 81 | snd_seq_event_t event; |
| 82 | size_t bytes_to_copy = sizeof(snd_seq_event_t); |
| 83 | if (size < bytes_to_copy) { |
| 84 | bytes_to_copy = size; |
| 85 | } |
| 86 | memcpy(&event, data, bytes_to_copy); |
| 87 | seq_handler_->ProcessMidiEvent(&event); |
| 88 | } |
| 89 | |
Prashant Malani | e6e6b27 | 2018-05-02 10:16:30 -0700 | [diff] [blame] | 90 | void SendMidiData(const uint8_t* data, size_t size) { |
| 91 | // We don't have a real output port, so we just supply a value. |
| 92 | // This ALSA seq interface should fail gracefully. |
| 93 | seq_handler_->SendMidiData(kFakeOutputPort, data, size); |
| 94 | } |
| 95 | |
Prashant Malani | 519dd74 | 2018-03-15 22:01:50 -0700 | [diff] [blame] | 96 | private: |
| 97 | std::unique_ptr<SeqHandler> seq_handler_; |
| 98 | FakeCallbacks callbacks_; |
| 99 | }; |
| 100 | |
Prashant Malani | e6e6b27 | 2018-05-02 10:16:30 -0700 | [diff] [blame] | 101 | struct Environment { |
Tom Hughes | aebcdce | 2020-08-27 15:18:39 -0700 | [diff] [blame] | 102 | Environment() { logging::SetMinLogLevel(logging::LOG_ERROR); } |
Prashant Malani | e6e6b27 | 2018-05-02 10:16:30 -0700 | [diff] [blame] | 103 | }; |
| 104 | |
| 105 | Environment* env = new Environment(); |
| 106 | |
Prashant Malani | 519dd74 | 2018-03-15 22:01:50 -0700 | [diff] [blame] | 107 | } // namespace midis |
| 108 | |
| 109 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| 110 | midis::SeqHandlerFuzzer fuzzer; |
| 111 | fuzzer.SetUpSeqHandler(); |
Prashant Malani | e6e6b27 | 2018-05-02 10:16:30 -0700 | [diff] [blame] | 112 | if (!fuzzer.SetUpOutputPort()) { |
| 113 | abort(); |
| 114 | } |
Prashant Malani | 519dd74 | 2018-03-15 22:01:50 -0700 | [diff] [blame] | 115 | fuzzer.ProcessMidiEvent(data, size); |
Prashant Malani | e6e6b27 | 2018-05-02 10:16:30 -0700 | [diff] [blame] | 116 | fuzzer.SendMidiData(data, size); |
Prashant Malani | 519dd74 | 2018-03-15 22:01:50 -0700 | [diff] [blame] | 117 | return 0; |
| 118 | } |