aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2018-03-23 13:06:51 -0400
committerDarrick J. Wong <darrick.wong@oracle.com>2018-03-23 21:05:07 -0400
commit6915ef35c0350e87a104cb4c4ab2121c81ca7a34 (patch)
treeb69db4b59599ceb73769e1c593326a9707ba9998
parenta27ba2607e60312554cbcd43fc660b2c7f29dc9c (diff)
xfs: sanity-check the unused space before trying to use it
In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if it doesn't make sense. Since a carefully crafted fuzzed image can cause the kernel to crash after blowing a bunch of assertions, let's move those checks into a validator function and rig everything up to return EFSCORRUPTED to userspace. Found by lastbit fuzzing ltail.bestcount via xfs/391. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Brian Foster <bfoster@redhat.com>
-rw-r--r--fs/xfs/libxfs/xfs_dir2.h2
-rw-r--r--fs/xfs/libxfs/xfs_dir2_block.c59
-rw-r--r--fs/xfs/libxfs/xfs_dir2_data.c78
-rw-r--r--fs/xfs/libxfs/xfs_dir2_leaf.c10
-rw-r--r--fs/xfs/libxfs/xfs_dir2_node.c11
5 files changed, 111 insertions, 49 deletions
diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index 388d67c5c903..989e95a53db2 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
173extern void xfs_dir2_data_make_free(struct xfs_da_args *args, 173extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
174 struct xfs_buf *bp, xfs_dir2_data_aoff_t offset, 174 struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
175 xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp); 175 xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
176extern void xfs_dir2_data_use_free(struct xfs_da_args *args, 176extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
177 struct xfs_buf *bp, struct xfs_dir2_data_unused *dup, 177 struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
178 xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len, 178 xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
179 int *needlogp, int *needscanp); 179 int *needlogp, int *needscanp);
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index 2da86a394bcf..875893ded514 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -451,15 +451,19 @@ xfs_dir2_block_addname(
451 * No stale entries, will use enddup space to hold new leaf. 451 * No stale entries, will use enddup space to hold new leaf.
452 */ 452 */
453 if (!btp->stale) { 453 if (!btp->stale) {
454 xfs_dir2_data_aoff_t aoff;
455
454 /* 456 /*
455 * Mark the space needed for the new leaf entry, now in use. 457 * Mark the space needed for the new leaf entry, now in use.
456 */ 458 */
457 xfs_dir2_data_use_free(args, bp, enddup, 459 aoff = (xfs_dir2_data_aoff_t)((char *)enddup - (char *)hdr +
458 (xfs_dir2_data_aoff_t) 460 be16_to_cpu(enddup->length) - sizeof(*blp));
459 ((char *)enddup - (char *)hdr + be16_to_cpu(enddup->length) - 461 error = xfs_dir2_data_use_free(args, bp, enddup, aoff,
460 sizeof(*blp)), 462 (xfs_dir2_data_aoff_t)sizeof(*blp), &needlog,
461 (xfs_dir2_data_aoff_t)sizeof(*blp), 463 &needscan);
462 &needlog, &needscan); 464 if (error)
465 return error;
466
463 /* 467 /*
464 * Update the tail (entry count). 468 * Update the tail (entry count).
465 */ 469 */
@@ -541,9 +545,11 @@ xfs_dir2_block_addname(
541 /* 545 /*
542 * Mark space for the data entry used. 546 * Mark space for the data entry used.
543 */ 547 */
544 xfs_dir2_data_use_free(args, bp, dup, 548 error = xfs_dir2_data_use_free(args, bp, dup,
545 (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), 549 (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
546 (xfs_dir2_data_aoff_t)len, &needlog, &needscan); 550 (xfs_dir2_data_aoff_t)len, &needlog, &needscan);
551 if (error)
552 return error;
547 /* 553 /*
548 * Create the new data entry. 554 * Create the new data entry.
549 */ 555 */
@@ -997,8 +1003,10 @@ xfs_dir2_leaf_to_block(
997 /* 1003 /*
998 * Use up the space at the end of the block (blp/btp). 1004 * Use up the space at the end of the block (blp/btp).
999 */ 1005 */
1000 xfs_dir2_data_use_free(args, dbp, dup, args->geo->blksize - size, size, 1006 error = xfs_dir2_data_use_free(args, dbp, dup,
1001 &needlog, &needscan); 1007 args->geo->blksize - size, size, &needlog, &needscan);
1008 if (error)
1009 return error;
1002 /* 1010 /*
1003 * Initialize the block tail. 1011 * Initialize the block tail.
1004 */ 1012 */
@@ -1110,18 +1118,14 @@ xfs_dir2_sf_to_block(
1110 * Add block 0 to the inode. 1118 * Add block 0 to the inode.
1111 */ 1119 */
1112 error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno); 1120 error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
1113 if (error) { 1121 if (error)
1114 kmem_free(sfp); 1122 goto out_free;
1115 return error;
1116 }
1117 /* 1123 /*
1118 * Initialize the data block, then convert it to block format. 1124 * Initialize the data block, then convert it to block format.
1119 */ 1125 */
1120 error = xfs_dir3_data_init(args, blkno, &bp); 1126 error = xfs_dir3_data_init(args, blkno, &bp);
1121 if (error) { 1127 if (error)
1122 kmem_free(sfp); 1128 goto out_free;
1123 return error;
1124 }
1125 xfs_dir3_block_init(mp, tp, bp, dp); 1129 xfs_dir3_block_init(mp, tp, bp, dp);
1126 hdr = bp->b_addr; 1130 hdr = bp->b_addr;
1127 1131
@@ -1136,8 +1140,10 @@ xfs_dir2_sf_to_block(
1136 */ 1140 */
1137 dup = dp->d_ops->data_unused_p(hdr); 1141 dup = dp->d_ops->data_unused_p(hdr);
1138 needlog = needscan = 0; 1142 needlog = needscan = 0;
1139 xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i, 1143 error = xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
1140 i, &needlog, &needscan); 1144 i, &needlog, &needscan);
1145 if (error)
1146 goto out_free;
1141 ASSERT(needscan == 0); 1147 ASSERT(needscan == 0);
1142 /* 1148 /*
1143 * Fill in the tail. 1149 * Fill in the tail.
@@ -1150,9 +1156,11 @@ xfs_dir2_sf_to_block(
1150 /* 1156 /*
1151 * Remove the freespace, we'll manage it. 1157 * Remove the freespace, we'll manage it.
1152 */ 1158 */
1153 xfs_dir2_data_use_free(args, bp, dup, 1159 error = xfs_dir2_data_use_free(args, bp, dup,
1154 (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), 1160 (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
1155 be16_to_cpu(dup->length), &needlog, &needscan); 1161 be16_to_cpu(dup->length), &needlog, &needscan);
1162 if (error)
1163 goto out_free;
1156 /* 1164 /*
1157 * Create entry for . 1165 * Create entry for .
1158 */ 1166 */
@@ -1256,4 +1264,7 @@ xfs_dir2_sf_to_block(
1256 xfs_dir2_block_log_tail(tp, bp); 1264 xfs_dir2_block_log_tail(tp, bp);
1257 xfs_dir3_data_check(dp, bp); 1265 xfs_dir3_data_check(dp, bp);
1258 return 0; 1266 return 0;
1267out_free:
1268 kmem_free(sfp);
1269 return error;
1259} 1270}
diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
index 920279485275..cb67ec730b9b 100644
--- a/fs/xfs/libxfs/xfs_dir2_data.c
+++ b/fs/xfs/libxfs/xfs_dir2_data.c
@@ -932,10 +932,51 @@ xfs_dir2_data_make_free(
932 *needscanp = needscan; 932 *needscanp = needscan;
933} 933}
934 934
935/* Check our free data for obvious signs of corruption. */
936static inline xfs_failaddr_t
937xfs_dir2_data_check_free(
938 struct xfs_dir2_data_hdr *hdr,
939 struct xfs_dir2_data_unused *dup,
940 xfs_dir2_data_aoff_t offset,
941 xfs_dir2_data_aoff_t len)
942{
943 if (hdr->magic != cpu_to_be32(XFS_DIR2_DATA_MAGIC) &&
944 hdr->magic != cpu_to_be32(XFS_DIR3_DATA_MAGIC) &&
945 hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) &&
946 hdr->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
947 return __this_address;
948 if (be16_to_cpu(dup->freetag) != XFS_DIR2_DATA_FREE_TAG)
949 return __this_address;
950 if (offset < (char *)dup - (char *)hdr)
951 return __this_address;
952 if (offset + len > (char *)dup + be16_to_cpu(dup->length) - (char *)hdr)
953 return __this_address;
954 if ((char *)dup - (char *)hdr !=
955 be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
956 return __this_address;
957 return NULL;
958}
959
960/* Sanity-check a new bestfree entry. */
961static inline xfs_failaddr_t
962xfs_dir2_data_check_new_free(
963 struct xfs_dir2_data_hdr *hdr,
964 struct xfs_dir2_data_free *dfp,
965 struct xfs_dir2_data_unused *newdup)
966{
967 if (dfp == NULL)
968 return __this_address;
969 if (dfp->length != newdup->length)
970 return __this_address;
971 if (be16_to_cpu(dfp->offset) != (char *)newdup - (char *)hdr)
972 return __this_address;
973 return NULL;
974}
975
935/* 976/*
936 * Take a byte range out of an existing unused space and make it un-free. 977 * Take a byte range out of an existing unused space and make it un-free.
937 */ 978 */
938void 979int
939xfs_dir2_data_use_free( 980xfs_dir2_data_use_free(
940 struct xfs_da_args *args, 981 struct xfs_da_args *args,
941 struct xfs_buf *bp, 982 struct xfs_buf *bp,
@@ -947,23 +988,19 @@ xfs_dir2_data_use_free(
947{ 988{
948 xfs_dir2_data_hdr_t *hdr; /* data block header */ 989 xfs_dir2_data_hdr_t *hdr; /* data block header */
949 xfs_dir2_data_free_t *dfp; /* bestfree pointer */ 990 xfs_dir2_data_free_t *dfp; /* bestfree pointer */
991 xfs_dir2_data_unused_t *newdup; /* new unused entry */
992 xfs_dir2_data_unused_t *newdup2; /* another new unused entry */
993 struct xfs_dir2_data_free *bf;
994 xfs_failaddr_t fa;
950 int matchback; /* matches end of freespace */ 995 int matchback; /* matches end of freespace */
951 int matchfront; /* matches start of freespace */ 996 int matchfront; /* matches start of freespace */
952 int needscan; /* need to regen bestfree */ 997 int needscan; /* need to regen bestfree */
953 xfs_dir2_data_unused_t *newdup; /* new unused entry */
954 xfs_dir2_data_unused_t *newdup2; /* another new unused entry */
955 int oldlen; /* old unused entry's length */ 998 int oldlen; /* old unused entry's length */
956 struct xfs_dir2_data_free *bf;
957 999
958 hdr = bp->b_addr; 1000 hdr = bp->b_addr;
959 ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) || 1001 fa = xfs_dir2_data_check_free(hdr, dup, offset, len);
960 hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) || 1002 if (fa)
961 hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) || 1003 goto corrupt;
962 hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
963 ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG);
964 ASSERT(offset >= (char *)dup - (char *)hdr);
965 ASSERT(offset + len <= (char *)dup + be16_to_cpu(dup->length) - (char *)hdr);
966 ASSERT((char *)dup - (char *)hdr == be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)));
967 /* 1004 /*
968 * Look up the entry in the bestfree table. 1005 * Look up the entry in the bestfree table.
969 */ 1006 */
@@ -1008,9 +1045,9 @@ xfs_dir2_data_use_free(
1008 xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp); 1045 xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
1009 dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup, 1046 dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
1010 needlogp); 1047 needlogp);
1011 ASSERT(dfp != NULL); 1048 fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
1012 ASSERT(dfp->length == newdup->length); 1049 if (fa)
1013 ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr); 1050 goto corrupt;
1014 /* 1051 /*
1015 * If we got inserted at the last slot, 1052 * If we got inserted at the last slot,
1016 * that means we don't know if there was a better 1053 * that means we don't know if there was a better
@@ -1036,9 +1073,9 @@ xfs_dir2_data_use_free(
1036 xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp); 1073 xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
1037 dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup, 1074 dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
1038 needlogp); 1075 needlogp);
1039 ASSERT(dfp != NULL); 1076 fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
1040 ASSERT(dfp->length == newdup->length); 1077 if (fa)
1041 ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr); 1078 goto corrupt;
1042 /* 1079 /*
1043 * If we got inserted at the last slot, 1080 * If we got inserted at the last slot,
1044 * that means we don't know if there was a better 1081 * that means we don't know if there was a better
@@ -1084,6 +1121,11 @@ xfs_dir2_data_use_free(
1084 } 1121 }
1085 } 1122 }
1086 *needscanp = needscan; 1123 *needscanp = needscan;
1124 return 0;
1125corrupt:
1126 xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount,
1127 hdr, __FILE__, __LINE__, fa);
1128 return -EFSCORRUPTED;
1087} 1129}
1088 1130
1089/* Find the end of the entry data in a data/block format dir block. */ 1131/* Find the end of the entry data in a data/block format dir block. */
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index d61d52da95a1..50fc9c0c5e2b 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -877,9 +877,13 @@ xfs_dir2_leaf_addname(
877 /* 877 /*
878 * Mark the initial part of our freespace in use for the new entry. 878 * Mark the initial part of our freespace in use for the new entry.
879 */ 879 */
880 xfs_dir2_data_use_free(args, dbp, dup, 880 error = xfs_dir2_data_use_free(args, dbp, dup,
881 (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length, 881 (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
882 &needlog, &needscan); 882 length, &needlog, &needscan);
883 if (error) {
884 xfs_trans_brelse(tp, lbp);
885 return error;
886 }
883 /* 887 /*
884 * Initialize our new entry (at last). 888 * Initialize our new entry (at last).
885 */ 889 */
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 0839ffe27e02..9df096cc3c37 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -1729,6 +1729,7 @@ xfs_dir2_node_addname_int(
1729 __be16 *bests; 1729 __be16 *bests;
1730 struct xfs_dir3_icfree_hdr freehdr; 1730 struct xfs_dir3_icfree_hdr freehdr;
1731 struct xfs_dir2_data_free *bf; 1731 struct xfs_dir2_data_free *bf;
1732 xfs_dir2_data_aoff_t aoff;
1732 1733
1733 dp = args->dp; 1734 dp = args->dp;
1734 mp = dp->i_mount; 1735 mp = dp->i_mount;
@@ -2023,9 +2024,13 @@ xfs_dir2_node_addname_int(
2023 /* 2024 /*
2024 * Mark the first part of the unused space, inuse for us. 2025 * Mark the first part of the unused space, inuse for us.
2025 */ 2026 */
2026 xfs_dir2_data_use_free(args, dbp, dup, 2027 aoff = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
2027 (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length, 2028 error = xfs_dir2_data_use_free(args, dbp, dup, aoff, length,
2028 &needlog, &needscan); 2029 &needlog, &needscan);
2030 if (error) {
2031 xfs_trans_brelse(tp, dbp);
2032 return error;
2033 }
2029 /* 2034 /*
2030 * Fill in the new entry and log it. 2035 * Fill in the new entry and log it.
2031 */ 2036 */