kern/114636: [patch] ioctl on empty scsi/atapicam cdrom drive locks up entire system

Tijl Coosemans tijl at
Mon Jul 16 17:50:02 UTC 2007

>Number:         114636
>Category:       kern
>Synopsis:       [patch] ioctl on empty scsi/atapicam cdrom drive locks up entire system
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jul 16 17:50:01 GMT 2007
>Originator:     Tijl Coosemans
>Release:        FreeBSD 7.0-CURRENT i386
cd0 at ata1 bus 0 target 0 lun 0
cd0: <MATSHITA UJ-831D 1.00> Removable CD-ROM SCSI-0 device
cd0: 3.300MB/s transfers
cd0: Attempt to query device size failed: NOT READY, Medium not present

An ioctl call on an empty scsi/atapicam cdrom drive causes
the system to semi-hang when the file descriptor is later
closed. Semi meaning the system is responsive to mouse and
keyboard, but (most) processes hang and no new processes can
be started. Pressing ctrl+alt+delete responds by disabling
terminals but doesn't reset the system. A hard reset is
needed to recover.

The following program opens /dev/cd0 and does CDIOCSTART
to spin up the drive. When the drive is empty the close(2)
call causes the entire system to (semi-)hang.

BE WARNED because a hard reset is required to recover.
So don't try if you don't like fsck.

--- cdrom.c begins here ---
#include <sys/cdio.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(void) {
	int fd;
	int ret;

	fd = open( "/dev/cd0", O_RDONLY );
	ret = ioctl( fd, CDIOCSTART );
	printf( "ret = %d, errno = %d\n", ret, errno );

	close( fd );	
	return 0;
--- cdrom.c ends here ---

The problem is caused by a forgotten cam_periph_unhold()
when there's no media in the drive.

--- patch-sys-cam-scsi-scsi_cd.c begins here ---
--- sys/cam/scsi/scsi_cd.c.orig	2007-07-12 15:32:30.000000000 +0200
+++ sys/cam/scsi/scsi_cd.c	2007-07-12 15:39:15.000000000 +0200
@@ -1859,16 +1859,18 @@
 	 && ((cmd != CDIOCCLOSE)
 	  && (cmd != CDIOCEJECT))
 	 && (IOCGROUP(cmd) == 'c')) {
-		error = cdcheckmedia(periph);
+		if ((error = cdcheckmedia(periph)) != 0) {
+			cam_periph_unhold(periph);
+			cam_periph_unlock(periph);
+			return (error);
+		}
 	 * Drop the lock here so later mallocs can use WAITOK.  The periph
 	 * is essentially locked still with the cam_periph_hold call above.
-	if (error != 0)
-		return (error);
 	nocopyout = 0;
 	switch (cmd) {
--- patch-sys-cam-scsi-scsi_cd.c ends here ---

More information about the freebsd-bugs mailing list