summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManuel Bentele2019-08-14 18:48:14 +0200
committerManuel Bentele2019-08-21 22:04:34 +0200
commit5bf58ecf30c12aaf6130332188facb5225b1c486 (patch)
tree8e68a8091339d795eb893c7f682a04c3b3983b46
parentblock: loop: file_fmt_qcow: write decompressed data to correct position in bvec (diff)
downloadkernel-qcow2-linux-5bf58ecf30c12aaf6130332188facb5225b1c486.tar.gz
kernel-qcow2-linux-5bf58ecf30c12aaf6130332188facb5225b1c486.tar.xz
kernel-qcow2-linux-5bf58ecf30c12aaf6130332188facb5225b1c486.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>
-rw-r--r--drivers/block/loop/loop_main.c130
-rw-r--r--include/uapi/linux/loop.h5
2 files changed, 123 insertions, 12 deletions
diff --git a/drivers/block/loop/loop_main.c b/drivers/block/loop/loop_main.c
index 1150956974fb..f37ed6e51a45 100644
--- a/drivers/block/loop/loop_main.c
+++ b/drivers/block/loop/loop_main.c
@@ -1146,9 +1146,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);
}
@@ -1157,9 +1170,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);
}
@@ -1167,15 +1193,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;
}
@@ -1183,13 +1231,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;
}
@@ -1337,7 +1409,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
@@ -1348,10 +1421,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;
@@ -1364,6 +1449,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
@@ -1381,6 +1467,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;
@@ -1393,6 +1490,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
@@ -1405,11 +1503,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;
}
diff --git a/include/uapi/linux/loop.h b/include/uapi/linux/loop.h
index f2cf1ff2ec1b..07a9db82d2bd 100644
--- a/include/uapi/linux/loop.h
+++ b/include/uapi/linux/loop.h
@@ -23,6 +23,7 @@ enum {
LO_FLAGS_AUTOCLEAR = 4,
LO_FLAGS_PARTSCAN = 8,
LO_FLAGS_DIRECT_IO = 16,
+ LO_FLAGS_FILE_FMT = 32 /* indicates file format support */
};
#include <asm/posix_types.h> /* for __kernel_old_dev_t */
@@ -60,7 +61,7 @@ struct loop_info64 {
__u8 lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
__u64 lo_init[2];
__u32 lo_file_fmt_type;
-};
+} __attribute__((packed));
/*
* Loop filter types
@@ -85,7 +86,7 @@ struct loop_info64 {
#define LO_FILE_FMT_QCOW 1
#define LO_FILE_FMT_VDI 2
#define LO_FILE_FMT_VMDK 3
-#define MAX_LO_FILE_FMT 5
+#define MAX_LO_FILE_FMT LO_FILE_FMT_VMDK + 1
/*
* IOCTL commands --- we will commandeer 0x4C ('L')