Skip to contentSkip to conversation

xfsprogs: fix use after free in inode_item_done()

PATCH In review Eric Sandeen 5 participants 10 messages Mar 3, 2014 – Mar 5, 2014 1 message not in the archive

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 xfsprogs: fix use after free in inode_item_done()

Commit "3a19fb7 libxfs: stop caching inode structures" introduced a use after free.

libxfs_iput() already does the check for ip->i_itemp, and a kmem_zone_free() if it's present, and then frees the ip pointer. Re-checking ip->i_itemp after the libxfs_iput call will access the freed ip pointer, as will setting ip_>i_itemp to NULL.

Simply remove the offending code to fix this up.

---

libxfs/trans.c +0 -7
index 6c9d202..2e6720e 100644
@@ -694,7 +694,6 @@ inode_item_done(
 	xfs_mount_t		*mp;
 	xfs_buf_t		*bp;
 	int			error;
-	extern kmem_zone_t	*xfs_ili_zone;
 
 	ip = iip->ili_inode;
 	mp = iip->ili_item.li_mountp;
@@ -739,12 +738,6 @@ ili_done:
 	} else {
 		libxfs_iput(ip, 0);
 	}
-
-	if (ip->i_itemp)
-		kmem_zone_free(xfs_ili_zone, ip->i_itemp);
-	else
-		ASSERT(0);
-	ip->i_itemp = NULL;
 }
 
 static void

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#1 lore raw
Quoted 9 lines · Eric Sandeen

Commit "3a19fb7 libxfs: stop caching inode structures" introduced a use after free.

libxfs_iput() already does the check for ip->i_itemp, and a kmem_zone_free() if it's present, and then frees the ip pointer. Re-checking ip->i_itemp after the libxfs_iput call will access the freed ip pointer, as will setting ip_>i_itemp to NULL.

Simply remove the offending code to fix this up.

which leaves the rest of the ili_done: code looking a little strange.

can you convert that now to be:

ili_done:

	if (iip->ili_lock_flags) {
		iip->ili_lock_flags = 0;
		return;
	}
	/* free the inode */
	libxfs_iput(ip, 0);
}

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#2 lore raw
Quoted 16 lines · Dave Chinner
Quoted 9 lines · Eric Sandeen

Commit "3a19fb7 libxfs: stop caching inode structures" introduced a use after free.

libxfs_iput() already does the check for ip->i_itemp, and a kmem_zone_free() if it's present, and then frees the ip pointer. Re-checking ip->i_itemp after the libxfs_iput call will access the freed ip pointer, as will setting ip_>i_itemp to NULL.

Simply remove the offending code to fix this up.

which leaves the rest of the ili_done: code looking a little strange.

can you convert that now to be:

Quoted 8 lines

ili_done:

	if (iip->ili_lock_flags) {
		iip->ili_lock_flags = 0;
		return;
	}
	/* free the inode */
	libxfs_iput(ip, 0);
}

yeah, I actually had that first. Not sure why I didn't go with it ;)

(Still looks strange to my untrained eye; "if lock flags are set, unset them and don't free the inode, otherwise free it")

Anyway, I'll resend. No need to educate me on these details, for now. ;)

-Eric

Quoted 4 lines

Cheers,

Dave.

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#3 lore raw
Quoted 26 lines · Eric Sandeen
Quoted 16 lines · Dave Chinner
Quoted 9 lines · Eric Sandeen

Commit "3a19fb7 libxfs: stop caching inode structures" introduced a use after free.

libxfs_iput() already does the check for ip->i_itemp, and a kmem_zone_free() if it's present, and then frees the ip pointer. Re-checking ip->i_itemp after the libxfs_iput call will access the freed ip pointer, as will setting ip_>i_itemp to NULL.

Simply remove the offending code to fix this up.

which leaves the rest of the ili_done: code looking a little strange.

can you convert that now to be:

Quoted 8 lines

ili_done:

	if (iip->ili_lock_flags) {
		iip->ili_lock_flags = 0;
		return;
	}
	/* free the inode */
	libxfs_iput(ip, 0);
}
Quoted 3 lines

yeah, I actually had that first. Not sure why I didn't go with it ;)

(Still looks strange to my untrained eye; "if lock flags are set, unset them and don't free the inode, otherwise free it")

If the lock falgs are set, it means the caller expects the transaction commit to return the inode to it in a locked state. Exactly the same semantics as the kernel code - the lock flags track how the transaction releases the inode...

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#5 lore raw

PATCH v2 1/1 xfsprogs: fix use after free in inode_item_done()

In reply to Eric Sandeen

Commit "3a19fb7 libxfs: stop caching inode structures" introduced a use after free.

libxfs_iput() already does the check for ip->i_itemp, and a kmem_zone_free() if it's present, and then frees the ip pointer. Re-checking ip->i_itemp after the libxfs_iput call will access the freed ip pointer, as will setting ip_>i_itemp to NULL.

Simply remove the offending code to fix this up.

---

V2: Make it prettier based on Dave's comments.

libxfs/trans.c +2 -9
index 6c9d202..c443863 100644
@@ -694,7 +694,6 @@ inode_item_done(
 	xfs_mount_t		*mp;
 	xfs_buf_t		*bp;
 	int			error;
-	extern kmem_zone_t	*xfs_ili_zone;
 
 	ip = iip->ili_inode;
 	mp = iip->ili_item.li_mountp;
@@ -736,15 +735,9 @@ ili_done:
 	if (iip->ili_lock_flags) {
 		iip->ili_lock_flags = 0;
 		return;
-	} else {
-		libxfs_iput(ip, 0);
 	}
-
-	if (ip->i_itemp)
-		kmem_zone_free(xfs_ili_zone, ip->i_itemp);
-	else
-		ASSERT(0);
-	ip->i_itemp = NULL;
+	/* free the inode */
+	libxfs_iput(ip, 0);
 }
 
 static void

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

Reviewed-by on v2 1/1
#6 lore raw
Quoted 8 lines · Eric Sandeen

Commit "3a19fb7 libxfs: stop caching inode structures" introduced a use after free.

libxfs_iput() already does the check for ip->i_itemp, and a kmem_zone_free() if it's present, and then frees the ip pointer. Re-checking ip->i_itemp after the libxfs_iput call will access the freed ip pointer, as will setting ip_>i_itemp to NULL.

Quoted 6 lines

Simply remove the offending code to fix this up.

---

V2: Make it prettier based on Dave's comments.

looks good.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#4 lore raw
In reply to Eric Sandeen
Quoted 1 line · Eric Sandeen

On 3/3/14, 4:36 PM, Dave Chinner wrote:

...

Quoted 14 lines
Quoted 5 lines

which leaves the rest of the ili_done: code looking a little strange.

can you convert that now to be:

Quoted 8 lines

ili_done:

	if (iip->ili_lock_flags) {
		iip->ili_lock_flags = 0;
		return;
	}
	/* free the inode */
	libxfs_iput(ip, 0);
}
Quoted 4 lines

yeah, I actually had that first. Not sure why I didn't go with it ;)

(Still looks strange to my untrained eye; "if lock flags are set, unset them and don't free the inode, otherwise free it")

I'd be tempted to write:

ili_done:

	if (iip->ili_lock_flags == 0) /* don't return locked inode */
		libxfs_iput(ip, 0);

	iip->ili_lock_flags = 0;
Signature · 6 lines
Roger Willcocks <[email protected]>

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#7 lore raw
In reply to Eric Sandeen
Quoted 2 lines · Eric Sandeen

Commit "3a19fb7 libxfs: stop caching inode structures" introduced a use after free.

I see the use after free, but I don't see how I would have introduced it in that commit. Before that libxfs_iput already was freeing the inode through the cache code, now we do it directly.

libxfs/trans.c ili_done: · line 741

Quoted 11 lines
libxfs/trans.c +0 -6
@@ -739,12 +738,6 @@ ili_done:
 	} else {
 		libxfs_iput(ip, 0);
 	}
-
-	if (ip->i_itemp)
-		kmem_zone_free(xfs_ili_zone, ip->i_itemp);
-	else
-		ASSERT(0);
-	ip->i_itemp = NULL;
 }

Seems like inode_item_done should call into inode_item_unlock, which factors the exact sequence we want into a nice helper.

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#8 lore raw
Quoted 7 lines · Christoph Hellwig
Quoted 2 lines · Eric Sandeen

Commit "3a19fb7 libxfs: stop caching inode structures" introduced a use after free.

I see the use after free, but I don't see how I would have introduced it in that commit. Before that libxfs_iput already was freeing the inode through the cache code, now we do it directly.

Ok, sorry if I wrongly implicated that commit.

Quoted 6 lines
Quoted 6 lines
libxfs/trans.c +0 -2
@@ -739,12 +738,6 @@ ili_done:
 	} else {
 		libxfs_iput(ip, 0);
 	}
-
-	if (ip->i_itemp)
Quoted 8 lines

libxfs/trans.c ili_done: · line 741

Quoted 5 lines
libxfs/trans.c +0 -4
-		kmem_zone_free(xfs_ili_zone, ip->i_itemp);
-	else
-		ASSERT(0);
-	ip->i_itemp = NULL;
 }

Seems like inode_item_done should call into inode_item_unlock, which factors the exact sequence we want into a nice helper.

Yeah, that does seem better! Thanks for spotting that.

The difference when calling inode_item_unlock is a bit more zeroing-out:

        ip->i_transp = NULL;

        iip->ili_flags = 0;

I'm not sure of the implications of that offhand, TBH.

Dave, hold off on my commit I guess ;)

-Eric

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#10 lore raw
In reply to a message not in the archive
Quoted 7 lines · Christoph Hellwig
Quoted 6 lines · Eric Sandeen

Yeah, that does seem better! Thanks for spotting that.

The difference when calling inode_item_unlock is a bit more zeroing-out:

        ip->i_transp = NULL;
Quoted 8 lines
Quoted 5 lines
        iip->ili_flags = 0;

I'm not sure of the implications of that offhand, TBH.

Dave, hold off on my commit I guess ;)

i_itransp nulling is obviously harmless as we are freeing the inode right after.

Not in all cases, right?

static void
inode_item_unlock(
xfs_inode_log_item_t *iip)

{
        xfs_inode_t             *ip = iip->ili_inode;

        /* Clear the transaction pointer in the inode. */
        ip->i_transp = NULL;

        iip->ili_flags = 0;
        if (!iip->ili_lock_flags)
                libxfs_iput(ip, 0);
        else
                iip->ili_lock_flags = 0;  // <-- not here.
}
Quoted 2 lines

ili_flags is always 0 in libxfs and we might as well just remove it.

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs