diff options
author | Manuel Bentele | 2019-08-14 18:48:14 +0200 |
---|---|---|
committer | Manuel Bentele | 2019-08-14 18:48:14 +0200 |
commit | 36b5758d8f950dd1ebfd7381250b69f0dcc9efcc (patch) | |
tree | 64c475fd4442c8e55d27c45eca583f0b6e3216d0 /drivers/block/loop/loop_main.c | |
parent | block: loop: file_fmt_qcow: write decompressed data to correct position in bvec (diff) | |
download | kernel-qcow2-linux-36b5758d8f950dd1ebfd7381250b69f0dcc9efcc.tar.gz kernel-qcow2-linux-36b5758d8f950dd1ebfd7381250b69f0dcc9efcc.tar.xz kernel-qcow2-linux-36b5758d8f950dd1ebfd7381250b69f0dcc9efcc.zip |
block: loop: fix backward compatibility for loop_info structure
While using the new loop kernel module with file format support and an old
losetup without file format support, problems occur. If losetup wants to have
the loop_info structure filled by the loop kernel module, the kernel module
writes across the boundaries of the old structure.
A new flag called LO_FLAGS_FILE_FMT is introduced to specify the support of the
loop_info extension for the file format support. If the flag is set before an
- LOOP_GET_STATUS
- LOOP_GET_STATUS64
- LOOP_SET_STATUS
- LOOP_SET_STATUS64
ioctl call, the caller informs the kernel module about the usage of the extended
loop_info structure. The module cna access the extension. Older versions of
losetup do not set the flag by default and the loop module preserves the
backward compatibility and do not write across the boundaries of the structure.
Signed-off-by: Manuel Bentele <development@manuel-bentele.de>
Diffstat (limited to 'drivers/block/loop/loop_main.c')
-rw-r--r-- | drivers/block/loop/loop_main.c | 130 |
1 files changed, 120 insertions, 10 deletions
diff --git a/drivers/block/loop/loop_main.c b/drivers/block/loop/loop_main.c index 1edc76115b18..726d4363b56f 100644 --- a/drivers/block/loop/loop_main.c +++ b/drivers/block/loop/loop_main.c @@ -1163,9 +1163,22 @@ loop_set_status_old(struct loop_device *lo, const struct loop_info __user *arg) { struct loop_info info; struct loop_info64 info64; + int err; - if (copy_from_user(&info, arg, sizeof (struct loop_info))) + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info, arg, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + if (err) return -EFAULT; + + if (info.lo_flags & LO_FLAGS_FILE_FMT) { + /* copy everything from the user space */ + err = copy_from_user(&info, arg, sizeof(info)); + if (err) + return -EFAULT; + } + loop_info64_from_old(&info, &info64); return loop_set_status(lo, &info64); } @@ -1174,9 +1187,22 @@ static int loop_set_status64(struct loop_device *lo, const struct loop_info64 __user *arg) { struct loop_info64 info64; + int err; - if (copy_from_user(&info64, arg, sizeof (struct loop_info64))) + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info64, arg, + sizeof(info64) - sizeof(info64.lo_file_fmt_type)); + if (err) return -EFAULT; + + if (info64.lo_flags & LO_FLAGS_FILE_FMT) { + /* copy everything from the user space */ + err = copy_from_user(&info64, arg, sizeof(info64)); + if (err) + return -EFAULT; + } + return loop_set_status(lo, &info64); } @@ -1184,15 +1210,37 @@ static int loop_get_status_old(struct loop_device *lo, struct loop_info __user *arg) { struct loop_info info; struct loop_info64 info64; + int lo_flags; int err; if (!arg) return -EINVAL; + + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info, arg, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + if (err) + return -EFAULT; + + lo_flags = info.lo_flags; + err = loop_get_status(lo, &info64); if (!err) err = loop_info64_to_old(&info64, &info); - if (!err && copy_to_user(arg, &info, sizeof(info))) - err = -EFAULT; + + if (lo_flags & LO_FLAGS_FILE_FMT) { + /* copy entire structure to user space because file format + * support is available */ + err = copy_to_user(arg, &info, sizeof(info)); + } else { + /* copy normal structure to user space */ + err = copy_to_user(arg, &info, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + } + + if (err) + return -EFAULT; return err; } @@ -1200,13 +1248,37 @@ loop_get_status_old(struct loop_device *lo, struct loop_info __user *arg) { static int loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) { struct loop_info64 info64; + u32 lo_flags; int err; if (!arg) return -EINVAL; + + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info64, arg, + sizeof(info64) - sizeof(info64.lo_file_fmt_type)); + if (err) + return -EFAULT; + + lo_flags = info64.lo_flags; + err = loop_get_status(lo, &info64); - if (!err && copy_to_user(arg, &info64, sizeof(info64))) - err = -EFAULT; + if (err) + return -EFAULT; + + if (lo_flags & LO_FLAGS_FILE_FMT) { + /* copy entire structure to user space because file format + * support is available */ + err = copy_to_user(arg, &info64, sizeof(info64)); + } else { + /* copy normal structure to user space */ + err = copy_to_user(arg, &info64, + sizeof(info64) - sizeof(info64.lo_file_fmt_type)); + } + + if (err) + return -EFAULT; return err; } @@ -1354,7 +1426,8 @@ struct compat_loop_info { unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ compat_ulong_t lo_init[2]; char reserved[4]; -}; + compat_int_t lo_file_fmt_type; +} __attribute__((packed)); /* * Transfer 32-bit compatibility structure in userspace to 64-bit loop info @@ -1365,10 +1438,22 @@ loop_info64_from_compat(const struct compat_loop_info __user *arg, struct loop_info64 *info64) { struct compat_loop_info info; + int err; - if (copy_from_user(&info, arg, sizeof(info))) + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info, arg, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + if (err) return -EFAULT; + if (info.lo_flags & LO_FLAGS_FILE_FMT) { + /* copy everything from the user space */ + err = copy_from_user(&info, arg, sizeof(info)); + if (err) + return -EFAULT; + } + memset(info64, 0, sizeof(*info64)); info64->lo_number = info.lo_number; info64->lo_device = info.lo_device; @@ -1381,6 +1466,7 @@ loop_info64_from_compat(const struct compat_loop_info __user *arg, info64->lo_flags = info.lo_flags; info64->lo_init[0] = info.lo_init[0]; info64->lo_init[1] = info.lo_init[1]; + info64->lo_file_fmt_type = info.lo_file_fmt_type; if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI) memcpy(info64->lo_crypt_name, info.lo_name, LO_NAME_SIZE); else @@ -1398,6 +1484,17 @@ loop_info64_to_compat(const struct loop_info64 *info64, struct compat_loop_info __user *arg) { struct compat_loop_info info; + compat_int_t lo_flags; + int err; + + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info, arg, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + if (err) + return -EFAULT; + + lo_flags = info.lo_flags; memset(&info, 0, sizeof(info)); info.lo_number = info64->lo_number; @@ -1410,6 +1507,7 @@ loop_info64_to_compat(const struct loop_info64 *info64, info.lo_flags = info64->lo_flags; info.lo_init[0] = info64->lo_init[0]; info.lo_init[1] = info64->lo_init[1]; + info.lo_file_fmt_type = info64->lo_file_fmt_type; if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI) memcpy(info.lo_name, info64->lo_crypt_name, LO_NAME_SIZE); else @@ -1422,11 +1520,23 @@ loop_info64_to_compat(const struct loop_info64 *info64, info.lo_inode != info64->lo_inode || info.lo_offset != info64->lo_offset || info.lo_init[0] != info64->lo_init[0] || - info.lo_init[1] != info64->lo_init[1]) + info.lo_init[1] != info64->lo_init[1] || + info.lo_file_fmt_type != info64->lo_file_fmt_type) return -EOVERFLOW; - if (copy_to_user(arg, &info, sizeof(info))) + if (lo_flags & LO_FLAGS_FILE_FMT) { + /* copy entire structure to user space because file format + * support is available */ + err = copy_to_user(arg, &info, sizeof(info)); + } else { + /* copy normal structure to user space */ + err = copy_to_user(arg, &info, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + } + + if (err) return -EFAULT; + return 0; } |