From 2701739f8ad80d14c36de1e3a7a35bd792fceabb Mon Sep 17 00:00:00 2001
From: Joachim Metz <joachim.metz@gmail.com>
Date: Wed, 28 Apr 2021 09:40:47 +0200
Subject: [PATCH] Fixed OOB reads in hfs_cat_traverse #1401

---
 tsk/fs/hfs.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 43 insertions(+), 3 deletions(-)

diff --git a/tsk/fs/hfs.c b/tsk/fs/hfs.c
index 22618534ee..003db5a950 100644
--- a/tsk/fs/hfs.c
+++ b/tsk/fs/hfs.c
@@ -469,6 +469,16 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
                 size_t rec_off;
                 hfs_btree_key_ext *key;
 
+                // Make sure node is large enough, note that (rec + 1) * 2 is an offset
+                // relative to the end of node
+                if ((rec + 1) * 2 > (int) nodesize) {
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr
+                        ("hfs_ext_find_extent_record: offset of record %d in leaf node %d too small (%"
+                        PRIu16 ")", rec, cur_node, nodesize);
+                    free(node);
+                    return 1;
+                }
                 // get the record offset in the node
                 rec_off =
                     tsk_getu16(fs->endian,
@@ -554,11 +564,21 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
                 int keylen;
                 TSK_FS_ATTR_RUN *attr_run;
 
+                // Make sure node is large enough, note that (rec + 1) * 2 is an offset
+                // relative to the end of node
+                if ((rec + 1) * 2 > (int) nodesize) {
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr
+                        ("hfs_ext_find_extent_record_attr: offset of record %d in leaf node %d too small (%"
+                        PRIu16 ")", rec, cur_node, nodesize);
+                    free(node);
+                    return 1;
+                }
                 // get the record offset in the node
                 rec_off =
                     tsk_getu16(fs->endian,
                     &node[nodesize - (rec + 1) * 2]);
-                if (rec_off > nodesize) {
+                if (rec_off >= nodesize) {
                     tsk_error_set_errno(TSK_ERR_FS_GENFS);
                     tsk_error_set_errstr
                         ("hfs_ext_find_extent_record_attr: offset of record %d in leaf node %d too large (%d vs %"
@@ -821,11 +841,21 @@ hfs_cat_traverse(HFS_INFO * hfs,
                 uint8_t retval;
                 int keylen;
 
+                // Make sure node is large enough, note that (rec + 1) * 2 is an offset
+                // relative to the end of node
+                if ((rec + 1) * 2 > (int) nodesize) {
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr
+                        ("hfs_cat_traverse: offset of record %d in leaf node %d too small (%"
+                        PRIu16 ")", rec, cur_node, nodesize);
+                    free(node);
+                    return 1;
+                }
                 // get the record offset in the node
                 rec_off =
                     tsk_getu16(fs->endian,
                     &node[nodesize - (rec + 1) * 2]);
-                if (rec_off > nodesize) {
+                if (rec_off >= nodesize) {
                     tsk_error_set_errno(TSK_ERR_FS_GENFS);
                     tsk_error_set_errstr
                         ("hfs_cat_traverse: offset of record %d in index node %d too large (%d vs %"
@@ -931,11 +961,21 @@ hfs_cat_traverse(HFS_INFO * hfs,
                 uint8_t retval;
                 int keylen;
 
+                // Make sure node is large enough, note that (rec + 1) * 2 is an offset
+                // relative to the end of node
+                if ((rec + 1) * 2 > (int) nodesize) {
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr
+                        ("hfs_cat_traverse: offset of record %d in leaf node %d too small (%"
+                        PRIu16 ")", rec, cur_node, nodesize);
+                    free(node);
+                    return 1;
+                }
                 // get the record offset in the node
                 rec_off =
                     tsk_getu16(fs->endian,
                     &node[nodesize - (rec + 1) * 2]);
-                if (rec_off > nodesize) {
+                if (rec_off >= nodesize) {
                     tsk_error_set_errno(TSK_ERR_FS_GENFS);
                     tsk_error_set_errstr
                         ("hfs_cat_traverse: offset of record %d in leaf node %d too large (%d vs %"
