diff --git a/ChangeLog b/ChangeLog index 6112d953..0c92dca7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,12 @@ - Make libntfs more portable by removing the dependency on asm/byteorder.h and using and instead. Also adapting our endians.h appropriately. (Richard Russon) + - Fix nasty buffer overflow bug in ntfs_get_attribute_value() which we + use to get the attribute list attribute. This is the same bug that + was biting us in the ntfs driver 2.1.4 and was fixed in 2.1.5. + Windows XP creates files where an attribute's data size is more than + one run list run shorter than the allocated size which causes us to + crash out as we assumed this would never happen. - Various other small cleanups and fixes. 10/11/2003 - 1.8.0beta2 - More features! diff --git a/libntfs/attrib.c b/libntfs/attrib.c index d5a59730..f425f772 100644 --- a/libntfs/attrib.c +++ b/libntfs/attrib.c @@ -66,6 +66,10 @@ s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a) s64 ntfs_get_attribute_value(const ntfs_volume *vol, const MFT_RECORD *m, const ATTR_RECORD *a, u8 *b) { + runlist *rl; + s64 total, r; + int i; + /* Sanity checks. */ if (!vol || !m || !a || !b) { errno = EINVAL; @@ -78,151 +82,150 @@ s64 ntfs_get_attribute_value(const ntfs_volume *vol, const MFT_RECORD *m, errno = ENOTSUP; return 0; } - if (!a->non_resident) { /* Attribute is resident. */ + if (!a->non_resident) { + /* Attribute is resident. */ + /* Sanity check. */ - if (le32_to_cpu(a->value_length) + - le16_to_cpu(a->value_offset) > le32_to_cpu(a->length)) { + if (le32_to_cpu(a->value_length) + le16_to_cpu(a->value_offset) + > le32_to_cpu(a->length)) { return 0; } + memcpy(b, (char*)a + le16_to_cpu(a->value_offset), - le32_to_cpu(a->value_length)); + le32_to_cpu(a->value_length)); errno = 0; return (s64)le32_to_cpu(a->value_length); - } else { /* Attribute is not resident. */ - runlist *rl; - s64 total, r; - int i; - - /* If no data, return 0. */ - if (!(a->data_size)) { - errno = 0; - return 0; - } - /* - * FIXME: What about attribute lists?!? (AIA) - */ - /* Decompress the mapping pairs array into a runlist. */ - rl = ntfs_mapping_pairs_decompress(vol, a, NULL); - if (!rl) { - errno = EINVAL; - return 0; - } - /* - * FIXED: We were overflowing here in a nasty fashion when we - * reach the last cluster in the runlist as the buffer will - * only be big enough to hold data_size bytes while we are - * reading in allocated_size bytes which is usually larger - * than data_size, since the actual data is unlikely to have a - * size equal to a multiple of the cluster size! - */ - /* Now load all clusters in the runlist into b. */ - for (i = 0, total = 0; rl[i].length; i++) { - if (!rl[i+1].length) { - unsigned char *intbuf = NULL; - /* - * We have reached the last run so we were - * going to overflow when executing the - * ntfs_pread() which is BAAAAAAAD! - * Temporary fix: - * Allocate a new buffer with size: - * rl[i].length << vol->cluster_size_bits, - * do the read into our buffer, then - * memcpy the correct amount of data into - * the caller supplied buffer, free our - * buffer, and continue. - */ - intbuf = malloc(rl[i].length << - vol->cluster_size_bits); - if (!intbuf) { - int eo = errno; - perror("Couldn't allocate memory for " - "internal buffer.\n"); - free(rl); - errno = eo; - return 0; - } - /* - * FIXME: If compressed file: Only read if - * lcn != -1. Otherwise, we are dealing with a - * sparse run and we just memset the user buffer - * to 0 for the length of the run, which should - * be 16 (= compression unit size). - * FIXME: Really only when file is compressed, - * or can we have sparse runs in uncompressed - * files as well? - */ - r = ntfs_pread(vol->dev, rl[i].lcn << - vol->cluster_size_bits, - rl[i].length << - vol->cluster_size_bits, intbuf); - if (r != rl[i].length << - vol->cluster_size_bits) { -#define ESTR "Error reading attribute value" - if (r == -1) { - int eo = errno; - perror(ESTR); - errno = eo; - } else if (r < rl[i].length << - vol->cluster_size_bits - ) { - fprintf(stderr, ESTR ": Ran " - "out of input data.\n"); - errno = EIO; - } else { - fprintf(stderr, ESTR ": " - "unknown error\n"); - errno = EIO; - } -#undef ESTR - free(rl); - return 0; - } - memcpy(b + total, intbuf, - sle64_to_cpu(a->data_size) - total); - free(intbuf); - total = sle64_to_cpu(a->data_size); - } else { - /* - * FIXME: If compressed file: Only read if - * lcn != -1. Otherwise, we are dealing with a - * sparse run and we just memset the user buffer - * to 0 for the length of the run, which should - * be 16 (= compression unit size). - */ - r = ntfs_pread(vol->dev, rl[i].lcn << - vol->cluster_size_bits, - rl[i].length << - vol->cluster_size_bits, - b + total); - if (r != rl[i].length << - vol->cluster_size_bits) { -#define ESTR "Error reading attribute value" - if (r == -1) { - int eo = errno; - perror(ESTR); - errno = eo; - } else if (r < rl[i].length << - vol->cluster_size_bits - ) { - fprintf(stderr, ESTR ": Ran " - "out of input data.\n"); - errno = EIO; - } else { - fprintf(stderr, ESTR ": " - "unknown error\n"); - errno = EIO; - } -#undef ESTR - return 0; - } - total += r; - } - } - free(rl); - return total; } - errno = EINVAL; - return 0; + + /* Attribute is not resident. */ + + /* If no data, return 0. */ + if (!(a->data_size)) { + errno = 0; + return 0; + } + /* + * FIXME: What about attribute lists?!? (AIA) + */ + /* Decompress the mapping pairs array into a runlist. */ + rl = ntfs_mapping_pairs_decompress(vol, a, NULL); + if (!rl) { + errno = EINVAL; + return 0; + } + /* + * FIXED: We were overflowing here in a nasty fashion when we + * reach the last cluster in the runlist as the buffer will + * only be big enough to hold data_size bytes while we are + * reading in allocated_size bytes which is usually larger + * than data_size, since the actual data is unlikely to have a + * size equal to a multiple of the cluster size! + * FIXED2: We were also overflowing here in the same fashion + * when the data_size was more than one run smaller than the + * allocated size which happens with Windows XP sometimes. + */ + /* Now load all clusters in the runlist into b. */ + for (i = 0, total = 0; rl[i].length; i++) { + if (total + (rl[i].length << vol->cluster_size_bits) >= + sle64_to_cpu(a->data_size)) { + unsigned char *intbuf = NULL; + /* + * We have reached the last run so we were going to + * overflow when executing the ntfs_pread() which is + * BAAAAAAAD! + * Temporary fix: + * Allocate a new buffer with size: + * rl[i].length << vol->cluster_size_bits, do the + * read into our buffer, then memcpy the correct + * amount of data into the caller supplied buffer, + * free our buffer, and continue. + * We have reached the end of data size so we were + * going to overflow in the same fashion. + * Temporary fix: same as above. + */ + intbuf = malloc(rl[i].length << vol->cluster_size_bits); + if (!intbuf) { + int eo = errno; + perror("Couldn't allocate memory for internal " + "buffer.\n"); + free(rl); + errno = eo; + return 0; + } + /* + * FIXME: If compressed file: Only read if lcn != -1. + * Otherwise, we are dealing with a sparse run and we + * just memset the user buffer to 0 for the length of + * the run, which should be 16 (= compression unit + * size). + * FIXME: Really only when file is compressed, or can + * we have sparse runs in uncompressed files as well? + * - Yes we can, in sparse files! But not necessarily + * size of 16, just run length. + */ + r = ntfs_pread(vol->dev, rl[i].lcn << + vol->cluster_size_bits, rl[i].length << + vol->cluster_size_bits, intbuf); + if (r != rl[i].length << vol->cluster_size_bits) { +#define ESTR "Error reading attribute value" + if (r == -1) { + int eo = errno; + perror(ESTR); + errno = eo; + } else if (r < rl[i].length << + vol->cluster_size_bits) { + fprintf(stderr, ESTR ": Ran out of " + "input data.\n"); + errno = EIO; + } else { + fprintf(stderr, ESTR ": unknown " + "error\n"); + errno = EIO; + } +#undef ESTR + free(rl); + return 0; + } + memcpy(b + total, intbuf, sle64_to_cpu(a->data_size) - + total); + free(intbuf); + total = sle64_to_cpu(a->data_size); + break; + } + /* + * FIXME: If compressed file: Only read if lcn != -1. + * Otherwise, we are dealing with a sparse run and we just + * memset the user buffer to 0 for the length of the run, which + * should be 16 (= compression unit size). + * FIXME: Really only when file is compressed, or can + * we have sparse runs in uncompressed files as well? + * - Yes we can, in sparse files! But not necessarily size of + * 16, just run length. + */ + r = ntfs_pread(vol->dev, rl[i].lcn << vol->cluster_size_bits, + rl[i].length << vol->cluster_size_bits, + b + total); + if (r != rl[i].length << vol->cluster_size_bits) { +#define ESTR "Error reading attribute value" + if (r == -1) { + int eo = errno; + perror(ESTR); + errno = eo; + } else if (r < rl[i].length << vol->cluster_size_bits) { + fprintf(stderr, ESTR ": Ran out of input " + "data.\n"); + errno = EIO; + } else { + fprintf(stderr, ESTR ": unknown error\n"); + errno = EIO; + } +#undef ESTR + return 0; + } + total += r; + } + free(rl); + return total; } /* Already cleaned up code below, but still look for FIXME:... */