Skip to content

Commit 621dc80

Browse files
committed
Merge branch 'guilt/xfs-5.19-recovery-buf-cancel' into xfs-5.19-for-next
As part of solving the memory leaks and UAF problems in the new LARP code, kmemleak also reported that log recovery will leak the table used to hash buffer cancellations if the recovery fails. Fix this problem by creating alloc/free helpers that initialize and free the hashtable contents correctly. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Dave Chinner <david@fromorbit.com>
2 parents 6f5097e + 910bbdf commit 621dc80

File tree

4 files changed

+85
-32
lines changed

4 files changed

+85
-32
lines changed

fs/xfs/libxfs/xfs_log_recover.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,6 @@ struct xlog_recover {
110110

111111
#define ITEM_TYPE(i) (*(unsigned short *)(i)->ri_buf[0].i_addr)
112112

113-
/*
114-
* This is the number of entries in the l_buf_cancel_table used during
115-
* recovery.
116-
*/
117-
#define XLOG_BC_TABLE_SIZE 64
118-
119113
#define XLOG_RECOVER_CRCPASS 0
120114
#define XLOG_RECOVER_PASS1 1
121115
#define XLOG_RECOVER_PASS2 2
@@ -128,5 +122,13 @@ int xlog_recover_iget(struct xfs_mount *mp, xfs_ino_t ino,
128122
struct xfs_inode **ipp);
129123
void xlog_recover_release_intent(struct xlog *log, unsigned short intent_type,
130124
uint64_t intent_id);
125+
int xlog_alloc_buf_cancel_table(struct xlog *log);
126+
void xlog_free_buf_cancel_table(struct xlog *log);
127+
128+
#ifdef DEBUG
129+
void xlog_check_buf_cancel_table(struct xlog *log);
130+
#else
131+
#define xlog_check_buf_cancel_table(log) do { } while (0)
132+
#endif
131133

132134
#endif /* __XFS_LOG_RECOVER_H__ */

fs/xfs/xfs_buf_item_recover.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@
2323
#include "xfs_dir2.h"
2424
#include "xfs_quota.h"
2525

26+
/*
27+
* This is the number of entries in the l_buf_cancel_table used during
28+
* recovery.
29+
*/
30+
#define XLOG_BC_TABLE_SIZE 64
31+
32+
#define XLOG_BUF_CANCEL_BUCKET(log, blkno) \
33+
((log)->l_buf_cancel_table + ((uint64_t)blkno % XLOG_BC_TABLE_SIZE))
34+
2635
/*
2736
* This structure is used during recovery to record the buf log items which
2837
* have been canceled and should not be replayed.
@@ -993,3 +1002,60 @@ const struct xlog_recover_item_ops xlog_buf_item_ops = {
9931002
.commit_pass1 = xlog_recover_buf_commit_pass1,
9941003
.commit_pass2 = xlog_recover_buf_commit_pass2,
9951004
};
1005+
1006+
#ifdef DEBUG
1007+
void
1008+
xlog_check_buf_cancel_table(
1009+
struct xlog *log)
1010+
{
1011+
int i;
1012+
1013+
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
1014+
ASSERT(list_empty(&log->l_buf_cancel_table[i]));
1015+
}
1016+
#endif
1017+
1018+
int
1019+
xlog_alloc_buf_cancel_table(
1020+
struct xlog *log)
1021+
{
1022+
void *p;
1023+
int i;
1024+
1025+
ASSERT(log->l_buf_cancel_table == NULL);
1026+
1027+
p = kmalloc_array(XLOG_BC_TABLE_SIZE, sizeof(struct list_head),
1028+
GFP_KERNEL);
1029+
if (!p)
1030+
return -ENOMEM;
1031+
1032+
log->l_buf_cancel_table = p;
1033+
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
1034+
INIT_LIST_HEAD(&log->l_buf_cancel_table[i]);
1035+
1036+
return 0;
1037+
}
1038+
1039+
void
1040+
xlog_free_buf_cancel_table(
1041+
struct xlog *log)
1042+
{
1043+
int i;
1044+
1045+
if (!log->l_buf_cancel_table)
1046+
return;
1047+
1048+
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++) {
1049+
struct xfs_buf_cancel *bc;
1050+
1051+
while ((bc = list_first_entry_or_null(
1052+
&log->l_buf_cancel_table[i],
1053+
struct xfs_buf_cancel, bc_list))) {
1054+
list_del(&bc->bc_list);
1055+
kmem_free(bc);
1056+
}
1057+
}
1058+
1059+
kmem_free(log->l_buf_cancel_table);
1060+
log->l_buf_cancel_table = NULL;
1061+
}

fs/xfs/xfs_log_priv.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,6 @@ struct xlog {
428428
struct rw_semaphore l_incompat_users;
429429
};
430430

431-
#define XLOG_BUF_CANCEL_BUCKET(log, blkno) \
432-
((log)->l_buf_cancel_table + ((uint64_t)blkno % XLOG_BC_TABLE_SIZE))
433-
434431
/*
435432
* Bits for operational state
436433
*/

fs/xfs/xfs_log_recover.c

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3223,45 +3223,33 @@ xlog_do_log_recovery(
32233223
xfs_daddr_t head_blk,
32243224
xfs_daddr_t tail_blk)
32253225
{
3226-
int error, i;
3226+
int error;
32273227

32283228
ASSERT(head_blk != tail_blk);
32293229

32303230
/*
32313231
* First do a pass to find all of the cancelled buf log items.
32323232
* Store them in the buf_cancel_table for use in the second pass.
32333233
*/
3234-
log->l_buf_cancel_table = kmem_zalloc(XLOG_BC_TABLE_SIZE *
3235-
sizeof(struct list_head),
3236-
0);
3237-
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
3238-
INIT_LIST_HEAD(&log->l_buf_cancel_table[i]);
3234+
error = xlog_alloc_buf_cancel_table(log);
3235+
if (error)
3236+
return error;
32393237

32403238
error = xlog_do_recovery_pass(log, head_blk, tail_blk,
32413239
XLOG_RECOVER_PASS1, NULL);
3242-
if (error != 0) {
3243-
kmem_free(log->l_buf_cancel_table);
3244-
log->l_buf_cancel_table = NULL;
3245-
return error;
3246-
}
3240+
if (error != 0)
3241+
goto out_cancel;
3242+
32473243
/*
32483244
* Then do a second pass to actually recover the items in the log.
32493245
* When it is complete free the table of buf cancel items.
32503246
*/
32513247
error = xlog_do_recovery_pass(log, head_blk, tail_blk,
32523248
XLOG_RECOVER_PASS2, NULL);
3253-
#ifdef DEBUG
3254-
if (!error) {
3255-
int i;
3256-
3257-
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
3258-
ASSERT(list_empty(&log->l_buf_cancel_table[i]));
3259-
}
3260-
#endif /* DEBUG */
3261-
3262-
kmem_free(log->l_buf_cancel_table);
3263-
log->l_buf_cancel_table = NULL;
3264-
3249+
if (!error)
3250+
xlog_check_buf_cancel_table(log);
3251+
out_cancel:
3252+
xlog_free_buf_cancel_table(log);
32653253
return error;
32663254
}
32673255

0 commit comments

Comments
 (0)