Skip to content

Commit 9f187ba

Browse files
author
Darrick J. Wong
committed
Merge tag 'fix-log-recovery-misuse-6.1_2022-10-31' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.1-fixes
xfs: fix various problems with log intent item recovery Starting with 6.1-rc1, CONFIG_FORTIFY_SOURCE checks became smart enough to detect memcpy() callers that copy beyond what seems to be the end of a struct. Unfortunately, gcc has a bug wherein it cannot reliably compute the size of a struct containing another struct containing a flex array at the end. This is the case with the xfs log item format structures, which means that -rc1 starts complaining all over the place. Fix these problems by memcpying the struct head and the flex arrays separately. Although it's tempting to use the FLEX_ARRAY macros, the structs involved are part of the ondisk log format. Some day we're going to want to make the ondisk log contents endian-safe, which means that we will have to stop using memcpy entirely. While we're at it, fix some deficiencies in the validation of recovered log intent items -- if the size of the recovery buffer is not even large enough to cover the flex array record count in the head, we should abort the recovery of that item immediately. The last patch of this series changes the EFI/EFD sizeof functions names and behaviors to be consistent with the similarly named sizeof helpers for other log intent items. v2: fix more inadequate log intent done recovery validation and dump corrupt recovered items Signed-off-by: Darrick J. Wong <[email protected]> * tag 'fix-log-recovery-misuse-6.1_2022-10-31' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux: xfs: dump corrupt recovered log intent items to dmesg consistently xfs: actually abort log recovery on corrupt intent-done log items xfs: refactor all the EFI/EFD log item sizeof logic xfs: fix memcpy fortify errors in EFI log format copying xfs: fix memcpy fortify errors in RUI log format copying xfs: fix memcpy fortify errors in CUI log format copying xfs: fix memcpy fortify errors in BUI log format copying xfs: fix validation in attr log item recovery
2 parents 47ba8cc + 950f0d5 commit 9f187ba

File tree

9 files changed

+266
-187
lines changed

9 files changed

+266
-187
lines changed

fs/xfs/libxfs/xfs_log_format.h

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -613,25 +613,49 @@ typedef struct xfs_efi_log_format {
613613
uint16_t efi_size; /* size of this item */
614614
uint32_t efi_nextents; /* # extents to free */
615615
uint64_t efi_id; /* efi identifier */
616-
xfs_extent_t efi_extents[1]; /* array of extents to free */
616+
xfs_extent_t efi_extents[]; /* array of extents to free */
617617
} xfs_efi_log_format_t;
618618

619+
static inline size_t
620+
xfs_efi_log_format_sizeof(
621+
unsigned int nr)
622+
{
623+
return sizeof(struct xfs_efi_log_format) +
624+
nr * sizeof(struct xfs_extent);
625+
}
626+
619627
typedef struct xfs_efi_log_format_32 {
620628
uint16_t efi_type; /* efi log item type */
621629
uint16_t efi_size; /* size of this item */
622630
uint32_t efi_nextents; /* # extents to free */
623631
uint64_t efi_id; /* efi identifier */
624-
xfs_extent_32_t efi_extents[1]; /* array of extents to free */
632+
xfs_extent_32_t efi_extents[]; /* array of extents to free */
625633
} __attribute__((packed)) xfs_efi_log_format_32_t;
626634

635+
static inline size_t
636+
xfs_efi_log_format32_sizeof(
637+
unsigned int nr)
638+
{
639+
return sizeof(struct xfs_efi_log_format_32) +
640+
nr * sizeof(struct xfs_extent_32);
641+
}
642+
627643
typedef struct xfs_efi_log_format_64 {
628644
uint16_t efi_type; /* efi log item type */
629645
uint16_t efi_size; /* size of this item */
630646
uint32_t efi_nextents; /* # extents to free */
631647
uint64_t efi_id; /* efi identifier */
632-
xfs_extent_64_t efi_extents[1]; /* array of extents to free */
648+
xfs_extent_64_t efi_extents[]; /* array of extents to free */
633649
} xfs_efi_log_format_64_t;
634650

651+
static inline size_t
652+
xfs_efi_log_format64_sizeof(
653+
unsigned int nr)
654+
{
655+
return sizeof(struct xfs_efi_log_format_64) +
656+
nr * sizeof(struct xfs_extent_64);
657+
}
658+
635659
/*
636660
* This is the structure used to lay out an efd log item in the
637661
* log. The efd_extents array is a variable size array whose
@@ -642,25 +666,49 @@ typedef struct xfs_efd_log_format {
642666
uint16_t efd_size; /* size of this item */
643667
uint32_t efd_nextents; /* # of extents freed */
644668
uint64_t efd_efi_id; /* id of corresponding efi */
645-
xfs_extent_t efd_extents[1]; /* array of extents freed */
669+
xfs_extent_t efd_extents[]; /* array of extents freed */
646670
} xfs_efd_log_format_t;
647671

672+
static inline size_t
673+
xfs_efd_log_format_sizeof(
674+
unsigned int nr)
675+
{
676+
return sizeof(struct xfs_efd_log_format) +
677+
nr * sizeof(struct xfs_extent);
678+
}
679+
648680
typedef struct xfs_efd_log_format_32 {
649681
uint16_t efd_type; /* efd log item type */
650682
uint16_t efd_size; /* size of this item */
651683
uint32_t efd_nextents; /* # of extents freed */
652684
uint64_t efd_efi_id; /* id of corresponding efi */
653-
xfs_extent_32_t efd_extents[1]; /* array of extents freed */
685+
xfs_extent_32_t efd_extents[]; /* array of extents freed */
654686
} __attribute__((packed)) xfs_efd_log_format_32_t;
655687

688+
static inline size_t
689+
xfs_efd_log_format32_sizeof(
690+
unsigned int nr)
691+
{
692+
return sizeof(struct xfs_efd_log_format_32) +
693+
nr * sizeof(struct xfs_extent_32);
694+
}
695+
656696
typedef struct xfs_efd_log_format_64 {
657697
uint16_t efd_type; /* efd log item type */
658698
uint16_t efd_size; /* size of this item */
659699
uint32_t efd_nextents; /* # of extents freed */
660700
uint64_t efd_efi_id; /* id of corresponding efi */
661-
xfs_extent_64_t efd_extents[1]; /* array of extents freed */
701+
xfs_extent_64_t efd_extents[]; /* array of extents freed */
662702
} xfs_efd_log_format_64_t;
663703

704+
static inline size_t
705+
xfs_efd_log_format64_sizeof(
706+
unsigned int nr)
707+
{
708+
return sizeof(struct xfs_efd_log_format_64) +
709+
nr * sizeof(struct xfs_extent_64);
710+
}
711+
664712
/*
665713
* RUI/RUD (reverse mapping) log format definitions
666714
*/

fs/xfs/xfs_attr_item.c

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -245,28 +245,6 @@ xfs_attri_init(
245245
return attrip;
246246
}
247247

248-
/*
249-
* Copy an attr format buffer from the given buf, and into the destination attr
250-
* format structure.
251-
*/
252-
STATIC int
253-
xfs_attri_copy_format(
254-
struct xfs_log_iovec *buf,
255-
struct xfs_attri_log_format *dst_attr_fmt)
256-
{
257-
struct xfs_attri_log_format *src_attr_fmt = buf->i_addr;
258-
size_t len;
259-
260-
len = sizeof(struct xfs_attri_log_format);
261-
if (buf->i_len != len) {
262-
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);
263-
return -EFSCORRUPTED;
264-
}
265-
266-
memcpy((char *)dst_attr_fmt, (char *)src_attr_fmt, len);
267-
return 0;
268-
}
269-
270248
static inline struct xfs_attrd_log_item *ATTRD_ITEM(struct xfs_log_item *lip)
271249
{
272250
return container_of(lip, struct xfs_attrd_log_item, attrd_item);
@@ -731,24 +709,50 @@ xlog_recover_attri_commit_pass2(
731709
struct xfs_attri_log_nameval *nv;
732710
const void *attr_value = NULL;
733711
const void *attr_name;
734-
int error;
712+
size_t len;
735713

736714
attri_formatp = item->ri_buf[0].i_addr;
737715
attr_name = item->ri_buf[1].i_addr;
738716

739717
/* Validate xfs_attri_log_format before the large memory allocation */
718+
len = sizeof(struct xfs_attri_log_format);
719+
if (item->ri_buf[0].i_len != len) {
720+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
721+
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
722+
return -EFSCORRUPTED;
723+
}
724+
740725
if (!xfs_attri_validate(mp, attri_formatp)) {
741-
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
726+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
727+
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
728+
return -EFSCORRUPTED;
729+
}
730+
731+
/* Validate the attr name */
732+
if (item->ri_buf[1].i_len !=
733+
xlog_calc_iovec_len(attri_formatp->alfi_name_len)) {
734+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
735+
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
742736
return -EFSCORRUPTED;
743737
}
744738

745739
if (!xfs_attr_namecheck(attr_name, attri_formatp->alfi_name_len)) {
746-
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
740+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
741+
item->ri_buf[1].i_addr, item->ri_buf[1].i_len);
747742
return -EFSCORRUPTED;
748743
}
749744

750-
if (attri_formatp->alfi_value_len)
745+
/* Validate the attr value, if present */
746+
if (attri_formatp->alfi_value_len != 0) {
747+
if (item->ri_buf[2].i_len != xlog_calc_iovec_len(attri_formatp->alfi_value_len)) {
748+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
749+
item->ri_buf[0].i_addr,
750+
item->ri_buf[0].i_len);
751+
return -EFSCORRUPTED;
752+
}
753+
751754
attr_value = item->ri_buf[2].i_addr;
755+
}
752756

753757
/*
754758
* Memory alloc failure will cause replay to abort. We attach the
@@ -760,9 +764,7 @@ xlog_recover_attri_commit_pass2(
760764
attri_formatp->alfi_value_len);
761765

762766
attrip = xfs_attri_init(mp, nv);
763-
error = xfs_attri_copy_format(&item->ri_buf[0], &attrip->attri_format);
764-
if (error)
765-
goto out;
767+
memcpy(&attrip->attri_format, attri_formatp, len);
766768

767769
/*
768770
* The ATTRI has two references. One for the ATTRD and one for ATTRI to
@@ -774,10 +776,6 @@ xlog_recover_attri_commit_pass2(
774776
xfs_attri_release(attrip);
775777
xfs_attri_log_nameval_put(nv);
776778
return 0;
777-
out:
778-
xfs_attri_item_free(attrip);
779-
xfs_attri_log_nameval_put(nv);
780-
return error;
781779
}
782780

783781
/*
@@ -842,7 +840,8 @@ xlog_recover_attrd_commit_pass2(
842840

843841
attrd_formatp = item->ri_buf[0].i_addr;
844842
if (item->ri_buf[0].i_len != sizeof(struct xfs_attrd_log_format)) {
845-
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);
843+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
844+
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
846845
return -EFSCORRUPTED;
847846
}
848847

fs/xfs/xfs_bmap_item.c

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -608,28 +608,18 @@ static const struct xfs_item_ops xfs_bui_item_ops = {
608608
.iop_relog = xfs_bui_item_relog,
609609
};
610610

611-
/*
612-
* Copy an BUI format buffer from the given buf, and into the destination
613-
* BUI format structure. The BUI/BUD items were designed not to need any
614-
* special alignment handling.
615-
*/
616-
static int
611+
static inline void
617612
xfs_bui_copy_format(
618-
struct xfs_log_iovec *buf,
619-
struct xfs_bui_log_format *dst_bui_fmt)
613+
struct xfs_bui_log_format *dst,
614+
const struct xfs_bui_log_format *src)
620615
{
621-
struct xfs_bui_log_format *src_bui_fmt;
622-
uint len;
616+
unsigned int i;
623617

624-
src_bui_fmt = buf->i_addr;
625-
len = xfs_bui_log_format_sizeof(src_bui_fmt->bui_nextents);
618+
memcpy(dst, src, offsetof(struct xfs_bui_log_format, bui_extents));
626619

627-
if (buf->i_len == len) {
628-
memcpy(dst_bui_fmt, src_bui_fmt, len);
629-
return 0;
630-
}
631-
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);
632-
return -EFSCORRUPTED;
620+
for (i = 0; i < src->bui_nextents; i++)
621+
memcpy(&dst->bui_extents[i], &src->bui_extents[i],
622+
sizeof(struct xfs_map_extent));
633623
}
634624

635625
/*
@@ -646,23 +636,34 @@ xlog_recover_bui_commit_pass2(
646636
struct xlog_recover_item *item,
647637
xfs_lsn_t lsn)
648638
{
649-
int error;
650639
struct xfs_mount *mp = log->l_mp;
651640
struct xfs_bui_log_item *buip;
652641
struct xfs_bui_log_format *bui_formatp;
642+
size_t len;
653643

654644
bui_formatp = item->ri_buf[0].i_addr;
655645

646+
if (item->ri_buf[0].i_len < xfs_bui_log_format_sizeof(0)) {
647+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
648+
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
649+
return -EFSCORRUPTED;
650+
}
651+
656652
if (bui_formatp->bui_nextents != XFS_BUI_MAX_FAST_EXTENTS) {
657-
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, log->l_mp);
653+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
654+
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
658655
return -EFSCORRUPTED;
659656
}
660-
buip = xfs_bui_init(mp);
661-
error = xfs_bui_copy_format(&item->ri_buf[0], &buip->bui_format);
662-
if (error) {
663-
xfs_bui_item_free(buip);
664-
return error;
657+
658+
len = xfs_bui_log_format_sizeof(bui_formatp->bui_nextents);
659+
if (item->ri_buf[0].i_len != len) {
660+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
661+
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
662+
return -EFSCORRUPTED;
665663
}
664+
665+
buip = xfs_bui_init(mp);
666+
xfs_bui_copy_format(&buip->bui_format, bui_formatp);
666667
atomic_set(&buip->bui_next_extent, bui_formatp->bui_nextents);
667668
/*
668669
* Insert the intent into the AIL directly and drop one reference so
@@ -696,7 +697,8 @@ xlog_recover_bud_commit_pass2(
696697

697698
bud_formatp = item->ri_buf[0].i_addr;
698699
if (item->ri_buf[0].i_len != sizeof(struct xfs_bud_log_format)) {
699-
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, log->l_mp);
700+
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
701+
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
700702
return -EFSCORRUPTED;
701703
}
702704

0 commit comments

Comments
 (0)