| #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 |