Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

Pull xfs fix from Darrick Wong:
"Here's a single fix for -rc3 to improve input validation on inline
directory data to prevent buffer overruns due to corrupt metadata"

* tag 'xfs-4.11-fixes-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
xfs: verify inline directory data forks

+122 -18
+2
fs/xfs/libxfs/xfs_dir2_priv.h
··· 125 125 extern int xfs_dir2_sf_lookup(struct xfs_da_args *args); 126 126 extern int xfs_dir2_sf_removename(struct xfs_da_args *args); 127 127 extern int xfs_dir2_sf_replace(struct xfs_da_args *args); 128 + extern int xfs_dir2_sf_verify(struct xfs_mount *mp, struct xfs_dir2_sf_hdr *sfp, 129 + int size); 128 130 129 131 /* xfs_dir2_readdir.c */ 130 132 extern int xfs_readdir(struct xfs_inode *dp, struct dir_context *ctx,
+87
fs/xfs/libxfs/xfs_dir2_sf.c
··· 629 629 } 630 630 #endif /* DEBUG */ 631 631 632 + /* Verify the consistency of an inline directory. */ 633 + int 634 + xfs_dir2_sf_verify( 635 + struct xfs_mount *mp, 636 + struct xfs_dir2_sf_hdr *sfp, 637 + int size) 638 + { 639 + struct xfs_dir2_sf_entry *sfep; 640 + struct xfs_dir2_sf_entry *next_sfep; 641 + char *endp; 642 + const struct xfs_dir_ops *dops; 643 + xfs_ino_t ino; 644 + int i; 645 + int i8count; 646 + int offset; 647 + __uint8_t filetype; 648 + 649 + dops = xfs_dir_get_ops(mp, NULL); 650 + 651 + /* 652 + * Give up if the directory is way too short. 653 + */ 654 + XFS_WANT_CORRUPTED_RETURN(mp, size > 655 + offsetof(struct xfs_dir2_sf_hdr, parent)); 656 + XFS_WANT_CORRUPTED_RETURN(mp, size >= 657 + xfs_dir2_sf_hdr_size(sfp->i8count)); 658 + 659 + endp = (char *)sfp + size; 660 + 661 + /* Check .. entry */ 662 + ino = dops->sf_get_parent_ino(sfp); 663 + i8count = ino > XFS_DIR2_MAX_SHORT_INUM; 664 + XFS_WANT_CORRUPTED_RETURN(mp, !xfs_dir_ino_validate(mp, ino)); 665 + offset = dops->data_first_offset; 666 + 667 + /* Check all reported entries */ 668 + sfep = xfs_dir2_sf_firstentry(sfp); 669 + for (i = 0; i < sfp->count; i++) { 670 + /* 671 + * struct xfs_dir2_sf_entry has a variable length. 672 + * Check the fixed-offset parts of the structure are 673 + * within the data buffer. 674 + */ 675 + XFS_WANT_CORRUPTED_RETURN(mp, 676 + ((char *)sfep + sizeof(*sfep)) < endp); 677 + 678 + /* Don't allow names with known bad length. */ 679 + XFS_WANT_CORRUPTED_RETURN(mp, sfep->namelen > 0); 680 + XFS_WANT_CORRUPTED_RETURN(mp, sfep->namelen < MAXNAMELEN); 681 + 682 + /* 683 + * Check that the variable-length part of the structure is 684 + * within the data buffer. The next entry starts after the 685 + * name component, so nextentry is an acceptable test. 686 + */ 687 + next_sfep = dops->sf_nextentry(sfp, sfep); 688 + XFS_WANT_CORRUPTED_RETURN(mp, endp >= (char *)next_sfep); 689 + 690 + /* Check that the offsets always increase. */ 691 + XFS_WANT_CORRUPTED_RETURN(mp, 692 + xfs_dir2_sf_get_offset(sfep) >= offset); 693 + 694 + /* Check the inode number. */ 695 + ino = dops->sf_get_ino(sfp, sfep); 696 + i8count += ino > XFS_DIR2_MAX_SHORT_INUM; 697 + XFS_WANT_CORRUPTED_RETURN(mp, !xfs_dir_ino_validate(mp, ino)); 698 + 699 + /* Check the file type. */ 700 + filetype = dops->sf_get_ftype(sfep); 701 + XFS_WANT_CORRUPTED_RETURN(mp, filetype < XFS_DIR3_FT_MAX); 702 + 703 + offset = xfs_dir2_sf_get_offset(sfep) + 704 + dops->data_entsize(sfep->namelen); 705 + 706 + sfep = next_sfep; 707 + } 708 + XFS_WANT_CORRUPTED_RETURN(mp, i8count == sfp->i8count); 709 + XFS_WANT_CORRUPTED_RETURN(mp, (void *)sfep == (void *)endp); 710 + 711 + /* Make sure this whole thing ought to be in local format. */ 712 + XFS_WANT_CORRUPTED_RETURN(mp, offset + 713 + (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + 714 + (uint)sizeof(xfs_dir2_block_tail_t) <= mp->m_dir_geo->blksize); 715 + 716 + return 0; 717 + } 718 + 632 719 /* 633 720 * Create a new (shortform) directory. 634 721 */
+23 -3
fs/xfs/libxfs/xfs_inode_fork.c
··· 33 33 #include "xfs_trace.h" 34 34 #include "xfs_attr_sf.h" 35 35 #include "xfs_da_format.h" 36 + #include "xfs_da_btree.h" 37 + #include "xfs_dir2_priv.h" 36 38 37 39 kmem_zone_t *xfs_ifork_zone; 38 40 ··· 322 320 int whichfork, 323 321 int size) 324 322 { 323 + int error; 325 324 326 325 /* 327 326 * If the size is unreasonable, then something ··· 337 334 XFS_CORRUPTION_ERROR("xfs_iformat_local", XFS_ERRLEVEL_LOW, 338 335 ip->i_mount, dip); 339 336 return -EFSCORRUPTED; 337 + } 338 + 339 + if (S_ISDIR(VFS_I(ip)->i_mode) && whichfork == XFS_DATA_FORK) { 340 + error = xfs_dir2_sf_verify(ip->i_mount, 341 + (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip), 342 + size); 343 + if (error) 344 + return error; 340 345 } 341 346 342 347 xfs_init_local_fork(ip, whichfork, XFS_DFORK_PTR(dip, whichfork), size); ··· 867 856 * In these cases, the format always takes precedence, because the 868 857 * format indicates the current state of the fork. 869 858 */ 870 - void 859 + int 871 860 xfs_iflush_fork( 872 861 xfs_inode_t *ip, 873 862 xfs_dinode_t *dip, ··· 877 866 char *cp; 878 867 xfs_ifork_t *ifp; 879 868 xfs_mount_t *mp; 869 + int error; 880 870 static const short brootflag[2] = 881 871 { XFS_ILOG_DBROOT, XFS_ILOG_ABROOT }; 882 872 static const short dataflag[2] = ··· 886 874 { XFS_ILOG_DEXT, XFS_ILOG_AEXT }; 887 875 888 876 if (!iip) 889 - return; 877 + return 0; 890 878 ifp = XFS_IFORK_PTR(ip, whichfork); 891 879 /* 892 880 * This can happen if we gave up in iformat in an error path, ··· 894 882 */ 895 883 if (!ifp) { 896 884 ASSERT(whichfork == XFS_ATTR_FORK); 897 - return; 885 + return 0; 898 886 } 899 887 cp = XFS_DFORK_PTR(dip, whichfork); 900 888 mp = ip->i_mount; 901 889 switch (XFS_IFORK_FORMAT(ip, whichfork)) { 902 890 case XFS_DINODE_FMT_LOCAL: 891 + if (S_ISDIR(VFS_I(ip)->i_mode) && whichfork == XFS_DATA_FORK) { 892 + error = xfs_dir2_sf_verify(mp, 893 + (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data, 894 + ifp->if_bytes); 895 + if (error) 896 + return error; 897 + } 903 898 if ((iip->ili_fields & dataflag[whichfork]) && 904 899 (ifp->if_bytes > 0)) { 905 900 ASSERT(ifp->if_u1.if_data != NULL); ··· 959 940 ASSERT(0); 960 941 break; 961 942 } 943 + return 0; 962 944 } 963 945 964 946 /*
+1 -1
fs/xfs/libxfs/xfs_inode_fork.h
··· 140 140 struct xfs_ifork *xfs_iext_state_to_fork(struct xfs_inode *ip, int state); 141 141 142 142 int xfs_iformat_fork(struct xfs_inode *, struct xfs_dinode *); 143 - void xfs_iflush_fork(struct xfs_inode *, struct xfs_dinode *, 143 + int xfs_iflush_fork(struct xfs_inode *, struct xfs_dinode *, 144 144 struct xfs_inode_log_item *, int); 145 145 void xfs_idestroy_fork(struct xfs_inode *, int); 146 146 void xfs_idata_realloc(struct xfs_inode *, int, int);
-11
fs/xfs/xfs_dir2_readdir.c
··· 71 71 struct xfs_da_geometry *geo = args->geo; 72 72 73 73 ASSERT(dp->i_df.if_flags & XFS_IFINLINE); 74 - /* 75 - * Give up if the directory is way too short. 76 - */ 77 - if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) { 78 - ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); 79 - return -EIO; 80 - } 81 - 82 74 ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); 83 75 ASSERT(dp->i_df.if_u1.if_data != NULL); 84 76 85 77 sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 86 - 87 - if (dp->i_d.di_size < xfs_dir2_sf_hdr_size(sfp->i8count)) 88 - return -EFSCORRUPTED; 89 78 90 79 /* 91 80 * If the block number in the offset is out of range, we're done.
+9 -3
fs/xfs/xfs_inode.c
··· 3475 3475 struct xfs_inode_log_item *iip = ip->i_itemp; 3476 3476 struct xfs_dinode *dip; 3477 3477 struct xfs_mount *mp = ip->i_mount; 3478 + int error; 3478 3479 3479 3480 ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); 3480 3481 ASSERT(xfs_isiflocked(ip)); ··· 3558 3557 if (ip->i_d.di_flushiter == DI_MAX_FLUSH) 3559 3558 ip->i_d.di_flushiter = 0; 3560 3559 3561 - xfs_iflush_fork(ip, dip, iip, XFS_DATA_FORK); 3562 - if (XFS_IFORK_Q(ip)) 3563 - xfs_iflush_fork(ip, dip, iip, XFS_ATTR_FORK); 3560 + error = xfs_iflush_fork(ip, dip, iip, XFS_DATA_FORK); 3561 + if (error) 3562 + return error; 3563 + if (XFS_IFORK_Q(ip)) { 3564 + error = xfs_iflush_fork(ip, dip, iip, XFS_ATTR_FORK); 3565 + if (error) 3566 + return error; 3567 + } 3564 3568 xfs_inobp_check(mp, bp); 3565 3569 3566 3570 /*