|
|
9257a6 |
commit ab1c367459b5fd71eb80ad40645f25c21b95e70d
|
|
|
9257a6 |
Author: Andrew Price <anprice@redhat.com>
|
|
|
9257a6 |
Date: Fri Oct 18 16:07:44 2019 +0100
|
|
|
9257a6 |
|
|
|
9257a6 |
fsck.gfs2: Fix segfault in build_and_check_metalist
|
|
|
9257a6 |
|
|
|
9257a6 |
In unlikely circumstances, indirect pointer corruption in a 'system'
|
|
|
9257a6 |
inode's metadata tree can lead to the inode block state being marked as
|
|
|
9257a6 |
'free' in pass1, which causes build_and_check_metalist() to be called in
|
|
|
9257a6 |
pass 2. The pass has a NULL ->check_metalist function pointer and so a
|
|
|
9257a6 |
segfault occurs when build_and_check_metalist attempts to call it.
|
|
|
9257a6 |
|
|
|
9257a6 |
Fix the segfault by calling ->check_metalist() only when it's not NULL.
|
|
|
9257a6 |
This required some refactoring to make the extra level of if-nesting
|
|
|
9257a6 |
easier to implement and read.
|
|
|
9257a6 |
|
|
|
9257a6 |
Resolves: rhbz#1487726
|
|
|
9257a6 |
|
|
|
9257a6 |
Signed-off-by: Andrew Price <anprice@redhat.com>
|
|
|
9257a6 |
|
|
|
9257a6 |
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
|
|
|
9257a6 |
index 4d6a2d2c..dbe9f1f6 100644
|
|
|
9257a6 |
--- a/gfs2/fsck/metawalk.c
|
|
|
9257a6 |
+++ b/gfs2/fsck/metawalk.c
|
|
|
9257a6 |
@@ -1207,6 +1207,51 @@ static void file_ra(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
|
|
|
9257a6 |
extlen * sdp->bsize, POSIX_FADV_WILLNEED);
|
|
|
9257a6 |
}
|
|
|
9257a6 |
|
|
|
9257a6 |
+static int do_check_metalist(struct gfs2_inode *ip, uint64_t block, int height,
|
|
|
9257a6 |
+ struct gfs2_buffer_head **bhp, struct metawalk_fxns *pass)
|
|
|
9257a6 |
+{
|
|
|
9257a6 |
+ int was_duplicate = 0;
|
|
|
9257a6 |
+ int is_valid = 1;
|
|
|
9257a6 |
+ int error;
|
|
|
9257a6 |
+
|
|
|
9257a6 |
+ if (pass->check_metalist == NULL)
|
|
|
9257a6 |
+ return 0;
|
|
|
9257a6 |
+
|
|
|
9257a6 |
+ error = pass->check_metalist(ip, block, bhp, height, &is_valid,
|
|
|
9257a6 |
+ &was_duplicate, pass->private);
|
|
|
9257a6 |
+ if (error == meta_error) {
|
|
|
9257a6 |
+ stack;
|
|
|
9257a6 |
+ log_info("\n");
|
|
|
9257a6 |
+ log_info(_("Serious metadata error on block %"PRIu64" (0x%"PRIx64").\n"),
|
|
|
9257a6 |
+ block, block);
|
|
|
9257a6 |
+ return error;
|
|
|
9257a6 |
+ }
|
|
|
9257a6 |
+ if (error == meta_skip_further) {
|
|
|
9257a6 |
+ log_info("\n");
|
|
|
9257a6 |
+ log_info(_("Unrecoverable metadata error on block %"PRIu64" (0x%"PRIx64")\n"),
|
|
|
9257a6 |
+ block, block);
|
|
|
9257a6 |
+ log_info(_("Further metadata will be skipped.\n"));
|
|
|
9257a6 |
+ return error;
|
|
|
9257a6 |
+ }
|
|
|
9257a6 |
+ if (!is_valid) {
|
|
|
9257a6 |
+ log_debug("Skipping rejected block %"PRIu64" (0x%"PRIx64")\n", block, block);
|
|
|
9257a6 |
+ if (pass->invalid_meta_is_fatal)
|
|
|
9257a6 |
+ return meta_error;
|
|
|
9257a6 |
+ return meta_skip_one;
|
|
|
9257a6 |
+ }
|
|
|
9257a6 |
+ if (was_duplicate) {
|
|
|
9257a6 |
+ log_debug("Skipping duplicate %"PRIu64" (0x%"PRIx64")\n", block, block);
|
|
|
9257a6 |
+ return meta_skip_one;
|
|
|
9257a6 |
+ }
|
|
|
9257a6 |
+ if (!valid_block_ip(ip, block)) {
|
|
|
9257a6 |
+ log_debug("Skipping invalid block %"PRIu64" (0x%"PRIx64")\n", block, block);
|
|
|
9257a6 |
+ if (pass->invalid_meta_is_fatal)
|
|
|
9257a6 |
+ return meta_error;
|
|
|
9257a6 |
+ return meta_skip_one;
|
|
|
9257a6 |
+ }
|
|
|
9257a6 |
+ return error;
|
|
|
9257a6 |
+}
|
|
|
9257a6 |
+
|
|
|
9257a6 |
/**
|
|
|
9257a6 |
* build_and_check_metalist - check a bunch of indirect blocks
|
|
|
9257a6 |
* This includes hash table blocks for directories
|
|
|
9257a6 |
@@ -1226,8 +1271,8 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
|
|
|
9257a6 |
osi_list_t *prev_list, *cur_list, *tmp;
|
|
|
9257a6 |
int h, head_size, iblk_type;
|
|
|
9257a6 |
uint64_t *ptr, block, *undoptr;
|
|
|
9257a6 |
- int error, was_duplicate, is_valid;
|
|
|
9257a6 |
int maxptrs;
|
|
|
9257a6 |
+ int error;
|
|
|
9257a6 |
|
|
|
9257a6 |
osi_list_add(&metabh->b_altlist, &mlp[0]);
|
|
|
9257a6 |
|
|
|
9257a6 |
@@ -1291,65 +1336,11 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
|
|
|
9257a6 |
continue;
|
|
|
9257a6 |
|
|
|
9257a6 |
block = be64_to_cpu(*ptr);
|
|
|
9257a6 |
- was_duplicate = 0;
|
|
|
9257a6 |
- error = pass->check_metalist(ip, block, &nbh,
|
|
|
9257a6 |
- h, &is_valid,
|
|
|
9257a6 |
- &was_duplicate,
|
|
|
9257a6 |
- pass->private);
|
|
|
9257a6 |
- /* check_metalist should hold any buffers
|
|
|
9257a6 |
- it gets with "bread". */
|
|
|
9257a6 |
- if (error == meta_error) {
|
|
|
9257a6 |
- stack;
|
|
|
9257a6 |
- log_info(_("\nSerious metadata "
|
|
|
9257a6 |
- "error on block %llu "
|
|
|
9257a6 |
- "(0x%llx).\n"),
|
|
|
9257a6 |
- (unsigned long long)block,
|
|
|
9257a6 |
- (unsigned long long)block);
|
|
|
9257a6 |
+ error = do_check_metalist(ip, block, h, &nbh, pass);
|
|
|
9257a6 |
+ if (error == meta_error || error == meta_skip_further)
|
|
|
9257a6 |
goto error_undo;
|
|
|
9257a6 |
- }
|
|
|
9257a6 |
- if (error == meta_skip_further) {
|
|
|
9257a6 |
- log_info(_("\nUnrecoverable metadata "
|
|
|
9257a6 |
- "error on block %llu "
|
|
|
9257a6 |
- "(0x%llx). Further metadata"
|
|
|
9257a6 |
- " will be skipped.\n"),
|
|
|
9257a6 |
- (unsigned long long)block,
|
|
|
9257a6 |
- (unsigned long long)block);
|
|
|
9257a6 |
- goto error_undo;
|
|
|
9257a6 |
- }
|
|
|
9257a6 |
- if (!is_valid) {
|
|
|
9257a6 |
- log_debug( _("Skipping rejected block "
|
|
|
9257a6 |
- "%llu (0x%llx)\n"),
|
|
|
9257a6 |
- (unsigned long long)block,
|
|
|
9257a6 |
- (unsigned long long)block);
|
|
|
9257a6 |
- if (pass->invalid_meta_is_fatal) {
|
|
|
9257a6 |
- error = meta_error;
|
|
|
9257a6 |
- goto error_undo;
|
|
|
9257a6 |
- }
|
|
|
9257a6 |
- continue;
|
|
|
9257a6 |
- }
|
|
|
9257a6 |
- /* Note that there's a special case in which
|
|
|
9257a6 |
- we need to process the metadata block, even
|
|
|
9257a6 |
- if it was a duplicate. That's for cases
|
|
|
9257a6 |
- where we deleted the last reference as
|
|
|
9257a6 |
- metadata. */
|
|
|
9257a6 |
- if (was_duplicate) {
|
|
|
9257a6 |
- log_debug( _("Skipping duplicate %llu "
|
|
|
9257a6 |
- "(0x%llx)\n"),
|
|
|
9257a6 |
- (unsigned long long)block,
|
|
|
9257a6 |
- (unsigned long long)block);
|
|
|
9257a6 |
+ if (error == meta_skip_one)
|
|
|
9257a6 |
continue;
|
|
|
9257a6 |
- }
|
|
|
9257a6 |
- if (!valid_block_ip(ip, block)) {
|
|
|
9257a6 |
- log_debug( _("Skipping invalid block "
|
|
|
9257a6 |
- "%lld (0x%llx)\n"),
|
|
|
9257a6 |
- (unsigned long long)block,
|
|
|
9257a6 |
- (unsigned long long)block);
|
|
|
9257a6 |
- if (pass->invalid_meta_is_fatal) {
|
|
|
9257a6 |
- error = meta_error;
|
|
|
9257a6 |
- goto error_undo;
|
|
|
9257a6 |
- }
|
|
|
9257a6 |
- continue;
|
|
|
9257a6 |
- }
|
|
|
9257a6 |
if (!nbh)
|
|
|
9257a6 |
nbh = bread(ip->i_sbd, block);
|
|
|
9257a6 |
osi_list_add_prev(&nbh->b_altlist, cur_list);
|
|
|
9257a6 |
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
|
|
|
9257a6 |
index 119efeed..b5a037a3 100644
|
|
|
9257a6 |
--- a/gfs2/fsck/metawalk.h
|
|
|
9257a6 |
+++ b/gfs2/fsck/metawalk.h
|
|
|
9257a6 |
@@ -39,6 +39,7 @@ enum meta_check_rc {
|
|
|
9257a6 |
meta_error = -1,
|
|
|
9257a6 |
meta_is_good = 0,
|
|
|
9257a6 |
meta_skip_further = 1,
|
|
|
9257a6 |
+ meta_skip_one = 2,
|
|
|
9257a6 |
};
|
|
|
9257a6 |
|
|
|
9257a6 |
/* metawalk_fxns: function pointers to check various parts of the fs
|