[XFS] Fix use-after-free with buffers

We have a use-after-free issue where log completions access buffers via
the buffer log item and the buffer has already been freed. Fix this by
taking a reference on the buffer when attaching the buffer log item and
release the hold when the buffer log item is detached and we no longer
need the buffer. Also create a new function xfs_buf_item_free() to combine
some common code.

SGI-PV: 985757

SGI-Modid: xfs-linux-melb:xfs-kern:32025a

Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Signed-off-by: Christoph Hellwig <hch@infradead.org>

authored by Lachlan McIlroy and committed by Lachlan McIlroy e1f5dbd7 f9114eba

+20 -24
+20 -24
fs/xfs/xfs_buf_item.c
··· 732 732 bip->bli_item.li_ops = &xfs_buf_item_ops; 733 733 bip->bli_item.li_mountp = mp; 734 734 bip->bli_buf = bp; 735 + xfs_buf_hold(bp); 735 736 bip->bli_format.blf_type = XFS_LI_BUF; 736 737 bip->bli_format.blf_blkno = (__int64_t)XFS_BUF_ADDR(bp); 737 738 bip->bli_format.blf_len = (ushort)BTOBB(XFS_BUF_COUNT(bp)); ··· 868 867 return (bip->bli_flags & XFS_BLI_DIRTY); 869 868 } 870 869 870 + STATIC void 871 + xfs_buf_item_free( 872 + xfs_buf_log_item_t *bip) 873 + { 874 + #ifdef XFS_TRANS_DEBUG 875 + kmem_free(bip->bli_orig); 876 + kmem_free(bip->bli_logged); 877 + #endif /* XFS_TRANS_DEBUG */ 878 + 879 + #ifdef XFS_BLI_TRACE 880 + ktrace_free(bip->bli_trace); 881 + #endif 882 + kmem_zone_free(xfs_buf_item_zone, bip); 883 + } 884 + 871 885 /* 872 886 * This is called when the buf log item is no longer needed. It should 873 887 * free the buf log item associated with the given buffer and clear ··· 903 887 (XFS_BUF_IODONE_FUNC(bp) != NULL)) { 904 888 XFS_BUF_CLR_IODONE_FUNC(bp); 905 889 } 906 - 907 - #ifdef XFS_TRANS_DEBUG 908 - kmem_free(bip->bli_orig); 909 - bip->bli_orig = NULL; 910 - kmem_free(bip->bli_logged); 911 - bip->bli_logged = NULL; 912 - #endif /* XFS_TRANS_DEBUG */ 913 - 914 - #ifdef XFS_BLI_TRACE 915 - ktrace_free(bip->bli_trace); 916 - #endif 917 - kmem_zone_free(xfs_buf_item_zone, bip); 890 + xfs_buf_rele(bp); 891 + xfs_buf_item_free(bip); 918 892 } 919 893 920 894 ··· 1126 1120 1127 1121 ASSERT(bip->bli_buf == bp); 1128 1122 1123 + xfs_buf_rele(bp); 1129 1124 mp = bip->bli_item.li_mountp; 1130 1125 1131 1126 /* ··· 1143 1136 * xfs_trans_delete_ail() drops the AIL lock. 1144 1137 */ 1145 1138 xfs_trans_delete_ail(mp, (xfs_log_item_t *)bip); 1146 - 1147 - #ifdef XFS_TRANS_DEBUG 1148 - kmem_free(bip->bli_orig); 1149 - bip->bli_orig = NULL; 1150 - kmem_free(bip->bli_logged); 1151 - bip->bli_logged = NULL; 1152 - #endif /* XFS_TRANS_DEBUG */ 1153 - 1154 - #ifdef XFS_BLI_TRACE 1155 - ktrace_free(bip->bli_trace); 1156 - #endif 1157 - kmem_zone_free(xfs_buf_item_zone, bip); 1139 + xfs_buf_item_free(bip); 1158 1140 } 1159 1141 1160 1142 #if defined(XFS_BLI_TRACE)