Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ntfs: add bound checking to ntfs_external_attr_find

Add bound validation in ntfs_external_attr_find to
prevent out-of-bounds memory accesses. This ensures
that the attribute record's length, name offset, and
both resident and non-resident value offsets strictly
fall within the safe boundaries of the MFT record.

Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>

authored by

Hyunchul Lee and committed by
Namjae Jeon
a198a0c4 6ceb4cc8

+69 -14
+69 -14
fs/ntfs/attrib.c
··· 946 946 struct attr_record *a; 947 947 __le16 *al_name; 948 948 u32 al_name_len; 949 + u32 attr_len, mft_free_len; 949 950 bool is_first_search = false; 950 951 int err = 0; 951 952 static const char *es = " Unmount and run chkdsk."; ··· 1210 1209 * with the same meanings as above. 1211 1210 */ 1212 1211 do_next_attr_loop: 1213 - if ((u8 *)a < (u8 *)ctx->mrec || (u8 *)a > (u8 *)ctx->mrec + 1214 - le32_to_cpu(ctx->mrec->bytes_allocated)) 1212 + if ((u8 *)a < (u8 *)ctx->mrec || 1213 + (u8 *)a >= (u8 *)ctx->mrec + le32_to_cpu(ctx->mrec->bytes_allocated) || 1214 + (u8 *)a >= (u8 *)ctx->mrec + le32_to_cpu(ctx->mrec->bytes_in_use)) 1215 1215 break; 1216 - if (a->type == AT_END) 1216 + 1217 + mft_free_len = le32_to_cpu(ctx->mrec->bytes_in_use) - 1218 + ((u8 *)a - (u8 *)ctx->mrec); 1219 + if (mft_free_len >= sizeof(a->type) && a->type == AT_END) 1217 1220 continue; 1218 - if (!a->length) 1221 + 1222 + attr_len = le32_to_cpu(a->length); 1223 + if (!attr_len || 1224 + attr_len < offsetof(struct attr_record, data.resident.reserved) + 1225 + sizeof(a->data.resident.reserved) || 1226 + attr_len > mft_free_len) 1219 1227 break; 1228 + 1220 1229 if (al_entry->instance != a->instance) 1221 1230 goto do_next_attr; 1222 1231 /* ··· 1236 1225 */ 1237 1226 if (al_entry->type != a->type) 1238 1227 break; 1228 + if (a->name_length && ((le16_to_cpu(a->name_offset) + 1229 + a->name_length * sizeof(__le16)) > attr_len)) 1230 + break; 1239 1231 if (!ntfs_are_names_equal((__le16 *)((u8 *)a + 1240 1232 le16_to_cpu(a->name_offset)), a->name_length, 1241 1233 al_name, al_name_len, CASE_SENSITIVE, 1242 1234 vol->upcase, vol->upcase_len)) 1243 1235 break; 1236 + 1244 1237 ctx->attr = a; 1238 + 1239 + if (a->non_resident) { 1240 + u32 min_len; 1241 + u16 mp_offset; 1242 + 1243 + min_len = offsetof(struct attr_record, 1244 + data.non_resident.initialized_size) + 1245 + sizeof(a->data.non_resident.initialized_size); 1246 + 1247 + if (le32_to_cpu(a->length) < min_len) 1248 + break; 1249 + 1250 + mp_offset = 1251 + le16_to_cpu(a->data.non_resident.mapping_pairs_offset); 1252 + if (mp_offset < min_len || mp_offset > attr_len) 1253 + break; 1254 + } 1255 + 1245 1256 /* 1246 1257 * If no @val specified or @val specified and it matches, we 1247 1258 * have found it! 1248 1259 */ 1249 - if ((type == AT_UNUSED) || !val || (!a->non_resident && le32_to_cpu( 1250 - a->data.resident.value_length) == val_len && 1251 - !memcmp((u8 *)a + 1252 - le16_to_cpu(a->data.resident.value_offset), 1253 - val, val_len))) { 1254 - ntfs_debug("Done, found."); 1255 - return 0; 1260 + if ((type == AT_UNUSED) || !val) 1261 + goto attr_found; 1262 + if (!a->non_resident) { 1263 + u32 value_length = le32_to_cpu(a->data.resident.value_length); 1264 + u16 value_offset = le16_to_cpu(a->data.resident.value_offset); 1265 + 1266 + if (attr_len < offsetof(struct attr_record, data.resident.reserved) + 1267 + sizeof(a->data.resident.reserved)) 1268 + break; 1269 + if (value_length > attr_len || value_offset > attr_len - value_length) 1270 + break; 1271 + 1272 + value_length = ntfs_resident_attr_min_value_length(a->type); 1273 + if (value_length && le32_to_cpu(a->data.resident.value_length) < 1274 + value_length) { 1275 + pr_err("Too small resident attribute value in MFT record %lld, type %#x\n", 1276 + (long long)ctx->ntfs_ino->mft_no, a->type); 1277 + break; 1278 + } 1279 + if (value_length == val_len && 1280 + !memcmp((u8 *)a + value_offset, val, val_len)) { 1281 + attr_found: 1282 + ntfs_debug("Done, found."); 1283 + return 0; 1284 + } 1256 1285 } 1257 1286 do_next_attr: 1258 1287 /* Proceed to the next attribute in the current mft record. */ 1259 - a = (struct attr_record *)((u8 *)a + le32_to_cpu(a->length)); 1288 + a = (struct attr_record *)((u8 *)a + attr_len); 1260 1289 goto do_next_attr_loop; 1261 1290 } 1262 1291 ··· 1311 1260 } 1312 1261 1313 1262 if (!err) { 1263 + u64 mft_no = ctx->al_entry ? MREF_LE(ctx->al_entry->mft_reference) : 0; 1264 + u32 type = ctx->al_entry ? le32_to_cpu(ctx->al_entry->type) : 0; 1265 + 1314 1266 ntfs_error(vol->sb, 1315 - "Base inode 0x%llx contains corrupt attribute list attribute.%s", 1316 - base_ni->mft_no, es); 1267 + "Base inode 0x%llx contains corrupt attribute, mft %#llx, type %#x. %s", 1268 + (long long)base_ni->mft_no, (long long)mft_no, type, 1269 + "Unmount and run chkdsk."); 1317 1270 err = -EIO; 1318 1271 } 1319 1272