isofs: fix KMSAN uninit-value bug in do_isofs_readdir()
Subsystems (2)
PATCH 1/1 isofs: fix KMSAN uninit-value bug in do_isofs_readdir()
In do_isofs_readdir() when assigning the variable
"struct iso_directory_record *de" the b_data field of the buffer_head is accessed and an offset is added to it, the size of b_data is 2048 and the offset size is 2047, meaning
"de = (struct iso_directory_record *) (bh->b_data + offset);" yields the final byte of the 2048 sized b_data block.
The first byte of the directory record (de_len) is then read and found to be 31, meaning the directory record size is 31 bytes long. The directory record is defined by the structure:
struct iso_directory_record {
__u8 length; // 1 byte
__u8 ext_attr_length; // 1 byte
__u8 extent[8]; // 8 bytes
__u8 size[8]; // 8 bytes
__u8 date[7]; // 7 bytes
__u8 flags; // 1 byte
__u8 file_unit_size; // 1 byte
__u8 interleave; // 1 byte
__u8 volume_sequence_number[4]; // 4 bytes
__u8 name_len; // 1 byte
char name[]; // variable size
} __attribute__((packed));
The fixed portion of this structure occupies 33 bytes. Therefore, a valid directory record must be at least 33 bytes long (even without considering the variable-length name field). Since de_len is only 31, it is insufficient to contain the complete fixed header.
The code later hits the following sanity check that compares de_len against the sum of de->name_len and sizeof(struct iso_directory_record):
if (de_len < de->name_len[0] + sizeof(struct iso_directory_record)) {
...
}Since the fixed portion of the structure is
33 bytes (up to and including name_len member),
a valid record should have de_len of at least 33 bytes; here, however, de_len is too short, and the field de->name_len (located at offset 32) is accessed even though it lies beyond the available 31 bytes.
This access on the corrupted isofs data triggers a KASAN uninitialized memory warning. The fix would be to first verify that de_len is at least sizeof(struct iso_directory_record) before accessing any fields like de->name_len.
--- fs/isofs/dir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/isofs/dir.c +2 -1
@@ -147,7 +147,8 @@ static int do_isofs_readdir(struct inode *inode, struct file *file, de = tmpde; }/* Basic sanity check, whether name doesn't exceed dir entry */ -if (de_len < de->name_len[0 ] + +if (de_len <sizeof (struct iso_directory_record ) || + de_len < de->name_len[0 ] +sizeof (struct iso_directory_record )) { printk(KERN_NOTICE "iso9660: Corrupted directory entry" " in block %lu of inode %lu\n", block,
Signature · 1 line
2.39.5
Jan Kara
maintainerQuoted 51 lines · Qasim Ijaz
In
do_isofs_readdir()when assigning the variable
"structiso_directory_record*de" theb_datafield of thebuffer_headis accessed and an offset is added to it, the size ofb_datais 2048 and the offset size is 2047, meaning
"de = (structiso_directory_record*) (bh->b_data+ offset);" yields the final byte of the 2048 sizedb_datablock.The first byte of the directory record (
de_len) is then read and found to be 31, meaning the directory record size is 31 bytes long. The directory record is defined by the structure:struct
iso_directory_record{
__u8length; // 1 byte
__u8ext_attr_length; // 1 byte
__u8extent[8]; // 8 bytes
__u8size[8]; // 8 bytes
__u8date[7]; // 7 bytes
__u8flags; // 1 byte
__u8file_unit_size; // 1 byte
__u8interleave; // 1 byte
__u8volume_sequence_number[4]; // 4 bytes
__u8name_len; // 1 byte
char name[]; // variable size
} __attribute__((packed));The fixed portion of this structure occupies 33 bytes. Therefore, a valid directory record must be at least 33 bytes long (even without considering the variable-length name field). Since
de_lenis only 31, it is insufficient to contain the complete fixed header.The code later hits the following sanity check that compares
de_lenagainst the sum ofde->name_lenandsizeof(struct iso_directory_record):if (de_len < de->name_len[0] + sizeof(struct iso_directory_record)) { ... }Since the fixed portion of the structure is
33 bytes (up to and includingname_lenmember),
a valid record should havede_lenof at least 33 bytes; here, however,de_lenis too short, and the fieldde->name_len(located at offset 32) is accessed even though it lies beyond the available 31 bytes.This access on the corrupted isofs data triggers a KASAN uninitialized memory warning. The fix would be to first verify that
de_lenis at leastsizeof(struct iso_directory_record)before accessing any fields likede->name_len.
Quoted 5 lines
Reported-by syzbot <[email protected]>Tested-by syzbot <[email protected]>Fixes 2deb1acc653c ("isofs: fix access to unallocated memory when reading corrupted filesystem")Signed-off-by Qasim Ijaz <[email protected]>
Thanks. Added to my tree.
Honza
Quoted 21 lines
--- fs/isofs/dir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)fs/isofs/dir.c +2
-1@@ -147,7 +147,8 @@ static int do_isofs_readdir(struct inode *inode, struct file *file, de = tmpde; }/* Basic sanity check, whether name doesn't exceed dir entry */ -if (de_len < de->name_len[0 ] + +if (de_len <sizeof (struct iso_directory_record ) || + de_len < de->name_len[0 ] +sizeof (struct iso_directory_record )) { printk(KERN_NOTICE "iso9660: Corrupted directory entry" " in block %lu of inode %lu\n", block,Signature · 1 line
2.39.5
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR