NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/56109: SCSI getconfiguration requests have size limit on USB3 only but do not return errors



>Number:         56109
>Category:       kern
>Synopsis:       SCSI getconfiguration requests have size limit on USB3 only but do not return errors
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Apr 16 12:45:00 +0000 2021
>Originator:     Reinoud Zandijk
>Release:        NetBSD 9.99.81
>Organization:
NetBSD
	
>Environment:
	
	
System: NetBSD gorilla.13thmonkey.org 9.99.81 NetBSD 9.99.81 (GENERIC) #1: Fri Apr 16 09:17:54 CEST 2021 reinoud%gorilla.13thmonkey.org@localhost:/usr/tmp/obj/sys/arch/amd64/compile/GENERIC amd64
Architecture: x86_64
Machine: amd64
Sources from Apr 15th.

>Description:

Non bulk read/write SCSI data read calls are size limited on their return
buffer length and are not transfered correctly from a device over USB3 or on
USB2 when an USB3 hubs is in between if bigger than 0x200. This size limit is
not on USB2 only machines nor on ATAPI/SATA connected drives.

The problem manifested itself with the GET_CONFIGURATION MMC call for
CD/DVD/BD players/recorders that transfers data about settings and
capabilities of the drive in chunks. On USB2, the maximum size of 0xffff can
be specified. On USB3 its failing silently; when transfering more than 0x200
bytes at a time, the SCSI call does NOT return an error but the memory block
is not filled in either; it is left untouched effectively returning garbage
when the block is not cleared in advance.

When specifying a larger buffer than the drive has to offer, USB2 seems to
fill it in upto 0x200 but I haven't seen a drive giving more than 0x140 in
practice so I am not sure what would happen if it is bigger; it might just
truncate it to 0x200. ATAPI/SATA drives will normally transfer for the size
they have.

USB3 will transfer upto 0x200 fine but on bigger requests returns NO error and
is not touching the buffer at all.

	
>How-To-Repeat:
Plug in an USB CD/DVD recorder drive in an USB3 port and run the following
program and see that for requests bigger than 0x200 it shows this behavior but
when plugged in an USB2 only machine this size limit at least transfers upto
0x200.

--------------------------
/*
 * Test program to show issue with CD/DVD/BD player/recorder connected over
 * USB3 where connected over USB2 works as expected.
 *
 * Transfers with databuf_len > 0x200 fail on USB3 where they succeed on USB2.
 * Note it does NOT return an error and the memory is left untouched.
 */

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/scsiio.h>

#define SCSI_CMD_LEN  12
typedef unsigned char scsicmd[SCSI_CMD_LEN];

int
main(int argc, char **argv) {
	scsireq_t req;
	scsicmd	  cmd;
	uint8_t  *databuf;
	int fd;
	int databuf_len;

	if (argc != 3) {
		fprintf(stderr, "usage: scsi_issue drive size\n");
		return 1;
	}

	fd = open(argv[1], O_RDWR, 0);
	if (fd < 0) {
		fprintf(stderr, "can't open device %s : %s\n", argv[1], strerror(errno));
		return ENODEV;
	}

	databuf_len = atoi(argv[2]);
	if (databuf_len <= 0) {
		fprintf(stderr, "invalid size\n");
		return 1;
	}

	databuf = malloc(databuf_len);
	memset(databuf, 0xff, databuf_len);

	bzero(cmd, SCSI_CMD_LEN);
	cmd[0] = 0x46;			// get configuration
	cmd[1] = 0;			// all independent of current setting
	cmd[2] = 0;			// MSB last feature
	cmd[3] = 0;			// LSB last feature
	cmd[7] = databuf_len >> 8;	// MSB buffersize
	cmd[8] = databuf_len & 0xff;	// LSB buffersize
	cmd[9] = 0;			// control

	memset(&req, 0, sizeof(req));
	req.cmdlen   = 10;
	memcpy(req.cmd, cmd, req.cmdlen);
	req.databuf  = databuf;
	req.datalen  = databuf_len;
	req.timeout  = 30000;
	req.flags    = SCCMD_READ;
	req.senselen = 0;

	if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
		fprintf(stderr, "SCSI command failed : %s\n", strerror(errno));
		exit(1);
	}

	for (int i = 0; i < databuf_len; i++) {
		printf("%02x ", databuf[i]);
		if (i % 16 == 15) printf("\n");
	}
	printf("\n");
	return 0;
}
--------------------------

	
>Fix:
Adapt programs to use buffers less than 0x200 for each transfer.
	

>Unformatted:
 	
 	


Home | Main Index | Thread Index | Old Index