Subject: Bug#3516: SCSI_IOCTL_SEND_COMMAND can cause major corruption

From: Ian Jackson <[email-masked]>
Date: Thu, 4 Jul 96 01:33 BST

Package: kernel
Version: 2.0.0

I ran the program below with the arguments 5000 170 10.
(/dev/cdrom is my SCSI CD-ROM device).  It is supposed to read digital
audio from the CD-ROM drive and save it to a file; the 10 is the
request size.  Usually I run it with a request size of 1.

This time I ran it with a request size of 10, and it appeared to lock.
I interrupted it and ran it again and it coredumped.  I tried to
remove the core and rm coredumped.  I tried to do something else (I
forget what) and got a coredump from the copy of bash that had been
forked to do whatever it was, with a bizarre message about
relocations being all screwed up or something.

I conjecture that my in-memory copy of some important thing like the
libc had been badly damaged, and decided to hit the reset switch
rather than letting any more garbage be written to the disk.

I have a file that looks like one of the corefiles generated during
this incident.  Let me know if you want it; it's 450K, or 55K
compressed.

I don't intend to try doing the same thing again to see whether the
problem is repeatable.  Unfortunately it will be hard to reproduce
unless you have an NEC SCSI-2 CD-ROM drive, or some other drive with
the same commands for reading digital audio.  However, you can
probably generate the problem by issuing perfectly ordinary READ
commands to a disk.

My SCSI controller is an NCR53c810:
 scsi-ncr53c7,8xx : at PCI bus 0, device 11,  function 0
 scsi-ncr53c7,8xx : NCR53c810 at memory 0xfbff0000, io 0xe800, irq 10
 scsi0 : burst length 8
 scsi0 : NCR code relocated to 0x1c600 (virt 0x0001c600)
 scsi0 : test 1 started
 scsi0 : NCR53c{7,8}xx (rel 17)
 scsi : 1 host.
...
 scsi0 : target 5 accepting period 200ns offset 8 5.00MHz synchronous SCSI
 scsi0 : setting target 5 to period 200ns offset 8 5.00MHz synchronous SCSI
   Vendor: NEC       Model: CD-ROM DRIVE:211  Rev: 1.0 
   Type:   CD-ROM                             ANSI SCSI revision: 02
 Detected scsi CD-ROM sr0 at scsi0, channel 0, id 5, lun 0

Ian.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/fcntl.h>

#ifndef SCSI_IOCTL_SEND_COMMAND
#define SCSI_IOCTL_SEND_COMMAND 1
#endif

#define IOCBUFSZ 1638400
#define BLOCKAUDIO 2352
#define BLOCKSUBQ 16
#define BLOCKSUBCODE 96
#define BLOCKTOTAL (BLOCKAUDIO+BLOCKSUBCODE)

struct msf { unsigned char m,s,f; };

struct subq {
  unsigned control:4;
  unsigned address:4;
  unsigned char tno, indexx;
  struct msf elapsed;
  unsigned char zero1;
  struct msf absolute;
  unsigned char zero6[6];
};

int main(int argc, char **argv) {
  int fd, r, e, i, nn, ns, ne, j, dfd, sfd;
  char *s;
  unsigned long ul;
  unsigned short us;
  struct subq *subq;

  struct {
    int length_of_input_data;
    int length_of_output_buffer;
    char actualstuff[IOCBUFSZ];
  } ioctlbuf;

  fd= open("/dev/cdrom",O_RDONLY);
  if (fd<0) { perror("open device"); exit(1); }
  dfd= open("rawsample",O_WRONLY|O_CREAT|O_TRUNC,0666);
  if (dfd<0) { perror("create rawsample"); exit(1); }
  sfd= open("subcode",O_WRONLY|O_CREAT|O_TRUNC,0666);
  if (sfd<0) { perror("create subcode"); exit(1); }

  ns= argc>1 ? atoi(argv[1]) : 0; /* Starting request number */
  nn= argc>2 ? atoi(argv[2]) : 1; /* Number of requests */
  ne= argc>3 ? atoi(argv[3]) : 1; /* Quantity in each request */
  for (i=0; i<nn; i++) {
    ioctlbuf.length_of_input_data= 0;
    ioctlbuf.length_of_output_buffer= IOCBUFSZ;
    ioctlbuf.actualstuff[0]= 0xd4;
    ioctlbuf.actualstuff[1]= 0x02; /* include the subq data */
    ul= htonl(ns+i*ne); memcpy(ioctlbuf.actualstuff+2, &ul, 4);
    ioctlbuf.actualstuff[6]= 0x00;
    us= htons(ne); memcpy(ioctlbuf.actualstuff+7, &us, 2);
    ioctlbuf.actualstuff[9]= 0x00;

/*if (!(i%50)) sleep(2);*/
    errno=0;
    r= ioctl(fd, SCSI_IOCTL_SEND_COMMAND, &ioctlbuf);
    if (r) {
      e=errno; s=strerror(e);
      fprintf(stdout,"%d... ioctl: %08x; (%d %s)\n",ns+i*ne,r,e,s?s:"");
    } else {
      for (j=0; j<ne; j++) {
        errno=0; r= write(dfd, ioctlbuf.actualstuff+BLOCKTOTAL*j, BLOCKAUDIO);
        if (r!=BLOCKAUDIO) { perror("write"); exit(1); }

        errno=0;
        r= write(sfd, ioctlbuf.actualstuff+BLOCKTOTAL*j+BLOCKAUDIO, BLOCKSUBCODE);
        if (r!=BLOCKSUBCODE) { perror("write"); exit(1); }

        subq= (struct subq*)(ioctlbuf.actualstuff+BLOCKTOTAL*j+BLOCKAUDIO);
        fprintf(stdout,
                "%d:  %c%c%x  %3d/%-3d %2d:%02d:%02x %2d:%02d:%02x  "
                "%02x %02x%02x%02x%02x%02x%02x\n",
                ns+i*ne+j,
                "sS__dX__qQ__YZ__"[subq->control&0x0d],
                (subq->control&0x02) ? '!' : '+',
                subq->address,
                subq->indexx, subq->tno,
                subq->elapsed.m, subq->elapsed.s, subq->elapsed.f /*BCD!*/,
                subq->absolute.m, subq->absolute.s, subq->absolute.f /*BCD!*/,
                subq->zero1,
                subq->zero6[0], subq->zero6[1], subq->zero6[2],
                subq->zero6[3], subq->zero6[4], subq->zero6[5]);
      }
    }
  }
  exit(0);
}