[PATCH] ovl: create UUIDs for file systems that do not set the superblock UUID

From: Colin King
Date: Wed Nov 06 2019 - 18:43:06 EST


From: Colin Ian King <colin.king@xxxxxxxxxxxxx>

Some file systems such as squashfs do not set the UUID in the
superblock resulting in a zero'd UUID. In cases were two or more
of these file systems are overlayed on the lower layer we can hit
overlay corruption issues because identical zero'd overlayfs UUIDs
are impossible to differentiate between. This can be fixed by
creating an overlayfs UUID based on the file system from the
superblock s_magic and s_dev fields. (This currently seems like
enough information to be able create a UUID, but the could be
scope to use other super block fields such as the pointer s_fs_info
but may need some obfuscation).

This issue can be reproduced with the following commands:

mkdir -p /cdrom
mount -t iso9660 -o ro,noatime /dev/sr0 /cdrom
sleep 1
mkdir -p /cow
mount -t tmpfs -o 'rw,noatime,mode=755' tmpfs /cow
mkdir -p /cow/upper
mkdir -p /cow/work
modprobe -q -b overlay
modprobe -q -b loop
dev=$(losetup -f)
mkdir -p /filesystem.squashfs
losetup $dev /cdrom/casper/filesystem.squashfs
mount -t squashfs -o ro,noatime $dev /filesystem.squashfs
dev=$(losetup -f)
mkdir -p /installer.squashfs
losetup $dev /cdrom/casper/installer.squashfs
mount -t squashfs -o ro,noatime $dev /installer.squashfs
mkdir -p /root-tmp
mount -t overlay -o 'upperdir=/cow/upper,lowerdir=/installer.squashfs:/filesystem.squashfs,workdir=/cow/work' /cow /root-tmp

FILE=/root-tmp/etc/.pwd.lock

echo foo > $FILE
cat $FILE
sync
echo 3 > /proc/sys/vm/drop_caches
cat $FILE

The output from cat $FILE:
cat: /root-tmp/etc/.pwd.lock: Input/output error

dmesg reports:
[ 42.415432] overlayfs: invalid origin (etc/.pwd.lock, ftype=8000, origin ftype=4000).

BugLink: https://bugs.launchpad.net/bugs/1824407
Signed-off-by: Colin Ian King <colin.king@xxxxxxxxxxxxx>
---
fs/overlayfs/copy_up.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index b801c6353100..a578db87936b 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -231,6 +231,7 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper)
void *buf;
int buflen = MAX_HANDLE_SZ;
uuid_t *uuid = &real->d_sb->s_uuid;
+ static const uuid_t z_uuid;

buf = kmalloc(buflen, GFP_KERNEL);
if (!buf)
@@ -272,7 +273,20 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper)
if (is_upper)
fh->flags |= OVL_FH_FLAG_PATH_UPPER;
fh->len = fh_len;
- fh->uuid = *uuid;
+
+ if (uuid_equal(uuid, &z_uuid)) {
+ /*
+ * An zero'd uuid indicates the uuid in the super block was
+ * not set by the file system, so fake one instead
+ */
+ struct super_block *sb = real->d_sb;
+
+ memcpy(&fh->uuid.b[0], &sb->s_magic, 8);
+ memcpy(&fh->uuid.b[8], &sb->s_dev, 8);
+ } else {
+ fh->uuid = *uuid;
+ }
+
memcpy(fh->fid, buf, buflen);

out:
--
2.20.1