xorl %eax, %eax

CVE-2013-1819: Linux kernel XFS _xfs_buf_find() NULL Pointer Dereference

with 2 comments

On 21 January 2013 Dave Chinner of Red Hat committed a change that fixes a NULL pointer dereference vulnerability in XFS filesystem. The below routine is located in fs/xfs/xfs_buf.c file.

 *	Finding and Reading Buffers

 *	Look up, and creates if absent, a lockable buffer for
 *	a given range of an inode.  The buffer is returned
 *	locked.	No I/O is implied by this call.
xfs_buf_t *
	struct xfs_buftarg	*btp,
	struct xfs_buf_map	*map,
	int			nmaps,
	xfs_buf_flags_t		flags,
	xfs_buf_t		*new_bp)
	size_t			numbytes;
	struct xfs_perag	*pag;
	/* get tree root */
	pag = xfs_perag_get(btp->bt_mount,
				xfs_daddr_to_agno(btp->bt_mount, blkno));

	/* walk tree */
	return bp;

First of all, the xfs_addr_to_agno() C macro is the following as defined in fs/xfs/xfs_mount.h header file.

#define xfs_daddr_to_agno(mp,d) \
        ((xfs_agnumber_t)(XFS_BB_TO_FSBT(mp, d) / (mp)->m_sb.sb_agblocks))

As Dave Chinner pointed out, if we try to walk a filesystem and the extent map has corrupted block number (out of range address) the call to xfs_perag_get() above will trigger a NULL pointer dereference.

 * Reference counting access wrappers to the perag structures.
 * Because we never free per-ag structures, the only thing we
 * have to protect against changes is the tree structure itself.
struct xfs_perag *
xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno)
        struct xfs_perag        *pag;
        int                     ref = 0;
        pag = radix_tree_lookup(&mp->m_perag_tree, agno);
        if (pag) {
               ASSERT(atomic_read(&pag->pag_ref) >= 0);
               ref = atomic_inc_return(&pag->pag_ref);
        trace_xfs_perag_get(mp, agno, ref, _RET_IP_);
        return pag;

The radix_tree_lookup() call will use the invalid block number ‘agblocks’ (size of an allocation group) as an index key to the ‘mp->m_perag_tree’ radix tree.

The fix to this bug was to add a new variable to the susceptible routine:

 	xfs_buf_t		*bp;
 	xfs_daddr_t		blkno = map[0].bm_bn;
+	xfs_daddr_t		eofs;
 	int			numblks = 0;

And write a check for the block number not being larger than the end of the filesystem.

 	ASSERT(!(BBTOB(blkno) & (xfs_off_t)btp->bt_smask));
+	/*
+	 * Corrupted block numbers can get through to here, unfortunately, so we
+	 * have to check that the buffer falls within the filesystem bounds.
+	 */
+	eofs = XFS_FSB_TO_BB(btp->bt_mount, btp->bt_mount->m_sb.sb_dblocks);
+	if (blkno >= eofs) {
+		/*
+		 * XXX (dgc): we should really be returning EFSCORRUPTED here,
+		 * but none of the higher level infrastructure supports
+		 * returning a specific error on buffer lookup failures.
+		 */
+		xfs_alert(btp->bt_mount,
+			  "%s: Block out of range: block 0x%llx, EOFS 0x%llx ",
+			  __func__, blkno, eofs);
+		return NULL;
+	}
 	/* get tree root */

Written by xorl

May 18, 2013 at 16:12

Posted in linux, vulnerabilities

2 Responses

Subscribe to comments with RSS.

  1. how do you exploit a null pointer dereference? can you show a link/example of this?


    May 19, 2013 at 14:14

  2. mall you can check spender’s exploits at: http://grsecurity.net/~spender/exploits/
    There are some excellent examples of NULL pointer dereference exploitation on recent vulnerabilities.


    May 19, 2013 at 21:59

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s