[6/8] fs/ntfs3: Add more attributes checks in mi_enum_attr()

Message ID 18640b25-5018-ebf2-38d9-e750404cb66f@paragon-software.com
State New
Headers
Series fs/ntfs3: Bugfix and refactoring |

Commit Message

Konstantin Komarov July 3, 2023, 7:27 a.m. UTC
  Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
---
  fs/ntfs3/record.c | 68 ++++++++++++++++++++++++++++++++++++-----------
  1 file changed, 52 insertions(+), 16 deletions(-)

          u32 total = le32_to_cpu(rec->total);
@@ -219,6 +220,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, 
struct ATTRIB *attr)
          if (!is_rec_inuse(rec))
              return NULL;

+        prev_type = 0;
          attr = Add2Ptr(rec, off);
      } else {
          /* Check if input attr inside record. */
@@ -232,11 +234,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, 
struct ATTRIB *attr)
              return NULL;
          }

-        if (off + asize < off) {
-            /* Overflow check. */
+        /* Overflow check. */
+        if (off + asize < off)
              return NULL;
-        }

+        prev_type = le32_to_cpu(attr->type);
          attr = Add2Ptr(attr, asize);
          off += asize;
      }
@@ -256,7 +258,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, 
struct ATTRIB *attr)

      /* 0x100 is last known attribute for now. */
      t32 = le32_to_cpu(attr->type);
-    if ((t32 & 0xf) || (t32 > 0x100))
+    if (!t32 || (t32 & 0xf) || (t32 > 0x100))
+        return NULL;
+
+    /* attributes in record must be ordered by type */
+    if (t32 < prev_type)
          return NULL;

      /* Check overflow and boundary. */
@@ -265,16 +271,15 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, 
struct ATTRIB *attr)

      /* Check size of attribute. */
      if (!attr->non_res) {
+        /* Check resident fields. */
          if (asize < SIZEOF_RESIDENT)
              return NULL;

          t16 = le16_to_cpu(attr->res.data_off);
-
          if (t16 > asize)
              return NULL;

-        t32 = le32_to_cpu(attr->res.data_size);
-        if (t16 + t32 > asize)
+        if (t16 + le32_to_cpu(attr->res.data_size) > asize)
              return NULL;

          t32 = sizeof(short) * attr->name_len;
@@ -284,21 +289,52 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, 
struct ATTRIB *attr)
          return attr;
      }

-    /* Check some nonresident fields. */
-    if (attr->name_len &&
-        le16_to_cpu(attr->name_off) + sizeof(short) * attr->name_len >
-            le16_to_cpu(attr->nres.run_off)) {
+    /* Check nonresident fields. */
+    if (attr->non_res != 1)
+        return NULL;
+
+    t16 = le16_to_cpu(attr->nres.run_off);
+    if (t16 > asize)
+        return NULL;
+
+    t32 = sizeof(short) * attr->name_len;
+    if (t32 && le16_to_cpu(attr->name_off) + t32 > t16)
+        return NULL;
+
+    /* Check start/end vcn. */
+    if (le64_to_cpu(attr->nres.svcn) > le64_to_cpu(attr->nres.evcn) + 1)
+        return NULL;
+
+    data_size = le64_to_cpu(attr->nres.data_size);
+    if (le64_to_cpu(attr->nres.valid_size) > data_size)
          return NULL;
-    }

-    if (attr->nres.svcn || !is_attr_ext(attr)) {
+    alloc_size = le64_to_cpu(attr->nres.alloc_size);
+    if (data_size > alloc_size)
+        return NULL;
+
+    t32 = mi->sbi->cluster_mask;
+    if (alloc_size & t32)
+        return NULL;
+
+    if (!attr->nres.svcn && is_attr_ext(attr)) {
+        /* First segment of sparse/compressed attribute */
+        if (asize + 8 < SIZEOF_NONRESIDENT_EX)
+            return NULL;
+
+        tot_size = le64_to_cpu(attr->nres.total_size);
+        if (tot_size & t32)
+            return NULL;
+
+        if (tot_size > alloc_size)
+            return NULL;
+    } else {
          if (asize + 8 < SIZEOF_NONRESIDENT)
              return NULL;

          if (attr->nres.c_unit)
              return NULL;
-    } else if (asize + 8 < SIZEOF_NONRESIDENT_EX)
-        return NULL;
+    }

      return attr;
  }
  

Patch

diff --git a/fs/ntfs3/record.c b/fs/ntfs3/record.c
index cae939cb42cf..53629b1f65e9 100644
--- a/fs/ntfs3/record.c
+++ b/fs/ntfs3/record.c
@@ -199,8 +199,9 @@  struct ATTRIB *mi_enum_attr(struct mft_inode *mi, 
struct ATTRIB *attr)
  {
      const struct MFT_REC *rec = mi->mrec;
      u32 used = le32_to_cpu(rec->used);
-    u32 t32, off, asize;
+    u32 t32, off, asize, prev_type;
      u16 t16;
+    u64 data_size, alloc_size, tot_size;

      if (!attr) {