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.

(Logical change 1.241)
This commit is contained in:
cantab.net!aia21 2003-11-19 12:16:02 +00:00
parent 12971872b4
commit 33918ceae9
2 changed files with 147 additions and 138 deletions

View File

@ -6,6 +6,12 @@
- Make libntfs more portable by removing the dependency on
asm/byteorder.h and using <endian.h> and <byteswap.h> 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!

View File

@ -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:... */