@@ -570,6 +570,35 @@ struct runlist_element *ntfs_attr_find_vcn_nolock(struct ntfs_inode *ni, const s
570570 return ERR_PTR (err );
571571}
572572
573+ static u32 ntfs_resident_attr_min_value_length (const __le32 type )
574+ {
575+ switch (type ) {
576+ case AT_STANDARD_INFORMATION :
577+ return offsetof(struct standard_information , ver ) +
578+ sizeof (((struct standard_information * )0 )-> ver .v1 .reserved12 );
579+ case AT_ATTRIBUTE_LIST :
580+ return offsetof(struct attr_list_entry , name );
581+ case AT_FILE_NAME :
582+ return offsetof(struct file_name_attr , file_name );
583+ case AT_OBJECT_ID :
584+ return sizeof (struct guid );
585+ case AT_SECURITY_DESCRIPTOR :
586+ return sizeof (struct security_descriptor_relative );
587+ case AT_VOLUME_INFORMATION :
588+ return sizeof (struct volume_information );
589+ case AT_INDEX_ROOT :
590+ return sizeof (struct index_root );
591+ case AT_REPARSE_POINT :
592+ return offsetof(struct reparse_point , reparse_data );
593+ case AT_EA_INFORMATION :
594+ return sizeof (struct ea_information );
595+ case AT_EA :
596+ return offsetof(struct ea_attr , ea_name ) + 1 ;
597+ default :
598+ return 0 ;
599+ }
600+ }
601+
573602/*
574603 * ntfs_attr_find - find (next) attribute in mft record
575604 * @type: attribute type to find
@@ -712,38 +741,69 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
712741 continue ;
713742 }
714743 }
744+
745+ /* Validate attribute's value offset/length */
746+ if (!a -> non_resident ) {
747+ u32 min_len ;
748+ u32 value_length = le32_to_cpu (a -> data .resident .value_length );
749+ u16 value_offset = le16_to_cpu (a -> data .resident .value_offset );
750+
751+ if (value_length > le32_to_cpu (a -> length ) ||
752+ value_offset > le32_to_cpu (a -> length ) - value_length )
753+ break ;
754+
755+ min_len = ntfs_resident_attr_min_value_length (a -> type );
756+ if (min_len && value_length < min_len ) {
757+ ntfs_error (vol -> sb ,
758+ "Too small %#x resident attribute value in MFT record %lld\n" ,
759+ le32_to_cpu (a -> type ), (long long )ctx -> ntfs_ino -> mft_no );
760+ break ;
761+ }
762+ } else {
763+ u32 min_len ;
764+ u16 mp_offset ;
765+
766+ min_len = offsetof(struct attr_record , data .non_resident .initialized_size ) +
767+ sizeof (a -> data .non_resident .initialized_size );
768+ if (le32_to_cpu (a -> length ) < min_len )
769+ break ;
770+
771+ mp_offset = le16_to_cpu (a -> data .non_resident .mapping_pairs_offset );
772+ if (mp_offset < min_len ||
773+ mp_offset > le32_to_cpu (a -> length ))
774+ break ;
775+ }
776+
715777 /*
716778 * The names match or @name not present and attribute is
717779 * unnamed. If no @val specified, we have found the attribute
718780 * and are done.
719781 */
720- if (!val )
782+ if (!val || a -> non_resident )
721783 return 0 ;
722784 /* @val is present; compare values. */
723785 else {
724- register int rc ;
786+ u32 value_length = le32_to_cpu (a -> data .resident .value_length );
787+ int rc ;
725788
726789 rc = memcmp (val , (u8 * )a + le16_to_cpu (
727790 a -> data .resident .value_offset ),
728- min_t (u32 , val_len , le32_to_cpu (
729- a -> data .resident .value_length )));
791+ min_t (u32 , val_len , value_length ));
730792 /*
731793 * If @val collates before the current attribute's
732794 * value, there is no matching attribute.
733795 */
734796 if (!rc ) {
735- register u32 avl ;
736-
737- avl = le32_to_cpu (a -> data .resident .value_length );
738- if (val_len == avl )
797+ if (val_len == value_length )
739798 return 0 ;
740- if (val_len < avl )
799+ if (val_len < value_length )
741800 return - ENOENT ;
742801 } else if (rc < 0 )
743802 return - ENOENT ;
744803 }
745804 }
746- ntfs_error (vol -> sb , "Inode is corrupt. Run chkdsk." );
805+ ntfs_error (vol -> sb , "mft %#llx, type %#x is corrupt. Run chkdsk." ,
806+ (long long )ctx -> ntfs_ino -> mft_no , le32_to_cpu (type ));
747807 NVolSetErrors (vol );
748808 return - EIO ;
749809}
0 commit comments