diff options
author | Jan Kara <jack@suse.cz> | 2013-09-12 16:00:15 -0400 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2013-09-24 05:23:33 -0400 |
commit | 69d75671d985ccdb291e4d19ddfdecd12440e857 (patch) | |
tree | 38e68e78441e81c8b35831fe559ef3fc0f9e2fb6 /fs/udf | |
parent | d8524ae9d6f492a9c6db9f4d89c5f9b8782fa2d5 (diff) |
udf: Fortify LVID loading
A user has reported an oops in udf_statfs() that was caused by
numOfPartitions entry in LVID structure being corrupted. Fix the problem
by verifying whether numOfPartitions makes sense at least to the extent
that LVID fits into a single block as it should.
Reported-by: Juergen Weigert <jw@suse.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/udf')
-rw-r--r-- | fs/udf/ialloc.c | 16 | ||||
-rw-r--r-- | fs/udf/super.c | 64 | ||||
-rw-r--r-- | fs/udf/udf_sb.h | 2 |
3 files changed, 48 insertions, 34 deletions
diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c index 7e5aae4bf46f..6eaf5edf1ea1 100644 --- a/fs/udf/ialloc.c +++ b/fs/udf/ialloc.c | |||
@@ -30,18 +30,17 @@ void udf_free_inode(struct inode *inode) | |||
30 | { | 30 | { |
31 | struct super_block *sb = inode->i_sb; | 31 | struct super_block *sb = inode->i_sb; |
32 | struct udf_sb_info *sbi = UDF_SB(sb); | 32 | struct udf_sb_info *sbi = UDF_SB(sb); |
33 | struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb); | ||
33 | 34 | ||
34 | mutex_lock(&sbi->s_alloc_mutex); | 35 | if (lvidiu) { |
35 | if (sbi->s_lvid_bh) { | 36 | mutex_lock(&sbi->s_alloc_mutex); |
36 | struct logicalVolIntegrityDescImpUse *lvidiu = | ||
37 | udf_sb_lvidiu(sbi); | ||
38 | if (S_ISDIR(inode->i_mode)) | 37 | if (S_ISDIR(inode->i_mode)) |
39 | le32_add_cpu(&lvidiu->numDirs, -1); | 38 | le32_add_cpu(&lvidiu->numDirs, -1); |
40 | else | 39 | else |
41 | le32_add_cpu(&lvidiu->numFiles, -1); | 40 | le32_add_cpu(&lvidiu->numFiles, -1); |
42 | udf_updated_lvid(sb); | 41 | udf_updated_lvid(sb); |
42 | mutex_unlock(&sbi->s_alloc_mutex); | ||
43 | } | 43 | } |
44 | mutex_unlock(&sbi->s_alloc_mutex); | ||
45 | 44 | ||
46 | udf_free_blocks(sb, NULL, &UDF_I(inode)->i_location, 0, 1); | 45 | udf_free_blocks(sb, NULL, &UDF_I(inode)->i_location, 0, 1); |
47 | } | 46 | } |
@@ -55,6 +54,7 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode, int *err) | |||
55 | uint32_t start = UDF_I(dir)->i_location.logicalBlockNum; | 54 | uint32_t start = UDF_I(dir)->i_location.logicalBlockNum; |
56 | struct udf_inode_info *iinfo; | 55 | struct udf_inode_info *iinfo; |
57 | struct udf_inode_info *dinfo = UDF_I(dir); | 56 | struct udf_inode_info *dinfo = UDF_I(dir); |
57 | struct logicalVolIntegrityDescImpUse *lvidiu; | ||
58 | 58 | ||
59 | inode = new_inode(sb); | 59 | inode = new_inode(sb); |
60 | 60 | ||
@@ -92,12 +92,10 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode, int *err) | |||
92 | return NULL; | 92 | return NULL; |
93 | } | 93 | } |
94 | 94 | ||
95 | if (sbi->s_lvid_bh) { | 95 | lvidiu = udf_sb_lvidiu(sb); |
96 | struct logicalVolIntegrityDescImpUse *lvidiu; | 96 | if (lvidiu) { |
97 | |||
98 | iinfo->i_unique = lvid_get_unique_id(sb); | 97 | iinfo->i_unique = lvid_get_unique_id(sb); |
99 | mutex_lock(&sbi->s_alloc_mutex); | 98 | mutex_lock(&sbi->s_alloc_mutex); |
100 | lvidiu = udf_sb_lvidiu(sbi); | ||
101 | if (S_ISDIR(mode)) | 99 | if (S_ISDIR(mode)) |
102 | le32_add_cpu(&lvidiu->numDirs, 1); | 100 | le32_add_cpu(&lvidiu->numDirs, 1); |
103 | else | 101 | else |
diff --git a/fs/udf/super.c b/fs/udf/super.c index 839a2bad7f45..91219385691d 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c | |||
@@ -94,13 +94,25 @@ static unsigned int udf_count_free(struct super_block *); | |||
94 | static int udf_statfs(struct dentry *, struct kstatfs *); | 94 | static int udf_statfs(struct dentry *, struct kstatfs *); |
95 | static int udf_show_options(struct seq_file *, struct dentry *); | 95 | static int udf_show_options(struct seq_file *, struct dentry *); |
96 | 96 | ||
97 | struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct udf_sb_info *sbi) | 97 | struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb) |
98 | { | 98 | { |
99 | struct logicalVolIntegrityDesc *lvid = | 99 | struct logicalVolIntegrityDesc *lvid; |
100 | (struct logicalVolIntegrityDesc *)sbi->s_lvid_bh->b_data; | 100 | unsigned int partnum; |
101 | __u32 number_of_partitions = le32_to_cpu(lvid->numOfPartitions); | 101 | unsigned int offset; |
102 | __u32 offset = number_of_partitions * 2 * | 102 | |
103 | sizeof(uint32_t)/sizeof(uint8_t); | 103 | if (!UDF_SB(sb)->s_lvid_bh) |
104 | return NULL; | ||
105 | lvid = (struct logicalVolIntegrityDesc *)UDF_SB(sb)->s_lvid_bh->b_data; | ||
106 | partnum = le32_to_cpu(lvid->numOfPartitions); | ||
107 | if ((sb->s_blocksize - sizeof(struct logicalVolIntegrityDescImpUse) - | ||
108 | offsetof(struct logicalVolIntegrityDesc, impUse)) / | ||
109 | (2 * sizeof(uint32_t)) < partnum) { | ||
110 | udf_err(sb, "Logical volume integrity descriptor corrupted " | ||
111 | "(numOfPartitions = %u)!\n", partnum); | ||
112 | return NULL; | ||
113 | } | ||
114 | /* The offset is to skip freeSpaceTable and sizeTable arrays */ | ||
115 | offset = partnum * 2 * sizeof(uint32_t); | ||
104 | return (struct logicalVolIntegrityDescImpUse *)&(lvid->impUse[offset]); | 116 | return (struct logicalVolIntegrityDescImpUse *)&(lvid->impUse[offset]); |
105 | } | 117 | } |
106 | 118 | ||
@@ -629,9 +641,10 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options) | |||
629 | struct udf_options uopt; | 641 | struct udf_options uopt; |
630 | struct udf_sb_info *sbi = UDF_SB(sb); | 642 | struct udf_sb_info *sbi = UDF_SB(sb); |
631 | int error = 0; | 643 | int error = 0; |
644 | struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb); | ||
632 | 645 | ||
633 | if (sbi->s_lvid_bh) { | 646 | if (lvidiu) { |
634 | int write_rev = le16_to_cpu(udf_sb_lvidiu(sbi)->minUDFWriteRev); | 647 | int write_rev = le16_to_cpu(lvidiu->minUDFWriteRev); |
635 | if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & MS_RDONLY)) | 648 | if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & MS_RDONLY)) |
636 | return -EACCES; | 649 | return -EACCES; |
637 | } | 650 | } |
@@ -1905,11 +1918,12 @@ static void udf_open_lvid(struct super_block *sb) | |||
1905 | 1918 | ||
1906 | if (!bh) | 1919 | if (!bh) |
1907 | return; | 1920 | return; |
1908 | |||
1909 | mutex_lock(&sbi->s_alloc_mutex); | ||
1910 | lvid = (struct logicalVolIntegrityDesc *)bh->b_data; | 1921 | lvid = (struct logicalVolIntegrityDesc *)bh->b_data; |
1911 | lvidiu = udf_sb_lvidiu(sbi); | 1922 | lvidiu = udf_sb_lvidiu(sb); |
1923 | if (!lvidiu) | ||
1924 | return; | ||
1912 | 1925 | ||
1926 | mutex_lock(&sbi->s_alloc_mutex); | ||
1913 | lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; | 1927 | lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; |
1914 | lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; | 1928 | lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; |
1915 | udf_time_to_disk_stamp(&lvid->recordingDateAndTime, | 1929 | udf_time_to_disk_stamp(&lvid->recordingDateAndTime, |
@@ -1937,10 +1951,12 @@ static void udf_close_lvid(struct super_block *sb) | |||
1937 | 1951 | ||
1938 | if (!bh) | 1952 | if (!bh) |
1939 | return; | 1953 | return; |
1954 | lvid = (struct logicalVolIntegrityDesc *)bh->b_data; | ||
1955 | lvidiu = udf_sb_lvidiu(sb); | ||
1956 | if (!lvidiu) | ||
1957 | return; | ||
1940 | 1958 | ||
1941 | mutex_lock(&sbi->s_alloc_mutex); | 1959 | mutex_lock(&sbi->s_alloc_mutex); |
1942 | lvid = (struct logicalVolIntegrityDesc *)bh->b_data; | ||
1943 | lvidiu = udf_sb_lvidiu(sbi); | ||
1944 | lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; | 1960 | lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; |
1945 | lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; | 1961 | lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; |
1946 | udf_time_to_disk_stamp(&lvid->recordingDateAndTime, CURRENT_TIME); | 1962 | udf_time_to_disk_stamp(&lvid->recordingDateAndTime, CURRENT_TIME); |
@@ -2093,15 +2109,19 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) | |||
2093 | 2109 | ||
2094 | if (sbi->s_lvid_bh) { | 2110 | if (sbi->s_lvid_bh) { |
2095 | struct logicalVolIntegrityDescImpUse *lvidiu = | 2111 | struct logicalVolIntegrityDescImpUse *lvidiu = |
2096 | udf_sb_lvidiu(sbi); | 2112 | udf_sb_lvidiu(sb); |
2097 | uint16_t minUDFReadRev = le16_to_cpu(lvidiu->minUDFReadRev); | 2113 | uint16_t minUDFReadRev; |
2098 | uint16_t minUDFWriteRev = le16_to_cpu(lvidiu->minUDFWriteRev); | 2114 | uint16_t minUDFWriteRev; |
2099 | /* uint16_t maxUDFWriteRev = | ||
2100 | le16_to_cpu(lvidiu->maxUDFWriteRev); */ | ||
2101 | 2115 | ||
2116 | if (!lvidiu) { | ||
2117 | ret = -EINVAL; | ||
2118 | goto error_out; | ||
2119 | } | ||
2120 | minUDFReadRev = le16_to_cpu(lvidiu->minUDFReadRev); | ||
2121 | minUDFWriteRev = le16_to_cpu(lvidiu->minUDFWriteRev); | ||
2102 | if (minUDFReadRev > UDF_MAX_READ_VERSION) { | 2122 | if (minUDFReadRev > UDF_MAX_READ_VERSION) { |
2103 | udf_err(sb, "minUDFReadRev=%x (max is %x)\n", | 2123 | udf_err(sb, "minUDFReadRev=%x (max is %x)\n", |
2104 | le16_to_cpu(lvidiu->minUDFReadRev), | 2124 | minUDFReadRev, |
2105 | UDF_MAX_READ_VERSION); | 2125 | UDF_MAX_READ_VERSION); |
2106 | ret = -EINVAL; | 2126 | ret = -EINVAL; |
2107 | goto error_out; | 2127 | goto error_out; |
@@ -2265,11 +2285,7 @@ static int udf_statfs(struct dentry *dentry, struct kstatfs *buf) | |||
2265 | struct logicalVolIntegrityDescImpUse *lvidiu; | 2285 | struct logicalVolIntegrityDescImpUse *lvidiu; |
2266 | u64 id = huge_encode_dev(sb->s_bdev->bd_dev); | 2286 | u64 id = huge_encode_dev(sb->s_bdev->bd_dev); |
2267 | 2287 | ||
2268 | if (sbi->s_lvid_bh != NULL) | 2288 | lvidiu = udf_sb_lvidiu(sb); |
2269 | lvidiu = udf_sb_lvidiu(sbi); | ||
2270 | else | ||
2271 | lvidiu = NULL; | ||
2272 | |||
2273 | buf->f_type = UDF_SUPER_MAGIC; | 2289 | buf->f_type = UDF_SUPER_MAGIC; |
2274 | buf->f_bsize = sb->s_blocksize; | 2290 | buf->f_bsize = sb->s_blocksize; |
2275 | buf->f_blocks = sbi->s_partmaps[sbi->s_partition].s_partition_len; | 2291 | buf->f_blocks = sbi->s_partmaps[sbi->s_partition].s_partition_len; |
diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index ed401e94aa8c..1f32c7bd9f57 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h | |||
@@ -162,7 +162,7 @@ static inline struct udf_sb_info *UDF_SB(struct super_block *sb) | |||
162 | return sb->s_fs_info; | 162 | return sb->s_fs_info; |
163 | } | 163 | } |
164 | 164 | ||
165 | struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct udf_sb_info *sbi); | 165 | struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb); |
166 | 166 | ||
167 | int udf_compute_nr_groups(struct super_block *sb, u32 partition); | 167 | int udf_compute_nr_groups(struct super_block *sb, u32 partition); |
168 | 168 | ||