| #include "fuzz/fuzz_private.h" | |
| #include "tusb.h" | |
| #include <cassert> | |
| #include <array> | |
| #include <limits> | |
| #if CFG_TUD_MSC==1 | |
| // Whether host does safe eject. | |
| // tud_msc_get_maxlun_cb returns a uint8_t so the max logical units that are | |
| // allowed is 255, so we need to keep track of 255 fuzzed logical units. | |
| static std::array<bool, std::numeric_limits<uint8_t>::max()> ejected = {false}; | |
| extern "C" { | |
| // Invoked when received SCSI_CMD_INQUIRY | |
| // Application fill vendor id, product id and revision with string up to 8, 16, | |
| // 4 characters respectively | |
| void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], | |
| uint8_t product_id[16], uint8_t product_rev[4]) { | |
| (void)lun; | |
| assert(_fuzz_data_provider.has_value()); | |
| std::string vid = _fuzz_data_provider->ConsumeBytesAsString(8); | |
| std::string pid = _fuzz_data_provider->ConsumeBytesAsString(16); | |
| std::string rev = _fuzz_data_provider->ConsumeBytesAsString(4); | |
| memcpy(vendor_id, vid.c_str(), strlen(vid.c_str())); | |
| memcpy(product_id, pid.c_str(), strlen(pid.c_str())); | |
| memcpy(product_rev, rev.c_str(), strlen(rev.c_str())); | |
| } | |
| // Invoked when received Test Unit Ready command. | |
| // return true allowing host to read/write this LUN e.g SD card inserted | |
| bool tud_msc_test_unit_ready_cb(uint8_t lun) { | |
| // RAM disk is ready until ejected | |
| if (ejected[lun]) { | |
| // Additional Sense 3A-00 is NOT_FOUND | |
| tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); | |
| return false; | |
| } | |
| return _fuzz_data_provider->ConsumeBool(); | |
| } | |
| // Invoked when received SCSI_CMD_READ_CAPACITY_10 and | |
| // SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size Application update | |
| // block count and block size | |
| void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, | |
| uint16_t *block_size) { | |
| (void)lun; | |
| *block_count = _fuzz_data_provider->ConsumeIntegral<uint32_t>(); | |
| *block_size = _fuzz_data_provider->ConsumeIntegral<uint16_t>(); | |
| } | |
| // Invoked when received Start Stop Unit command | |
| // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage | |
| // - Start = 1 : active mode, if load_eject = 1 : load disk storage | |
| bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, | |
| bool load_eject) { | |
| (void)power_condition; | |
| assert(_fuzz_data_provider.has_value()); | |
| if (load_eject) { | |
| if (start) { | |
| // load disk storage | |
| } else { | |
| // unload disk storage | |
| ejected[lun] = true; | |
| } | |
| } | |
| return _fuzz_data_provider->ConsumeBool(); | |
| } | |
| // Callback invoked when received READ10 command. | |
| // Copy disk's data to buffer (up to bufsize) and return number of copied bytes. | |
| int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, | |
| void *buffer, uint32_t bufsize) { | |
| assert(_fuzz_data_provider.has_value()); | |
| (void)lun; | |
| (void)lba; | |
| (void)offset; | |
| std::vector<uint8_t> consumed_buffer = _fuzz_data_provider->ConsumeBytes<uint8_t>( | |
| _fuzz_data_provider->ConsumeIntegralInRange<uint32_t>(0, bufsize)); | |
| memcpy(buffer, consumed_buffer.data(), consumed_buffer.size()); | |
| // Sometimes return an error code; | |
| if (_fuzz_data_provider->ConsumeBool()) { | |
| return _fuzz_data_provider->ConsumeIntegralInRange( | |
| std::numeric_limits<int32_t>::min(), -1); | |
| } | |
| return consumed_buffer.size(); | |
| } | |
| bool tud_msc_is_writable_cb(uint8_t lun) { | |
| assert(_fuzz_data_provider.has_value()); | |
| (void)lun; | |
| return _fuzz_data_provider->ConsumeBool(); | |
| } | |
| // Callback invoked when received WRITE10 command. | |
| // Process data in buffer to disk's storage and return number of written bytes | |
| int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, | |
| uint8_t *buffer, uint32_t bufsize) { | |
| // Ignore these as they are outputs and don't affect the return value. | |
| (void)lun; | |
| (void)lba; | |
| (void)offset; | |
| (void)buffer; | |
| assert(_fuzz_data_provider.has_value()); | |
| // -ve error codes -> bufsize. | |
| return _fuzz_data_provider->ConsumeIntegralInRange<int32_t>( | |
| std::numeric_limits<int32_t>::min(), bufsize); | |
| } | |
| // Callback invoked when received an SCSI command not in built-in list below | |
| // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE | |
| // - READ10 and WRITE10 has their own callbacks | |
| int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, | |
| uint16_t bufsize) { | |
| (void)buffer; | |
| (void)bufsize; | |
| assert(_fuzz_data_provider.has_value()); | |
| switch (scsi_cmd[0]) { | |
| case SCSI_CMD_TEST_UNIT_READY: | |
| break; | |
| case SCSI_CMD_INQUIRY: | |
| break; | |
| case SCSI_CMD_MODE_SELECT_6: | |
| break; | |
| case SCSI_CMD_MODE_SENSE_6: | |
| break; | |
| case SCSI_CMD_START_STOP_UNIT: | |
| break; | |
| case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: | |
| break; | |
| case SCSI_CMD_READ_CAPACITY_10: | |
| break; | |
| case SCSI_CMD_REQUEST_SENSE: | |
| break; | |
| case SCSI_CMD_READ_FORMAT_CAPACITY: | |
| break; | |
| case SCSI_CMD_READ_10: | |
| break; | |
| case SCSI_CMD_WRITE_10: | |
| break; | |
| default: | |
| // Set Sense = Invalid Command Operation | |
| tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); | |
| return _fuzz_data_provider->ConsumeIntegralInRange<int32_t>( | |
| std::numeric_limits<int32_t>::min(), -1); | |
| } | |
| return 0; | |
| } | |
| } | |
| #endif |