Tests: USB: Check ep buffer is released by abort

Validate that endpoint buffer is not used after a transfer has been
aborted.
pull/9768/head
Filip Jagodzinski 2018-06-29 18:05:36 +02:00 committed by Russ Butler
parent 36d4c355a9
commit 1f8bf46f2e
4 changed files with 107 additions and 10 deletions

View File

@ -56,6 +56,7 @@ VENDOR_TEST_CTRL_OUT_STATUS_DELAY = 8
VENDOR_TEST_CTRL_IN_SIZES = 9 VENDOR_TEST_CTRL_IN_SIZES = 9
VENDOR_TEST_CTRL_OUT_SIZES = 10 VENDOR_TEST_CTRL_OUT_SIZES = 10
VENDOR_TEST_READ_START = 11 VENDOR_TEST_READ_START = 11
VENDOR_TEST_ABORT_BUFF_CHECK = 12
VENDOR_TEST_UNSUPPORTED_REQUEST = 32 VENDOR_TEST_UNSUPPORTED_REQUEST = 32
REQUEST_GET_STATUS = 0 REQUEST_GET_STATUS = 0
@ -1062,6 +1063,16 @@ def request_endpoint_read_start(dev, ep):
dev.ctrl_transfer(**ctrl_kwargs) dev.ctrl_transfer(**ctrl_kwargs)
def request_abort_buff_check(dev, ep):
ctrl_kwargs = {
'bmRequestType': build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_ENDPOINT),
'bRequest': VENDOR_TEST_ABORT_BUFF_CHECK,
'wValue': 0,
'wIndex': ep.bEndpointAddress,
'data_or_wLength': 1}
return bool(dev.ctrl_transfer(**ctrl_kwargs)[0])
USB_ERROR_FMT = str('Got {0!r} while testing endpoints ' USB_ERROR_FMT = str('Got {0!r} while testing endpoints '
'{1.bEndpointAddress:#04x}({1.wMaxPacketSize:02}) and ' '{1.bEndpointAddress:#04x}({1.wMaxPacketSize:02}) and '
'{2.bEndpointAddress:#04x}({2.wMaxPacketSize:02}) with ' '{2.bEndpointAddress:#04x}({2.wMaxPacketSize:02}) with '
@ -1304,9 +1315,16 @@ def ep_test_abort(dev, log, verbose=False):
Given a USB device with multiple OUT/IN endpoint pairs Given a USB device with multiple OUT/IN endpoint pairs
When a device aborts an in progress data transfer When a device aborts an in progress data transfer
Then no more data is transmitted Then no more data is transmitted
and endpoint buffer is correctly released on the device end
""" """
NUM_PACKETS_UNTIL_ABORT = 2 NUM_PACKETS_UNTIL_ABORT = 2
NUM_PACKETS_AFTER_ABORT = 8 NUM_PACKETS_AFTER_ABORT = 8
# If the host ever receives a payload with any byte set to this value,
# the device does not handle abort operation correctly. The buffer
# passed to aborted operation must not be used after call to abort().
FORBIDDEN_PAYLOAD_VALUE = NUM_PACKETS_AFTER_ABORT + 1
cfg = dev.get_active_configuration() cfg = dev.get_active_configuration()
for intf in cfg: for intf in cfg:
log('interface {}, alt {} -- '.format(intf.bInterfaceNumber, intf.bAlternateSetting), end='') log('interface {}, alt {} -- '.format(intf.bInterfaceNumber, intf.bAlternateSetting), end='')
@ -1336,6 +1354,11 @@ def ep_test_abort(dev, log, verbose=False):
payload_in.extend(packet) payload_in.extend(packet)
except usb.core.USBError as err: except usb.core.USBError as err:
break break
if FORBIDDEN_PAYLOAD_VALUE in payload_in:
raise_unconditionally(
lineno(), 'Endpoint buffer not released when aborting the '
'write operation on endpoint {0.bEndpointAddress:#04x}.'
.format(ep_in))
if verbose: if verbose:
log('The size of data successfully received from endpoint {0.bEndpointAddress:#04x}: {1} B.' log('The size of data successfully received from endpoint {0.bEndpointAddress:#04x}: {1} B.'
.format(ep_in, len(payload_in))) .format(ep_in, len(payload_in)))
@ -1353,13 +1376,24 @@ def ep_test_abort(dev, log, verbose=False):
log('Testing aborting an in progress transfer for OUT endpoints.') log('Testing aborting an in progress transfer for OUT endpoints.')
for ep_out in (bulk_out, interrupt_out): for ep_out in (bulk_out, interrupt_out):
payload_size = (NUM_PACKETS_UNTIL_ABORT + NUM_PACKETS_AFTER_ABORT) * ep_out.wMaxPacketSize payload_size = (NUM_PACKETS_UNTIL_ABORT + NUM_PACKETS_AFTER_ABORT) * ep_out.wMaxPacketSize
payload_out = array.array('B', (0x01 for _ in range(ep_out.wMaxPacketSize)))
num_bytes_written = 0 num_bytes_written = 0
while num_bytes_written < payload_size: while num_bytes_written < payload_size:
payload_out = array.array('B', (num_bytes_written/ep_out.wMaxPacketSize
for _ in range(ep_out.wMaxPacketSize)))
try: try:
num_bytes_written += ep_out.write(payload_out) num_bytes_written += ep_out.write(payload_out)
except usb.core.USBError: except usb.core.USBError:
break break
try:
ep_buff_correct = request_abort_buff_check(dev, ep_out)
except (usb.core.USBError, IndexError, TypeError) as err:
raise_unconditionally(
lineno(), 'Unable to verify endpoint buffer content ({!r}).'.format(err))
if not ep_buff_correct:
raise_unconditionally(
lineno(), 'Endpoint buffer not released when aborting the '
'read operation on endpoint {0.bEndpointAddress:#04x}.'
.format(ep_out))
if verbose: if verbose:
log('The size of data successfully sent to endpoint {0.bEndpointAddress:#04x}: {1} B.' log('The size of data successfully sent to endpoint {0.bEndpointAddress:#04x}: {1} B.'
.format(ep_out, num_bytes_written)) .format(ep_out, num_bytes_written))

View File

@ -24,12 +24,20 @@
#define NUM_PACKETS_UNTIL_ABORT 2 #define NUM_PACKETS_UNTIL_ABORT 2
#define NUM_PACKETS_AFTER_ABORT 8 #define NUM_PACKETS_AFTER_ABORT 8
#define EP_ABORT_BUFF_VALUE 0xff
#define VENDOR_TEST_CTRL_IN 1 /* If the host ever receives a payload with any byte set to this value,
#define VENDOR_TEST_CTRL_OUT 2 * the device does not handle abort operation correctly. The buffer
#define VENDOR_TEST_CTRL_IN_SIZES 9 * passed to aborted operation must not be used after call to abort().
#define VENDOR_TEST_CTRL_OUT_SIZES 10 */
#define VENDOR_TEST_READ_START 11 #define FORBIDDEN_PAYLOAD_VALUE (NUM_PACKETS_AFTER_ABORT + 1)
#define VENDOR_TEST_CTRL_IN 1
#define VENDOR_TEST_CTRL_OUT 2
#define VENDOR_TEST_CTRL_IN_SIZES 9
#define VENDOR_TEST_CTRL_OUT_SIZES 10
#define VENDOR_TEST_READ_START 11
#define VENDOR_TEST_ABORT_BUFF_CHECK 12
#define EVENT_READY (1 << 0) #define EVENT_READY (1 << 0)
@ -236,6 +244,12 @@ void USBEndpointTester::callback_request(const setup_packet_t *setup)
case VENDOR_TEST_READ_START: case VENDOR_TEST_READ_START:
result = (_request_read_start(setup)) ? Success : Failure; result = (_request_read_start(setup)) ? Success : Failure;
break; break;
case VENDOR_TEST_ABORT_BUFF_CHECK:
result = Send;
ctrl_buf[0] = _request_abort_buff_check(setup);
data = ctrl_buf;
size = 1;
break;
default: default:
result = PassThrough; result = PassThrough;
break; break;
@ -273,20 +287,50 @@ bool USBEndpointTester::_request_read_start(const setup_packet_t *setup)
if (setup->bmRequestType.Recipient != ENDPOINT_RECIPIENT) { if (setup->bmRequestType.Recipient != ENDPOINT_RECIPIENT) {
return false; return false;
} }
size_t ep_index = NUM_ENDPOINTS + 1; size_t ep_index = NUM_ENDPOINTS;
for (size_t i = 0; i < NUM_ENDPOINTS; i++) { for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
if (_endpoints[i] == setup->wIndex) { if (_endpoints[i] == setup->wIndex) {
ep_index = i; ep_index = i;
break; break;
} }
} }
if (ep_index > NUM_ENDPOINTS) { if (ep_index == NUM_ENDPOINTS) {
return false;
}
if (_endpoint_buffs[ep_index] == NULL) {
return false; return false;
} }
endpoint_abort(_endpoints[ep_index]); endpoint_abort(_endpoints[ep_index]);
return read_start(_endpoints[ep_index], _endpoint_buffs[ep_index], (*_endpoint_configs)[ep_index].max_packet); return read_start(_endpoints[ep_index], _endpoint_buffs[ep_index], (*_endpoint_configs)[ep_index].max_packet);
} }
bool USBEndpointTester::_request_abort_buff_check(const setup_packet_t *setup)
{
assert_locked();
if (setup->bmRequestType.Recipient != ENDPOINT_RECIPIENT) {
return false;
}
size_t ep_index = NUM_ENDPOINTS;
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
if (_endpoints[i] == setup->wIndex) {
ep_index = i;
break;
}
}
if (ep_index == NUM_ENDPOINTS) {
return false;
}
if (_endpoint_buffs[ep_index] == NULL) {
return false;
}
for (size_t i = 0; i < (*_endpoint_configs)[ep_index].max_packet; i++) {
if (_endpoint_buffs[ep_index][i] != EP_ABORT_BUFF_VALUE) {
return false;
}
}
return true;
}
void USBEndpointTester::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) void USBEndpointTester::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
{ {
if (aborted) { if (aborted) {
@ -309,6 +353,9 @@ void USBEndpointTester::callback_request_xfer_done(const setup_packet_t *setup,
case VENDOR_TEST_CTRL_IN_SIZES: case VENDOR_TEST_CTRL_IN_SIZES:
result = true; result = true;
break; break;
case VENDOR_TEST_ABORT_BUFF_CHECK:
result = true;
break;
default: default:
result = false; result = false;
break; break;
@ -723,7 +770,6 @@ void USBEndpointTester::_cb_bulk_out()
{ {
_cnt_cb_bulk_out++; _cnt_cb_bulk_out++;
uint32_t rx_size = read_finish(_endpoints[EP_BULK_OUT]); uint32_t rx_size = read_finish(_endpoints[EP_BULK_OUT]);
if (_abort_transfer_test == false) { if (_abort_transfer_test == false) {
// Send data back to host using the IN endpoint. // Send data back to host using the IN endpoint.
memset(_endpoint_buffs[EP_BULK_IN], 0, (*_endpoint_configs)[EP_BULK_IN].max_packet); memset(_endpoint_buffs[EP_BULK_IN], 0, (*_endpoint_configs)[EP_BULK_IN].max_packet);
@ -732,6 +778,10 @@ void USBEndpointTester::_cb_bulk_out()
} else { } else {
// Abort the transfer if enough data was received. // Abort the transfer if enough data was received.
_num_packets_bulk_out_abort++; _num_packets_bulk_out_abort++;
if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) {
// Set every byte of the buffer to a known value.
memset(_endpoint_buffs[EP_BULK_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_BULK_OUT].max_packet);
}
read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet); read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet);
if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) { if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_BULK_OUT]); endpoint_abort(_endpoints[EP_BULK_OUT]);
@ -743,7 +793,6 @@ void USBEndpointTester::_cb_bulk_in()
{ {
_cnt_cb_bulk_in++; _cnt_cb_bulk_in++;
write_finish(_endpoints[EP_BULK_IN]); write_finish(_endpoints[EP_BULK_IN]);
if (_abort_transfer_test == false) { if (_abort_transfer_test == false) {
// Receive more data from the host using the OUT endpoint. // Receive more data from the host using the OUT endpoint.
read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet); read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet);
@ -757,6 +806,10 @@ void USBEndpointTester::_cb_bulk_in()
write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], (*_endpoint_configs)[EP_BULK_IN].max_packet); write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], (*_endpoint_configs)[EP_BULK_IN].max_packet);
if (_num_packets_bulk_in_abort == NUM_PACKETS_UNTIL_ABORT) { if (_num_packets_bulk_in_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_BULK_IN]); endpoint_abort(_endpoints[EP_BULK_IN]);
// Verify that buffer given in write_start is not used after the
// call to endpoint_abort(), by changing the buffer contents.
// The test will fail if the host receives new buffer content.
memset(_endpoint_buffs[EP_BULK_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_BULK_IN].max_packet);
} }
} }
} }
@ -773,6 +826,10 @@ void USBEndpointTester::_cb_int_out()
} else { } else {
// Abort the transfer if enough data was received. // Abort the transfer if enough data was received.
_num_packets_int_out_abort++; _num_packets_int_out_abort++;
if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) {
// Set every byte of the buffer to a known value.
memset(_endpoint_buffs[EP_INT_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_INT_OUT].max_packet);
}
read_start(_endpoints[EP_INT_OUT], _endpoint_buffs[EP_INT_OUT], (*_endpoint_configs)[EP_INT_OUT].max_packet); read_start(_endpoints[EP_INT_OUT], _endpoint_buffs[EP_INT_OUT], (*_endpoint_configs)[EP_INT_OUT].max_packet);
if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) { if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_INT_OUT]); endpoint_abort(_endpoints[EP_INT_OUT]);
@ -797,6 +854,10 @@ void USBEndpointTester::_cb_int_in()
write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], (*_endpoint_configs)[EP_INT_IN].max_packet); write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], (*_endpoint_configs)[EP_INT_IN].max_packet);
if (_num_packets_int_in_abort == NUM_PACKETS_UNTIL_ABORT) { if (_num_packets_int_in_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_INT_IN]); endpoint_abort(_endpoints[EP_INT_IN]);
// Verify that buffer given in write_start is not used after the
// call to endpoint_abort(), by changing the buffer contents.
// The test will fail if the host receives new buffer content.
memset(_endpoint_buffs[EP_INT_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_INT_IN].max_packet);
} }
} }
} }

View File

@ -104,6 +104,7 @@ protected:
private: private:
const char *get_desc_string(const uint8_t *desc); const char *get_desc_string(const uint8_t *desc);
bool _request_read_start(const setup_packet_t *setup); bool _request_read_start(const setup_packet_t *setup);
bool _request_abort_buff_check(const setup_packet_t *setup);
}; };
#endif #endif

View File

@ -355,6 +355,7 @@ void ep_test_parallel_transfers_ctrl()
* Given a USB device with multiple OUT/IN endpoint pairs * Given a USB device with multiple OUT/IN endpoint pairs
* When a device aborts an in progress data transfer * When a device aborts an in progress data transfer
* Then no more data is transmitted * Then no more data is transmitted
* and endpoint buffer is correctly released on the device end
*/ */
void ep_test_abort() void ep_test_abort()
{ {