Skip to content

Commit 7975aba

Browse files
author
Darrick J. Wong
committed
xfs: fix integer overflows in the fsmap rtbitmap and logdev backends
It's not correct to use the rmap irec structure to hold query key information to query the rtbitmap because the realtime volume can be longer than 2^32 fsblocks in length. Because the rt volume doesn't have allocation groups, introduce a daddr-based record filtering algorithm and compute the rtextent values using 64-bit variables. The same problem exists in the external log device fsmap implementation, so use the same solution to fix it too. After this patch, all the code that touches info->low and info->high under xfs_getfsmap_logdev and __xfs_getfsmap_rtdev are unnecessary. Cleaning this up will be done in subsequent patches. Fixes: 4c934c7 ("xfs: report realtime space information via the rtbitmap") Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
1 parent 63ef7a3 commit 7975aba

File tree

1 file changed

+64
-26
lines changed

1 file changed

+64
-26
lines changed

fs/xfs/xfs_fsmap.c

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ struct xfs_getfsmap_info {
160160
struct xfs_buf *agf_bp; /* AGF, for refcount queries */
161161
struct xfs_perag *pag; /* AG info, if applicable */
162162
xfs_daddr_t next_daddr; /* next daddr we expect */
163+
/* daddr of low fsmap key when we're using the rtbitmap */
164+
xfs_daddr_t low_daddr;
163165
u64 missing_owner; /* owner of holes */
164166
u32 dev; /* device id */
165167
/*
@@ -250,21 +252,25 @@ xfs_getfsmap_rec_before_start(
250252
const struct xfs_rmap_irec *rec,
251253
xfs_daddr_t rec_daddr)
252254
{
255+
if (info->low_daddr != -1ULL)
256+
return rec_daddr < info->low_daddr;
253257
if (info->low.rm_blockcount)
254258
return xfs_rmap_compare(rec, &info->low) < 0;
255259
return false;
256260
}
257261

258262
/*
259263
* Format a reverse mapping for getfsmap, having translated rm_startblock
260-
* into the appropriate daddr units.
264+
* into the appropriate daddr units. Pass in a nonzero @len_daddr if the
265+
* length could be larger than rm_blockcount in struct xfs_rmap_irec.
261266
*/
262267
STATIC int
263268
xfs_getfsmap_helper(
264269
struct xfs_trans *tp,
265270
struct xfs_getfsmap_info *info,
266271
const struct xfs_rmap_irec *rec,
267-
xfs_daddr_t rec_daddr)
272+
xfs_daddr_t rec_daddr,
273+
xfs_daddr_t len_daddr)
268274
{
269275
struct xfs_fsmap fmr;
270276
struct xfs_mount *mp = tp->t_mountp;
@@ -274,12 +280,15 @@ xfs_getfsmap_helper(
274280
if (fatal_signal_pending(current))
275281
return -EINTR;
276282

283+
if (len_daddr == 0)
284+
len_daddr = XFS_FSB_TO_BB(mp, rec->rm_blockcount);
285+
277286
/*
278287
* Filter out records that start before our startpoint, if the
279288
* caller requested that.
280289
*/
281290
if (xfs_getfsmap_rec_before_start(info, rec, rec_daddr)) {
282-
rec_daddr += XFS_FSB_TO_BB(mp, rec->rm_blockcount);
291+
rec_daddr += len_daddr;
283292
if (info->next_daddr < rec_daddr)
284293
info->next_daddr = rec_daddr;
285294
return 0;
@@ -298,7 +307,7 @@ xfs_getfsmap_helper(
298307

299308
info->head->fmh_entries++;
300309

301-
rec_daddr += XFS_FSB_TO_BB(mp, rec->rm_blockcount);
310+
rec_daddr += len_daddr;
302311
if (info->next_daddr < rec_daddr)
303312
info->next_daddr = rec_daddr;
304313
return 0;
@@ -338,7 +347,7 @@ xfs_getfsmap_helper(
338347
if (error)
339348
return error;
340349
fmr.fmr_offset = XFS_FSB_TO_BB(mp, rec->rm_offset);
341-
fmr.fmr_length = XFS_FSB_TO_BB(mp, rec->rm_blockcount);
350+
fmr.fmr_length = len_daddr;
342351
if (rec->rm_flags & XFS_RMAP_UNWRITTEN)
343352
fmr.fmr_flags |= FMR_OF_PREALLOC;
344353
if (rec->rm_flags & XFS_RMAP_ATTR_FORK)
@@ -355,7 +364,7 @@ xfs_getfsmap_helper(
355364

356365
xfs_getfsmap_format(mp, &fmr, info);
357366
out:
358-
rec_daddr += XFS_FSB_TO_BB(mp, rec->rm_blockcount);
367+
rec_daddr += len_daddr;
359368
if (info->next_daddr < rec_daddr)
360369
info->next_daddr = rec_daddr;
361370
return 0;
@@ -376,7 +385,7 @@ xfs_getfsmap_datadev_helper(
376385
fsb = XFS_AGB_TO_FSB(mp, cur->bc_ag.pag->pag_agno, rec->rm_startblock);
377386
rec_daddr = XFS_FSB_TO_DADDR(mp, fsb);
378387

379-
return xfs_getfsmap_helper(cur->bc_tp, info, rec, rec_daddr);
388+
return xfs_getfsmap_helper(cur->bc_tp, info, rec, rec_daddr, 0);
380389
}
381390

382391
/* Transform a bnobt irec into a fsmap */
@@ -400,7 +409,7 @@ xfs_getfsmap_datadev_bnobt_helper(
400409
irec.rm_offset = 0;
401410
irec.rm_flags = 0;
402411

403-
return xfs_getfsmap_helper(cur->bc_tp, info, &irec, rec_daddr);
412+
return xfs_getfsmap_helper(cur->bc_tp, info, &irec, rec_daddr, 0);
404413
}
405414

406415
/* Set rmap flags based on the getfsmap flags */
@@ -427,9 +436,13 @@ xfs_getfsmap_logdev(
427436
{
428437
struct xfs_mount *mp = tp->t_mountp;
429438
struct xfs_rmap_irec rmap;
439+
xfs_daddr_t rec_daddr, len_daddr;
440+
xfs_fsblock_t start_fsb;
430441
int error;
431442

432443
/* Set up search keys */
444+
start_fsb = XFS_BB_TO_FSBT(mp,
445+
keys[0].fmr_physical + keys[0].fmr_length);
433446
info->low.rm_startblock = XFS_BB_TO_FSBT(mp, keys[0].fmr_physical);
434447
info->low.rm_offset = XFS_BB_TO_FSBT(mp, keys[0].fmr_offset);
435448
error = xfs_fsmap_owner_to_rmap(&info->low, keys);
@@ -438,6 +451,10 @@ xfs_getfsmap_logdev(
438451
info->low.rm_blockcount = 0;
439452
xfs_getfsmap_set_irec_flags(&info->low, &keys[0]);
440453

454+
/* Adjust the low key if we are continuing from where we left off. */
455+
if (keys[0].fmr_length > 0)
456+
info->low_daddr = XFS_FSB_TO_BB(mp, start_fsb);
457+
441458
error = xfs_fsmap_owner_to_rmap(&info->high, keys + 1);
442459
if (error)
443460
return error;
@@ -451,7 +468,7 @@ xfs_getfsmap_logdev(
451468
trace_xfs_fsmap_low_key(mp, info->dev, NULLAGNUMBER, &info->low);
452469
trace_xfs_fsmap_high_key(mp, info->dev, NULLAGNUMBER, &info->high);
453470

454-
if (keys[0].fmr_physical > 0)
471+
if (start_fsb > 0)
455472
return 0;
456473

457474
/* Fabricate an rmap entry for the external log device. */
@@ -461,7 +478,9 @@ xfs_getfsmap_logdev(
461478
rmap.rm_offset = 0;
462479
rmap.rm_flags = 0;
463480

464-
return xfs_getfsmap_helper(tp, info, &rmap, 0);
481+
rec_daddr = XFS_FSB_TO_BB(mp, rmap.rm_startblock);
482+
len_daddr = XFS_FSB_TO_BB(mp, rmap.rm_blockcount);
483+
return xfs_getfsmap_helper(tp, info, &rmap, rec_daddr, len_daddr);
465484
}
466485

467486
#ifdef CONFIG_XFS_RT
@@ -475,16 +494,22 @@ xfs_getfsmap_rtdev_rtbitmap_helper(
475494
{
476495
struct xfs_getfsmap_info *info = priv;
477496
struct xfs_rmap_irec irec;
478-
xfs_daddr_t rec_daddr;
497+
xfs_rtblock_t rtbno;
498+
xfs_daddr_t rec_daddr, len_daddr;
499+
500+
rtbno = rec->ar_startext * mp->m_sb.sb_rextsize;
501+
rec_daddr = XFS_FSB_TO_BB(mp, rtbno);
502+
irec.rm_startblock = rtbno;
503+
504+
rtbno = rec->ar_extcount * mp->m_sb.sb_rextsize;
505+
len_daddr = XFS_FSB_TO_BB(mp, rtbno);
506+
irec.rm_blockcount = rtbno;
479507

480-
irec.rm_startblock = rec->ar_startext * mp->m_sb.sb_rextsize;
481-
rec_daddr = XFS_FSB_TO_BB(mp, irec.rm_startblock);
482-
irec.rm_blockcount = rec->ar_extcount * mp->m_sb.sb_rextsize;
483508
irec.rm_owner = XFS_RMAP_OWN_NULL; /* "free" */
484509
irec.rm_offset = 0;
485510
irec.rm_flags = 0;
486511

487-
return xfs_getfsmap_helper(tp, info, &irec, rec_daddr);
512+
return xfs_getfsmap_helper(tp, info, &irec, rec_daddr, len_daddr);
488513
}
489514

490515
/* Execute a getfsmap query against the realtime device. */
@@ -493,31 +518,41 @@ __xfs_getfsmap_rtdev(
493518
struct xfs_trans *tp,
494519
const struct xfs_fsmap *keys,
495520
int (*query_fn)(struct xfs_trans *,
496-
struct xfs_getfsmap_info *),
521+
struct xfs_getfsmap_info *,
522+
xfs_rtblock_t start_rtb,
523+
xfs_rtblock_t end_rtb),
497524
struct xfs_getfsmap_info *info)
498525
{
499526
struct xfs_mount *mp = tp->t_mountp;
500-
xfs_fsblock_t start_fsb;
501-
xfs_fsblock_t end_fsb;
527+
xfs_rtblock_t start_rtb;
528+
xfs_rtblock_t end_rtb;
502529
uint64_t eofs;
503530
int error = 0;
504531

505532
eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks);
506533
if (keys[0].fmr_physical >= eofs)
507534
return 0;
508-
start_fsb = XFS_BB_TO_FSBT(mp, keys[0].fmr_physical);
509-
end_fsb = XFS_BB_TO_FSB(mp, min(eofs - 1, keys[1].fmr_physical));
535+
start_rtb = XFS_BB_TO_FSBT(mp,
536+
keys[0].fmr_physical + keys[0].fmr_length);
537+
end_rtb = XFS_BB_TO_FSB(mp, min(eofs - 1, keys[1].fmr_physical));
510538

511539
/* Set up search keys */
512-
info->low.rm_startblock = start_fsb;
540+
info->low.rm_startblock = start_rtb;
513541
error = xfs_fsmap_owner_to_rmap(&info->low, &keys[0]);
514542
if (error)
515543
return error;
516544
info->low.rm_offset = XFS_BB_TO_FSBT(mp, keys[0].fmr_offset);
517545
info->low.rm_blockcount = 0;
518546
xfs_getfsmap_set_irec_flags(&info->low, &keys[0]);
519547

520-
info->high.rm_startblock = end_fsb;
548+
/* Adjust the low key if we are continuing from where we left off. */
549+
if (keys[0].fmr_length > 0) {
550+
info->low_daddr = XFS_FSB_TO_BB(mp, start_rtb);
551+
if (info->low_daddr >= eofs)
552+
return 0;
553+
}
554+
555+
info->high.rm_startblock = end_rtb;
521556
error = xfs_fsmap_owner_to_rmap(&info->high, &keys[1]);
522557
if (error)
523558
return error;
@@ -528,14 +563,16 @@ __xfs_getfsmap_rtdev(
528563
trace_xfs_fsmap_low_key(mp, info->dev, NULLAGNUMBER, &info->low);
529564
trace_xfs_fsmap_high_key(mp, info->dev, NULLAGNUMBER, &info->high);
530565

531-
return query_fn(tp, info);
566+
return query_fn(tp, info, start_rtb, end_rtb);
532567
}
533568

534569
/* Actually query the realtime bitmap. */
535570
STATIC int
536571
xfs_getfsmap_rtdev_rtbitmap_query(
537572
struct xfs_trans *tp,
538-
struct xfs_getfsmap_info *info)
573+
struct xfs_getfsmap_info *info,
574+
xfs_rtblock_t start_rtb,
575+
xfs_rtblock_t end_rtb)
539576
{
540577
struct xfs_rtalloc_rec alow = { 0 };
541578
struct xfs_rtalloc_rec ahigh = { 0 };
@@ -548,8 +585,8 @@ xfs_getfsmap_rtdev_rtbitmap_query(
548585
* Set up query parameters to return free rtextents covering the range
549586
* we want.
550587
*/
551-
alow.ar_startext = info->low.rm_startblock;
552-
ahigh.ar_startext = info->high.rm_startblock;
588+
alow.ar_startext = start_rtb;
589+
ahigh.ar_startext = end_rtb;
553590
do_div(alow.ar_startext, mp->m_sb.sb_rextsize);
554591
if (do_div(ahigh.ar_startext, mp->m_sb.sb_rextsize))
555592
ahigh.ar_startext++;
@@ -988,6 +1025,7 @@ xfs_getfsmap(
9881025
info.dev = handlers[i].dev;
9891026
info.last = false;
9901027
info.pag = NULL;
1028+
info.low_daddr = -1ULL;
9911029
info.low.rm_blockcount = 0;
9921030
error = handlers[i].fn(tp, dkeys, &info);
9931031
if (error)

0 commit comments

Comments
 (0)