Changes since last update:

- Fix a stale kernel memory exposure when logging inodes.
 - Fix some build problems with CONFIG_XFS_RT=n
 - Don't change inode mode if the acl write fails, leaving the file totally
   inaccessible.
 - Fix a dangling pointer problem when removing an attr fork under memory
   pressure.
 - Don't crash while trying to invalidate a null buffer associated with a
   corrupt metadata pointer.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCgAGBQJZ3lPiAAoJEPh/dxk0SrTrfuMP/Axy7VSX71tE/eXPOmzxCVZD
 w4/usqO+OsQj+q8o+rwwuX9hz0VGF8kWZJOdgGdXpYT7pWqPmcf88wbThheTetLF
 fjevusqva0Ds+U4AE7DCNWSKQQRhu2jDgnhQXTv1hdYhWIF59qGwioIijbEvb72I
 0QW+/uV9yXmODjWL6KfRh9zRT9N4npMtszukScONwJr9t0/5ub8H03H/ktv8T9oi
 C3ljEWwyMk5lEYH8p6tpta8EbY0mrIZgo+kj33PU5s9rHvcrTGtyPNqidREUm1fL
 X3+STMytcDQFAcZdBBXHN0nFMwa8ADTrVvKmEgaR8OsXmOmrlcPn7HfVVlWrY31w
 X3awJ0b0+IXUrsbbQOPeqgTo5hIkMDkMOga5AP/rqpx1yCCOrlMHaRPXB2NxNcVw
 dyTj6IpKybhsQ4GkcqmFcgnxPPaogNpYlp6SXV5Dm+8zEJdIQNUuci/EGsNz7UcV
 msxNlJJkxczXOew6JzCyw45wTnJCxduX7Y1xrOTLaDfa9pkWO2zQBXukCJNIqVIq
 35Q4P4JVYtmwQr8XkkX9tiqU0gBWTCTG9KjmTCMm5MYkutEYM0uTNR5Jvyiobl7L
 Nn+RydssVw7ssnNfgsLhzQHPElUivRdYoYFSBa2DQp6ViILrefqQegd5INAjK63W
 7vnHVZyJMHPM0YFoiX8w
 =6Yvh
 -----END PGP SIGNATURE-----

Merge tag 'xfs-4.14-fixes-5' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Darrick Wong:

 - Fix a stale kernel memory exposure when logging inodes.

 - Fix some build problems with CONFIG_XFS_RT=n

 - Don't change inode mode if the acl write fails, leaving the file
   totally inaccessible.

 - Fix a dangling pointer problem when removing an attr fork under
   memory pressure.

 - Don't crash while trying to invalidate a null buffer associated with
   a corrupt metadata pointer.

* tag 'xfs-4.14-fixes-5' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: handle error if xfs_btree_get_bufs fails
  xfs: reinit btree pointer on attr tree inactivation walk
  xfs: Fix bool initialization/comparison
  xfs: don't change inode mode if ACL update fails
  xfs: move more RT specific code under CONFIG_XFS_RT
  xfs: Don't log uninitialised fields in inode structures
This commit is contained in:
Linus Torvalds 2017-10-12 14:51:13 -07:00
commit 8ff0b97cf2
14 changed files with 109 additions and 70 deletions

View File

@ -1584,6 +1584,10 @@ xfs_alloc_ag_vextent_small(
bp = xfs_btree_get_bufs(args->mp, args->tp, bp = xfs_btree_get_bufs(args->mp, args->tp,
args->agno, fbno, 0); args->agno, fbno, 0);
if (!bp) {
error = -EFSCORRUPTED;
goto error0;
}
xfs_trans_binval(args->tp, bp); xfs_trans_binval(args->tp, bp);
} }
args->len = 1; args->len = 1;
@ -2141,6 +2145,10 @@ xfs_alloc_fix_freelist(
if (error) if (error)
goto out_agbp_relse; goto out_agbp_relse;
bp = xfs_btree_get_bufs(mp, tp, args->agno, bno, 0); bp = xfs_btree_get_bufs(mp, tp, args->agno, bno, 0);
if (!bp) {
error = -EFSCORRUPTED;
goto out_agbp_relse;
}
xfs_trans_binval(tp, bp); xfs_trans_binval(tp, bp);
} }

View File

@ -1477,14 +1477,14 @@ xfs_bmap_isaeof(
int is_empty; int is_empty;
int error; int error;
bma->aeof = 0; bma->aeof = false;
error = xfs_bmap_last_extent(NULL, bma->ip, whichfork, &rec, error = xfs_bmap_last_extent(NULL, bma->ip, whichfork, &rec,
&is_empty); &is_empty);
if (error) if (error)
return error; return error;
if (is_empty) { if (is_empty) {
bma->aeof = 1; bma->aeof = true;
return 0; return 0;
} }

View File

@ -1962,7 +1962,7 @@ xfs_difree_inobt(
if (!(mp->m_flags & XFS_MOUNT_IKEEP) && if (!(mp->m_flags & XFS_MOUNT_IKEEP) &&
rec.ir_free == XFS_INOBT_ALL_FREE && rec.ir_free == XFS_INOBT_ALL_FREE &&
mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK) { mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK) {
xic->deleted = 1; xic->deleted = true;
xic->first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino); xic->first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino);
xic->alloc = xfs_inobt_irec_to_allocmask(&rec); xic->alloc = xfs_inobt_irec_to_allocmask(&rec);
@ -1989,7 +1989,7 @@ xfs_difree_inobt(
xfs_difree_inode_chunk(mp, agno, &rec, dfops); xfs_difree_inode_chunk(mp, agno, &rec, dfops);
} else { } else {
xic->deleted = 0; xic->deleted = false;
error = xfs_inobt_update(cur, &rec); error = xfs_inobt_update(cur, &rec);
if (error) { if (error) {

View File

@ -270,6 +270,7 @@ typedef struct xfs_inode_log_format {
uint32_t ilf_fields; /* flags for fields logged */ uint32_t ilf_fields; /* flags for fields logged */
uint16_t ilf_asize; /* size of attr d/ext/root */ uint16_t ilf_asize; /* size of attr d/ext/root */
uint16_t ilf_dsize; /* size of data/ext/root */ uint16_t ilf_dsize; /* size of data/ext/root */
uint32_t ilf_pad; /* pad for 64 bit boundary */
uint64_t ilf_ino; /* inode number */ uint64_t ilf_ino; /* inode number */
union { union {
uint32_t ilfu_rdev; /* rdev value for dev inode*/ uint32_t ilfu_rdev; /* rdev value for dev inode*/
@ -280,7 +281,12 @@ typedef struct xfs_inode_log_format {
int32_t ilf_boffset; /* off of inode in buffer */ int32_t ilf_boffset; /* off of inode in buffer */
} xfs_inode_log_format_t; } xfs_inode_log_format_t;
typedef struct xfs_inode_log_format_32 { /*
* Old 32 bit systems will log in this format without the 64 bit
* alignment padding. Recovery will detect this and convert it to the
* correct format.
*/
struct xfs_inode_log_format_32 {
uint16_t ilf_type; /* inode log item type */ uint16_t ilf_type; /* inode log item type */
uint16_t ilf_size; /* size of this item */ uint16_t ilf_size; /* size of this item */
uint32_t ilf_fields; /* flags for fields logged */ uint32_t ilf_fields; /* flags for fields logged */
@ -294,24 +300,7 @@ typedef struct xfs_inode_log_format_32 {
int64_t ilf_blkno; /* blkno of inode buffer */ int64_t ilf_blkno; /* blkno of inode buffer */
int32_t ilf_len; /* len of inode buffer */ int32_t ilf_len; /* len of inode buffer */
int32_t ilf_boffset; /* off of inode in buffer */ int32_t ilf_boffset; /* off of inode in buffer */
} __attribute__((packed)) xfs_inode_log_format_32_t; } __attribute__((packed));
typedef struct xfs_inode_log_format_64 {
uint16_t ilf_type; /* inode log item type */
uint16_t ilf_size; /* size of this item */
uint32_t ilf_fields; /* flags for fields logged */
uint16_t ilf_asize; /* size of attr d/ext/root */
uint16_t ilf_dsize; /* size of data/ext/root */
uint32_t ilf_pad; /* pad for 64 bit boundary */
uint64_t ilf_ino; /* inode number */
union {
uint32_t ilfu_rdev; /* rdev value for dev inode*/
uuid_t ilfu_uuid; /* mount point value */
} ilf_u;
int64_t ilf_blkno; /* blkno of inode buffer */
int32_t ilf_len; /* len of inode buffer */
int32_t ilf_boffset; /* off of inode in buffer */
} xfs_inode_log_format_64_t;
/* /*

View File

@ -247,6 +247,8 @@ xfs_set_mode(struct inode *inode, umode_t mode)
int int
xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{ {
umode_t mode;
bool set_mode = false;
int error = 0; int error = 0;
if (!acl) if (!acl)
@ -257,16 +259,24 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
return error; return error;
if (type == ACL_TYPE_ACCESS) { if (type == ACL_TYPE_ACCESS) {
umode_t mode;
error = posix_acl_update_mode(inode, &mode, &acl); error = posix_acl_update_mode(inode, &mode, &acl);
if (error) if (error)
return error; return error;
error = xfs_set_mode(inode, mode); set_mode = true;
if (error)
return error;
} }
set_acl: set_acl:
return __xfs_set_acl(inode, acl, type); error = __xfs_set_acl(inode, acl, type);
if (error)
return error;
/*
* We set the mode after successfully updating the ACL xattr because the
* xattr update can fail at ENOSPC and we don't want to change the mode
* if the ACL update hasn't been applied.
*/
if (set_mode)
error = xfs_set_mode(inode, mode);
return error;
} }

View File

@ -302,6 +302,8 @@ xfs_attr3_node_inactive(
&bp, XFS_ATTR_FORK); &bp, XFS_ATTR_FORK);
if (error) if (error)
return error; return error;
node = bp->b_addr;
btree = dp->d_ops->node_tree_p(node);
child_fsb = be32_to_cpu(btree[i + 1].before); child_fsb = be32_to_cpu(btree[i + 1].before);
xfs_trans_brelse(*trans, bp); xfs_trans_brelse(*trans, bp);
} }

View File

@ -84,6 +84,7 @@ xfs_zero_extent(
GFP_NOFS, 0); GFP_NOFS, 0);
} }
#ifdef CONFIG_XFS_RT
int int
xfs_bmap_rtalloc( xfs_bmap_rtalloc(
struct xfs_bmalloca *ap) /* bmap alloc argument struct */ struct xfs_bmalloca *ap) /* bmap alloc argument struct */
@ -190,6 +191,7 @@ xfs_bmap_rtalloc(
} }
return 0; return 0;
} }
#endif /* CONFIG_XFS_RT */
/* /*
* Check if the endoff is outside the last extent. If so the caller will grow * Check if the endoff is outside the last extent. If so the caller will grow

View File

@ -28,7 +28,20 @@ struct xfs_mount;
struct xfs_trans; struct xfs_trans;
struct xfs_bmalloca; struct xfs_bmalloca;
#ifdef CONFIG_XFS_RT
int xfs_bmap_rtalloc(struct xfs_bmalloca *ap); int xfs_bmap_rtalloc(struct xfs_bmalloca *ap);
#else /* !CONFIG_XFS_RT */
/*
* Attempts to allocate RT extents when RT is disable indicates corruption and
* should trigger a shutdown.
*/
static inline int
xfs_bmap_rtalloc(struct xfs_bmalloca *ap)
{
return -EFSCORRUPTED;
}
#endif /* CONFIG_XFS_RT */
int xfs_bmap_eof(struct xfs_inode *ip, xfs_fileoff_t endoff, int xfs_bmap_eof(struct xfs_inode *ip, xfs_fileoff_t endoff,
int whichfork, int *eof); int whichfork, int *eof);
int xfs_bmap_punch_delalloc_range(struct xfs_inode *ip, int xfs_bmap_punch_delalloc_range(struct xfs_inode *ip,

View File

@ -764,7 +764,7 @@ xfs_file_fallocate(
enum xfs_prealloc_flags flags = 0; enum xfs_prealloc_flags flags = 0;
uint iolock = XFS_IOLOCK_EXCL; uint iolock = XFS_IOLOCK_EXCL;
loff_t new_size = 0; loff_t new_size = 0;
bool do_file_insert = 0; bool do_file_insert = false;
if (!S_ISREG(inode->i_mode)) if (!S_ISREG(inode->i_mode))
return -EINVAL; return -EINVAL;
@ -825,7 +825,7 @@ xfs_file_fallocate(
error = -EINVAL; error = -EINVAL;
goto out_unlock; goto out_unlock;
} }
do_file_insert = 1; do_file_insert = true;
} else { } else {
flags |= XFS_PREALLOC_SET; flags |= XFS_PREALLOC_SET;

View File

@ -521,6 +521,7 @@ __xfs_getfsmap_rtdev(
return query_fn(tp, info); return query_fn(tp, info);
} }
#ifdef CONFIG_XFS_RT
/* Actually query the realtime bitmap. */ /* Actually query the realtime bitmap. */
STATIC int STATIC int
xfs_getfsmap_rtdev_rtbitmap_query( xfs_getfsmap_rtdev_rtbitmap_query(
@ -561,6 +562,7 @@ xfs_getfsmap_rtdev_rtbitmap(
return __xfs_getfsmap_rtdev(tp, keys, xfs_getfsmap_rtdev_rtbitmap_query, return __xfs_getfsmap_rtdev(tp, keys, xfs_getfsmap_rtdev_rtbitmap_query,
info); info);
} }
#endif /* CONFIG_XFS_RT */
/* Execute a getfsmap query against the regular data device. */ /* Execute a getfsmap query against the regular data device. */
STATIC int STATIC int
@ -795,7 +797,15 @@ xfs_getfsmap_check_keys(
return false; return false;
} }
/*
* There are only two devices if we didn't configure RT devices at build time.
*/
#ifdef CONFIG_XFS_RT
#define XFS_GETFSMAP_DEVS 3 #define XFS_GETFSMAP_DEVS 3
#else
#define XFS_GETFSMAP_DEVS 2
#endif /* CONFIG_XFS_RT */
/* /*
* Get filesystem's extents as described in head, and format for * Get filesystem's extents as described in head, and format for
* output. Calls formatter to fill the user's buffer until all * output. Calls formatter to fill the user's buffer until all
@ -853,10 +863,12 @@ xfs_getfsmap(
handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev); handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev);
handlers[1].fn = xfs_getfsmap_logdev; handlers[1].fn = xfs_getfsmap_logdev;
} }
#ifdef CONFIG_XFS_RT
if (mp->m_rtdev_targp) { if (mp->m_rtdev_targp) {
handlers[2].dev = new_encode_dev(mp->m_rtdev_targp->bt_dev); handlers[2].dev = new_encode_dev(mp->m_rtdev_targp->bt_dev);
handlers[2].fn = xfs_getfsmap_rtdev_rtbitmap; handlers[2].fn = xfs_getfsmap_rtdev_rtbitmap;
} }
#endif /* CONFIG_XFS_RT */
xfs_sort(handlers, XFS_GETFSMAP_DEVS, sizeof(struct xfs_getfsmap_dev), xfs_sort(handlers, XFS_GETFSMAP_DEVS, sizeof(struct xfs_getfsmap_dev),
xfs_getfsmap_dev_compare); xfs_getfsmap_dev_compare);

View File

@ -364,6 +364,9 @@ xfs_inode_to_log_dinode(
to->di_dmstate = from->di_dmstate; to->di_dmstate = from->di_dmstate;
to->di_flags = from->di_flags; to->di_flags = from->di_flags;
/* log a dummy value to ensure log structure is fully initialised */
to->di_next_unlinked = NULLAGINO;
if (from->di_version == 3) { if (from->di_version == 3) {
to->di_changecount = inode->i_version; to->di_changecount = inode->i_version;
to->di_crtime.t_sec = from->di_crtime.t_sec; to->di_crtime.t_sec = from->di_crtime.t_sec;
@ -404,6 +407,11 @@ xfs_inode_item_format_core(
* the second with the on-disk inode structure, and a possible third and/or * the second with the on-disk inode structure, and a possible third and/or
* fourth with the inode data/extents/b-tree root and inode attributes * fourth with the inode data/extents/b-tree root and inode attributes
* data/extents/b-tree root. * data/extents/b-tree root.
*
* Note: Always use the 64 bit inode log format structure so we don't
* leave an uninitialised hole in the format item on 64 bit systems. Log
* recovery on 32 bit systems handles this just fine, so there's no reason
* for not using an initialising the properly padded structure all the time.
*/ */
STATIC void STATIC void
xfs_inode_item_format( xfs_inode_item_format(
@ -412,8 +420,8 @@ xfs_inode_item_format(
{ {
struct xfs_inode_log_item *iip = INODE_ITEM(lip); struct xfs_inode_log_item *iip = INODE_ITEM(lip);
struct xfs_inode *ip = iip->ili_inode; struct xfs_inode *ip = iip->ili_inode;
struct xfs_inode_log_format *ilf;
struct xfs_log_iovec *vecp = NULL; struct xfs_log_iovec *vecp = NULL;
struct xfs_inode_log_format *ilf;
ASSERT(ip->i_d.di_version > 1); ASSERT(ip->i_d.di_version > 1);
@ -425,7 +433,17 @@ xfs_inode_item_format(
ilf->ilf_boffset = ip->i_imap.im_boffset; ilf->ilf_boffset = ip->i_imap.im_boffset;
ilf->ilf_fields = XFS_ILOG_CORE; ilf->ilf_fields = XFS_ILOG_CORE;
ilf->ilf_size = 2; /* format + core */ ilf->ilf_size = 2; /* format + core */
xlog_finish_iovec(lv, vecp, sizeof(struct xfs_inode_log_format));
/*
* make sure we don't leak uninitialised data into the log in the case
* when we don't log every field in the inode.
*/
ilf->ilf_dsize = 0;
ilf->ilf_asize = 0;
ilf->ilf_pad = 0;
uuid_copy(&ilf->ilf_u.ilfu_uuid, &uuid_null);
xlog_finish_iovec(lv, vecp, sizeof(*ilf));
xfs_inode_item_format_core(ip, lv, &vecp); xfs_inode_item_format_core(ip, lv, &vecp);
xfs_inode_item_format_data_fork(iip, ilf, lv, &vecp); xfs_inode_item_format_data_fork(iip, ilf, lv, &vecp);
@ -855,16 +873,18 @@ xfs_istale_done(
} }
/* /*
* convert an xfs_inode_log_format struct from either 32 or 64 bit versions * convert an xfs_inode_log_format struct from the old 32 bit version
* (which can have different field alignments) to the native version * (which can have different field alignments) to the native 64 bit version
*/ */
int int
xfs_inode_item_format_convert( xfs_inode_item_format_convert(
xfs_log_iovec_t *buf, struct xfs_log_iovec *buf,
xfs_inode_log_format_t *in_f) struct xfs_inode_log_format *in_f)
{ {
if (buf->i_len == sizeof(xfs_inode_log_format_32_t)) { struct xfs_inode_log_format_32 *in_f32 = buf->i_addr;
xfs_inode_log_format_32_t *in_f32 = buf->i_addr;
if (buf->i_len != sizeof(*in_f32))
return -EFSCORRUPTED;
in_f->ilf_type = in_f32->ilf_type; in_f->ilf_type = in_f32->ilf_type;
in_f->ilf_size = in_f32->ilf_size; in_f->ilf_size = in_f32->ilf_size;
@ -878,21 +898,4 @@ xfs_inode_item_format_convert(
in_f->ilf_len = in_f32->ilf_len; in_f->ilf_len = in_f32->ilf_len;
in_f->ilf_boffset = in_f32->ilf_boffset; in_f->ilf_boffset = in_f32->ilf_boffset;
return 0; return 0;
} else if (buf->i_len == sizeof(xfs_inode_log_format_64_t)){
xfs_inode_log_format_64_t *in_f64 = buf->i_addr;
in_f->ilf_type = in_f64->ilf_type;
in_f->ilf_size = in_f64->ilf_size;
in_f->ilf_fields = in_f64->ilf_fields;
in_f->ilf_asize = in_f64->ilf_asize;
in_f->ilf_dsize = in_f64->ilf_dsize;
in_f->ilf_ino = in_f64->ilf_ino;
/* copy biggest field of ilf_u */
uuid_copy(&in_f->ilf_u.ilfu_uuid, &in_f64->ilf_u.ilfu_uuid);
in_f->ilf_blkno = in_f64->ilf_blkno;
in_f->ilf_len = in_f64->ilf_len;
in_f->ilf_boffset = in_f64->ilf_boffset;
return 0;
}
return -EFSCORRUPTED;
} }

View File

@ -2515,7 +2515,7 @@ next_lv:
if (lv) if (lv)
vecp = lv->lv_iovecp; vecp = lv->lv_iovecp;
} }
if (record_cnt == 0 && ordered == false) { if (record_cnt == 0 && !ordered) {
if (!lv) if (!lv)
return 0; return 0;
break; break;

View File

@ -704,7 +704,7 @@ xfs_mountfs(
xfs_set_maxicount(mp); xfs_set_maxicount(mp);
/* enable fail_at_unmount as default */ /* enable fail_at_unmount as default */
mp->m_fail_unmount = 1; mp->m_fail_unmount = true;
error = xfs_sysfs_init(&mp->m_kobj, &xfs_mp_ktype, NULL, mp->m_fsname); error = xfs_sysfs_init(&mp->m_kobj, &xfs_mp_ktype, NULL, mp->m_fsname);
if (error) if (error)

View File

@ -134,7 +134,7 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_STRUCT_SIZE(struct xfs_icreate_log, 28); XFS_CHECK_STRUCT_SIZE(struct xfs_icreate_log, 28);
XFS_CHECK_STRUCT_SIZE(struct xfs_ictimestamp, 8); XFS_CHECK_STRUCT_SIZE(struct xfs_ictimestamp, 8);
XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format_32, 52); XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format_32, 52);
XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format_64, 56); XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format, 56);
XFS_CHECK_STRUCT_SIZE(struct xfs_qoff_logformat, 20); XFS_CHECK_STRUCT_SIZE(struct xfs_qoff_logformat, 20);
XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header, 16); XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header, 16);
} }