Commit f0828420 authored by Samuel Thibault's avatar Samuel Thibault Committed by Linus Torvalds

[PATCH] Subject: cdrom.c get_last_written fixup

There's something wrong in cdrom.c: cdrom_get_last_written() for instance
calls cdrom_get_disc_info() and cdrom_get_track_info() to get information
about tracks, but these functions don't ensure that all the
track_information or disc_information structure is filled:

	/* (buflen was first set to 8 to get track_information_length field) */

	if ((ret = cdo->generic_packet(cdi, &cgc)))
		return ret;

	cgc.buflen = be16_to_cpu(ti->track_information_length) +
		     sizeof(ti->track_information_length);

	if (cgc.buflen > sizeof(track_information))
		cgc.buflen = sizeof(track_information);

	cgc.cmd[8] = cgc.buflen;
	return cdo->generic_packet(cdi, &cgc);

The second test ensures that at least we won't overflow the structure, but
nothing ensures that all the structure will be filled.

And indeed, we have a drive here that won't fill it all: the returned
track_information_length field will be *less than*
sizeof(track_information) - sizeof(ti->track_information_length), so that
cdrom_get_last_written() reads values that weren't filled in!  As a result,
we are sometimes unable to read some parts of CDROMs, depending on the
uninitialized state of the structure...

Here is a patch that adds filling checks: cdrom_get_disc_info() and
cdrom_get_track_info() return the actual filled length, and it's up to the
caller to check that this is enough for him to get the values it wants.

Note: adding something like a
#define spanof(TYPE, MEMBER) ((size_t) ((&((TYPE *)0)->MEMBER)+1))
definition just near that of offsetof() in include/linux/stddef.h would
make it more pretty, but still it won't help for bitfields :/
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent a2a3b74d
...@@ -607,7 +607,7 @@ static int cdrom_mrw_exit(struct cdrom_device_info *cdi) ...@@ -607,7 +607,7 @@ static int cdrom_mrw_exit(struct cdrom_device_info *cdi)
disc_information di; disc_information di;
int ret = 0; int ret = 0;
if (cdrom_get_disc_info(cdi, &di)) if (cdrom_get_disc_info(cdi, &di) < offsetof(typeof(di),disc_type))
return 1; return 1;
if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) { if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) {
...@@ -716,7 +716,7 @@ static int cdrom_media_erasable(struct cdrom_device_info *cdi) ...@@ -716,7 +716,7 @@ static int cdrom_media_erasable(struct cdrom_device_info *cdi)
{ {
disc_information di; disc_information di;
if (cdrom_get_disc_info(cdi, &di)) if (cdrom_get_disc_info(cdi, &di) < offsetof(typeof(di),n_first_track))
return -1; return -1;
return di.erasable; return di.erasable;
...@@ -752,7 +752,7 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi) ...@@ -752,7 +752,7 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi)
return 1; return 1;
} }
if (cdrom_get_disc_info(cdi, &di)) if (cdrom_get_disc_info(cdi, &di) < offsetof(typeof(di),disc_type))
return 1; return 1;
if (!di.erasable) if (!di.erasable)
...@@ -2760,7 +2760,7 @@ static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8 ...@@ -2760,7 +2760,7 @@ static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8
{ {
struct cdrom_device_ops *cdo = cdi->ops; struct cdrom_device_ops *cdo = cdi->ops;
struct packet_command cgc; struct packet_command cgc;
int ret; int ret, buflen;
init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ); init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO; cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
...@@ -2773,14 +2773,18 @@ static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8 ...@@ -2773,14 +2773,18 @@ static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8
if ((ret = cdo->generic_packet(cdi, &cgc))) if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret; return ret;
cgc.buflen = be16_to_cpu(ti->track_information_length) + buflen = be16_to_cpu(ti->track_information_length) +
sizeof(ti->track_information_length); sizeof(ti->track_information_length);
if (cgc.buflen > sizeof(track_information)) if (buflen > sizeof(track_information))
cgc.buflen = sizeof(track_information); buflen = sizeof(track_information);
cgc.cmd[8] = cgc.buflen; cgc.cmd[8] = cgc.buflen = buflen;
return cdo->generic_packet(cdi, &cgc); if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
/* return actual fill size */
return buflen;
} }
/* requires CD R/RW */ /* requires CD R/RW */
...@@ -2788,7 +2792,7 @@ static int cdrom_get_disc_info(struct cdrom_device_info *cdi, disc_information * ...@@ -2788,7 +2792,7 @@ static int cdrom_get_disc_info(struct cdrom_device_info *cdi, disc_information *
{ {
struct cdrom_device_ops *cdo = cdi->ops; struct cdrom_device_ops *cdo = cdi->ops;
struct packet_command cgc; struct packet_command cgc;
int ret; int ret, buflen;
/* set up command and get the disc info */ /* set up command and get the disc info */
init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ); init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
...@@ -2802,14 +2806,18 @@ static int cdrom_get_disc_info(struct cdrom_device_info *cdi, disc_information * ...@@ -2802,14 +2806,18 @@ static int cdrom_get_disc_info(struct cdrom_device_info *cdi, disc_information *
/* not all drives have the same disc_info length, so requeue /* not all drives have the same disc_info length, so requeue
* packet with the length the drive tells us it can supply * packet with the length the drive tells us it can supply
*/ */
cgc.buflen = be16_to_cpu(di->disc_information_length) + buflen = be16_to_cpu(di->disc_information_length) +
sizeof(di->disc_information_length); sizeof(di->disc_information_length);
if (cgc.buflen > sizeof(disc_information)) if (buflen > sizeof(disc_information))
cgc.buflen = sizeof(disc_information); buflen = sizeof(disc_information);
cgc.cmd[8] = cgc.buflen; cgc.cmd[8] = cgc.buflen = buflen;
return cdo->generic_packet(cdi, &cgc); if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
/* return actual fill size */
return buflen;
} }
/* return the last written block on the CD-R media. this is for the udf /* return the last written block on the CD-R media. this is for the udf
...@@ -2820,27 +2828,33 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written) ...@@ -2820,27 +2828,33 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written)
disc_information di; disc_information di;
track_information ti; track_information ti;
__u32 last_track; __u32 last_track;
int ret = -1; int ret = -1, ti_size;
if (!CDROM_CAN(CDC_GENERIC_PACKET)) if (!CDROM_CAN(CDC_GENERIC_PACKET))
goto use_toc; goto use_toc;
if ((ret = cdrom_get_disc_info(cdi, &di))) if ((ret = cdrom_get_disc_info(cdi, &di))
< offsetof(typeof(di), last_track_msb)
+ sizeof(di.last_track_msb))
goto use_toc; goto use_toc;
last_track = (di.last_track_msb << 8) | di.last_track_lsb; last_track = (di.last_track_msb << 8) | di.last_track_lsb;
if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti))) ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
if (ti_size < offsetof(typeof(ti), track_start))
goto use_toc; goto use_toc;
/* if this track is blank, try the previous. */ /* if this track is blank, try the previous. */
if (ti.blank) { if (ti.blank) {
last_track--; last_track--;
if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti))) ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
goto use_toc;
} }
if (ti_size < offsetof(typeof(ti), track_size) + sizeof(ti.track_size))
goto use_toc;
/* if last recorded field is valid, return it. */ /* if last recorded field is valid, return it. */
if (ti.lra_v) { if (ti.lra_v && ti_size >= offsetof(typeof(ti), last_rec_address)
+ sizeof(ti.last_rec_address)) {
*last_written = be32_to_cpu(ti.last_rec_address); *last_written = be32_to_cpu(ti.last_rec_address);
} else { } else {
/* make it up instead */ /* make it up instead */
...@@ -2853,11 +2867,12 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written) ...@@ -2853,11 +2867,12 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written)
/* this is where we end up if the drive either can't do a /* this is where we end up if the drive either can't do a
GPCMD_READ_DISC_INFO or GPCMD_READ_TRACK_RZONE_INFO or if GPCMD_READ_DISC_INFO or GPCMD_READ_TRACK_RZONE_INFO or if
it fails. then we return the toc contents. */ it doesn't give enough information or fails. then we return
the toc contents. */
use_toc: use_toc:
toc.cdte_format = CDROM_MSF; toc.cdte_format = CDROM_MSF;
toc.cdte_track = CDROM_LEADOUT; toc.cdte_track = CDROM_LEADOUT;
if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc)) if ((ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc)))
return ret; return ret;
sanitize_format(&toc.cdte_addr, &toc.cdte_format, CDROM_LBA); sanitize_format(&toc.cdte_addr, &toc.cdte_format, CDROM_LBA);
*last_written = toc.cdte_addr.lba; *last_written = toc.cdte_addr.lba;
...@@ -2870,32 +2885,33 @@ static int cdrom_get_next_writable(struct cdrom_device_info *cdi, long *next_wri ...@@ -2870,32 +2885,33 @@ static int cdrom_get_next_writable(struct cdrom_device_info *cdi, long *next_wri
disc_information di; disc_information di;
track_information ti; track_information ti;
__u16 last_track; __u16 last_track;
int ret = -1; int ret = -1, ti_size;
if (!CDROM_CAN(CDC_GENERIC_PACKET)) if (!CDROM_CAN(CDC_GENERIC_PACKET))
goto use_last_written; goto use_last_written;
if ((ret = cdrom_get_disc_info(cdi, &di))) if ((ret = cdrom_get_disc_info(cdi, &di))
< offsetof(typeof(di), last_track_msb)
+ sizeof(di.last_track_msb))
goto use_last_written; goto use_last_written;
last_track = (di.last_track_msb << 8) | di.last_track_lsb; last_track = (di.last_track_msb << 8) | di.last_track_lsb;
if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti))) ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
if (ti_size < offsetof(typeof(ti), track_start))
goto use_last_written; goto use_last_written;
/* if this track is blank, try the previous. */ /* if this track is blank, try the previous. */
if (ti.blank) { if (ti.blank) {
last_track--; last_track--;
if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti))) ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
goto use_last_written;
} }
/* if next recordable address field is valid, use it. */ /* if next recordable address field is valid, use it. */
if (ti.nwa_v) if (ti.nwa_v && ti_size >= offsetof(typeof(ti), next_writable)
+ sizeof(ti.next_writable)) {
*next_writable = be32_to_cpu(ti.next_writable); *next_writable = be32_to_cpu(ti.next_writable);
else return 0;
goto use_last_written; }
return 0;
use_last_written: use_last_written:
if ((ret = cdrom_get_last_written(cdi, next_writable))) { if ((ret = cdrom_get_last_written(cdi, next_writable))) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment