A pseudo-filesystem, such as tmpfs, can have anything at all in its
mnt_fsname entry. Normally, it is just "tmpfs", like this:
tmpfs /tmp tmpfs rw,relatime,inode64 0 0
^^^^^
but in a pathological or malicious case, a system administrator can
specify a block device as its mnt_fsname which is the same as some
other block device. For example:
/dev/loop0 /tmp/test-tmpfs tmpfs rw,relatime,inode64 0 0
^^^^^^^^^^
/dev/loop0 /tmp/test-mnt ext4 rw,relatime 0 0
In this case, ext2fs_check_mount_point() may erroneously return that
the mountpoint for the file system on /dev/loop0 is mounted on
/tmp/test-tmpfs, instead of the correct /tmp/test-mnt. This causes
problems for resize2fs, since in order to do an online resize, it
needs to open the directory where the file system is mounted, and
trigger the online resize ioctl. If it opens the incorrect directory,
then resize2fs will fail.
So we need to add some additional checking to make sure that
directory's st_dev matches the block device's st_rdev field.
An example shell script which reproduces the problem fixed by this
commit is as follows:
loop_file=/tmp/foo.img
tmpfs_dir=/tmp/test-tmpfs
mnt_dir=/tmp/test-mnt
mkdir -p $tmpfs_dir $mnt_dir
dd if=/dev/zero of=$loop_file bs=1k count=65536
test_dev=$(losetup --show -f $loop_file)
mke2fs -t ext4 -F -b 1024 $test_dev 32768
mount -t tmpfs $test_dev $tmpfs_dir # create the evil /proc/mounts entry
mount -t ext4 $test_dev $mnt_dir
ln -f ${test_dev} ${test_dev}-ln
resize2fs ${test_dev}-ln
[ Fixed up the corrupted patch and rewrote the commit description to
be more clear -- tytso ]
Signed-off-by: zhanchengbin <zhanchengbin1@huawei.com>
Signed-off-by: Zhiqiang Liu <liuzhiqiang26@huawei.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
int *mount_flags, char *mtpt, int mtlen)
{
struct mntent *mnt;
- struct stat st_buf;
+ struct stat st_buf, dir_st_buf;
errcode_t retval = 0;
dev_t file_dev=0, file_rdev=0;
ino_t file_ino=0;
if (stat(mnt->mnt_fsname, &st_buf) == 0) {
if (ext2fsP_is_disk_device(st_buf.st_mode)) {
#ifndef __GNU__
- if (file_rdev && (file_rdev == st_buf.st_rdev))
- break;
+ if (file_rdev &&
+ (file_rdev == st_buf.st_rdev)) {
+ if (stat(mnt->mnt_dir,
+ &dir_st_buf) != 0)
+ continue;
+ if (file_rdev == dir_st_buf.st_dev)
+ break;
+ }
if (check_loop_mounted(mnt->mnt_fsname,
st_buf.st_rdev, file_dev,
file_ino) == 1)