From: Andy Polyakov Fix a few range checks which aren't working right because if (int < sizeof(...)) does the wrong thing if `int' is negative, due to `sizeof' returning unsigned. In addition to comparisons, the patch makes CDROMVOL* ioctl more robust. Signed-off-by: Andrew Morton --- 25-akpm/drivers/cdrom/cdrom.c | 93 +++++++++++++++++++++++++++--------------- 1 files changed, 61 insertions(+), 32 deletions(-) diff -puN drivers/cdrom/cdrom.c~cdrom-range-fixes drivers/cdrom/cdrom.c --- 25/drivers/cdrom/cdrom.c~cdrom-range-fixes 2004-09-02 20:18:02.392542360 -0700 +++ 25-akpm/drivers/cdrom/cdrom.c 2004-09-02 20:18:02.398541448 -0700 @@ -614,13 +614,16 @@ static int cdrom_flush_cache(struct cdro static int cdrom_mrw_exit(struct cdrom_device_info *cdi) { disc_information di; - int ret = 0; + int ret; - if (cdrom_get_disc_info(cdi, &di) < offsetof(typeof(di),disc_type)) + ret = cdrom_get_disc_info(cdi, &di); + if (ret < 0 || ret < (int)offsetof(typeof(di),disc_type)) return 1; + ret = 0; if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) { - printk(KERN_INFO "cdrom: issuing MRW back ground format suspend\n"); + printk(KERN_INFO "cdrom: issuing MRW back ground " + "format suspend\n"); ret = cdrom_mrw_bgformat_susp(cdi, 0); } @@ -724,8 +727,10 @@ int cdrom_is_random_writable(struct cdro static int cdrom_media_erasable(struct cdrom_device_info *cdi) { disc_information di; + int ret; - if (cdrom_get_disc_info(cdi, &di) < offsetof(typeof(di),n_first_track)) + ret = cdrom_get_disc_info(cdi, &di); + if (ret < 0 || ret < offsetof(typeof(di), n_first_track)) return -1; return di.erasable; @@ -761,7 +766,8 @@ static int cdrom_mrw_open_write(struct c return 1; } - if (cdrom_get_disc_info(cdi, &di) < offsetof(typeof(di),disc_type)) + ret = cdrom_get_disc_info(cdi, &di); + if (ret < 0 || ret < offsetof(typeof(di),disc_type)) return 1; if (!di.erasable) @@ -775,10 +781,12 @@ static int cdrom_mrw_open_write(struct c * 3 - MRW formatting complete */ ret = 0; - printk(KERN_INFO "cdrom open: mrw_status '%s'\n", mrw_format_status[di.mrw_status]); + printk(KERN_INFO "cdrom open: mrw_status '%s'\n", + mrw_format_status[di.mrw_status]); if (!di.mrw_status) ret = 1; - else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE && mrw_format_restart) + else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE && + mrw_format_restart) ret = cdrom_mrw_bgformat(cdi, 1); return ret; @@ -2593,7 +2601,7 @@ static int mmc_ioctl(struct cdrom_device struct cdrom_device_ops *cdo = cdi->ops; struct packet_command cgc; struct request_sense sense; - char buffer[32]; + unsigned char buffer[32]; int ret = 0; memset(&cgc, 0, sizeof(cgc)); @@ -2720,8 +2728,9 @@ static int mmc_ioctl(struct cdrom_device case CDROMVOLCTRL: case CDROMVOLREAD: { struct cdrom_volctrl volctrl; - char mask[32]; + char mask[sizeof(buffer)]; unsigned short offset; + cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n"); IOCTL_IN(arg, struct cdrom_volctrl, volctrl); @@ -2731,17 +2740,27 @@ static int mmc_ioctl(struct cdrom_device if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0))) return ret; - /* some drives have longer pages, adjust and reread. */ - if (buffer[1] > cgc.buflen) { - cgc.buflen = buffer[1] + 2; - if ((ret = cdrom_mode_sense(cdi, &cgc, - GPMODE_AUDIO_CTL_PAGE, 0))) - return ret; + /* originally the code depended on buffer[1] to determine + how much data is available for transfer. buffer[1] is + unfortunately ambigious and the only reliable way seem + to be to simply skip over the block descriptor... */ + offset = 8 + be16_to_cpu(*(unsigned short *)(buffer+6)); + + if (offset + 16 > sizeof(buffer)) + return -E2BIG; + + if (offset + 16 > cgc.buflen) { + cgc.buflen = offset+16; + ret = cdrom_mode_sense(cdi, &cgc, + GPMODE_AUDIO_CTL_PAGE, 0); + if (ret) + return ret; } - - /* get the offset from the length of the page. length - is measure from byte 2 an on, thus the 14. */ - offset = buffer[1] - 14; + + /* sanity check */ + if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE || + buffer[offset+1] < 14) + return -EINVAL; /* now we have the current volume settings. if it was only a CDROMVOLREAD, return these values */ @@ -2766,7 +2785,8 @@ static int mmc_ioctl(struct cdrom_device buffer[offset+15] = volctrl.channel3 & mask[offset+15]; /* set volume */ - cgc.buffer = buffer; + cgc.buffer = buffer + offset - 8; + memset(cgc.buffer, 0, 8); return cdrom_mode_select(cdi, &cgc); } @@ -2922,28 +2942,32 @@ int cdrom_get_last_written(struct cdrom_ if (!CDROM_CAN(CDC_GENERIC_PACKET)) goto use_toc; - if ((ret = cdrom_get_disc_info(cdi, &di)) - < offsetof(typeof(di), last_track_msb) - + sizeof(di.last_track_msb)) + ret = cdrom_get_disc_info(cdi, &di); + if (ret < (int)(offsetof(typeof(di), last_track_lsb) + + sizeof(di.last_track_lsb))) goto use_toc; + /* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */ last_track = (di.last_track_msb << 8) | di.last_track_lsb; ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); - if (ti_size < offsetof(typeof(ti), track_start)) + if (ti_size < (int)offsetof(typeof(ti), track_start)) goto use_toc; /* if this track is blank, try the previous. */ if (ti.blank) { + if (last_track==1) + goto use_toc; last_track--; ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); } - if (ti_size < offsetof(typeof(ti), track_size) + sizeof(ti.track_size)) + if (ti_size < (int)(offsetof(typeof(ti), track_size) + + sizeof(ti.track_size))) goto use_toc; /* if last recorded field is valid, return it. */ - if (ti.lra_v && ti_size >= offsetof(typeof(ti), last_rec_address) - + sizeof(ti.last_rec_address)) { + if (ti.lra_v && ti_size >= (int)(offsetof(typeof(ti), last_rec_address) + + sizeof(ti.last_rec_address))) { *last_written = be32_to_cpu(ti.last_rec_address); } else { /* make it up instead */ @@ -2974,25 +2998,30 @@ static int cdrom_get_next_writable(struc disc_information di; track_information ti; __u16 last_track; - int ret = -1, ti_size; + int ret, ti_size; if (!CDROM_CAN(CDC_GENERIC_PACKET)) goto use_last_written; - if ((ret = cdrom_get_disc_info(cdi, &di)) - < offsetof(typeof(di), last_track_msb) - + sizeof(di.last_track_msb)) + ret = cdrom_get_disc_info(cdi, &di); + if (ret < 0 || ret < offsetof(typeof(di), last_track_lsb) + + sizeof(di.last_track_lsb)) goto use_last_written; + /* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */ last_track = (di.last_track_msb << 8) | di.last_track_lsb; ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); - if (ti_size < offsetof(typeof(ti), track_start)) + if (ti_size < 0 || ti_size < offsetof(typeof(ti), track_start)) goto use_last_written; /* if this track is blank, try the previous. */ if (ti.blank) { + if (last_track == 1) + goto use_last_written; last_track--; ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); + if (ti_size < 0) + goto use_last_written; } /* if next recordable address field is valid, use it. */ _