mtd: provide proper 32/64-bit compat_ioctl() support for BLKPG
Subsystems (3)
RFC 1/1 mtd: provide proper 32/64-bit compat_ioctl() support for BLKPG
After a bit of poking around wondering why my 32-bit user-space can't seem to send a proper ioctl(BLKPG) to an MTD on my 64-bit kernel (ARM64), I noticed that struct blkpg_ioctl_arg is actually pretty unsuitable for use in the ioctl() ABI, due to its use of raw pointers, and its lack of alignment/packing restrictions (32-bit arch'es tend to pack the 4 fields into 4 32-bit words, whereas 64-bit arch'es would add padding after the third int, and make this 6 32-bit words).
Anyway, this means BLKPG deserves some special compat_ioctl handling. Do the conversion in a small shim for MTD.
The same bug applies to block/ioctl.c, but I wanted to get some comments first. I can send a non-RFC with the same approach for the block subsystem. But then: which tree should it go in?
Tested only on MTD, with an ARM32 user space on an ARM64 kernel.
--- drivers/mtd/mtdchar.c | 42 +++++++++++++++++++++++++++++++++--------- include/uapi/linux/blkpg.h | 10 ++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-)
drivers/mtd/mtdchar.c +33 -9
@@ -498,21 +498,17 @@ static int shrink_ecclayout(const struct nand_ecclayout *from, }static int mtdchar_blkpg_ioctl (struct mtd_info *mtd, -struct blkpg_ioctl_arg __user *arg) +struct blkpg_ioctl_arg *arg) { -struct blkpg_ioctl_arg a;struct blkpg_partition p;if (!capable (CAP_SYS_ADMIN ))return -EPERM ; -if (copy_from_user (&a, arg,sizeof (struct blkpg_ioctl_arg ))) +if (copy_from_user (&p, arg->data,sizeof (p)))return -EFAULT ; -if (copy_from_user (&p, a.data,sizeof (struct blkpg_partition ))) -return -EFAULT ; - -switch (a.op) { +switch (arg->op) {case BLKPG_ADD_PARTITION :/* Only master mtd device must be used to add partitions */
@@ -966,8 +962,13 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)case BLKPG : { - ret =mtdchar_blkpg_ioctl (mtd, - (struct blkpg_ioctl_arg __user *)arg); +struct blkpg_ioctl_arg __user *blk_arg = argp; +struct blkpg_ioctl_arg a; + +if (copy_from_user (&a, blk_arg,sizeof (a))) + ret = -EFAULT ; +else + ret =mtdchar_blkpg_ioctl (mtd, &a);break ; }
@@ -1046,6 +1047,29 @@ static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd, &buf_user->start);break ; } + +case BLKPG : + { +/* Convert from blkpg_compat_ioctl_arg to blkpg_ioctl_arg */ +struct blkpg_compat_ioctl_arg __user *uarg = argp; +struct blkpg_compat_ioctl_arg arg; +struct blkpg_ioctl_arg a; + +if (copy_from_user (&arg, uarg,sizeof (arg))) { + ret = -EFAULT ; +break ; + } + +memset (&a,0 ,sizeof (a)); + a.op = arg.op; + a.flags = arg.flags; + a.datalen = arg.datalen; + a.data =compat_ptr (arg.data); + + ret =mtdchar_blkpg_ioctl (mtd, &a); +break ; + } +default : ret =mtdchar_ioctl (file, cmd, (unsigned long )argp); }
include/uapi/linux/blkpg.h +10 -0
@@ -26,6 +26,7 @@*/ #include <linux/compiler.h> #include <linux/ioctl.h> +#include <linux/compat.h> #define BLKPG _IO(0x12,105)
@@ -37,6 +38,15 @@ struct blkpg_ioctl_arg {void __user *data; }; +#ifdef CONFIG_COMPAT +struct blkpg_compat_ioctl_arg { +compat_int_t op; +compat_int_t flags; +compat_int_t datalen; +compat_uptr_t data; +}; +#endif +/* The subfunctions (for the op field) */ #define BLKPG_ADD_PARTITION 1#define BLKPG_DEL_PARTITION 2
Signature · 1 line
2.5.0.457.gab17608
Quoted 1 line · Brian Norris
The same bug applies to
block/ioctl.c
Actually I'll correct myself: it looks like block/ already has OK compat_ioctl support for this. Block devices do a copy to/from user space to marshal a new struct. Personally, I find my approach a little clearer.
Quoted 1 line
But then: which tree should it go in?
I guess since this is only an MTD bug, then it'd be fair to take via MTD. I'll plan to do that eventually if there are no objections.
Quoted 7 lines
Tested only on MTD, with an ARM32 user space on an ARM64 kernel.
Signed-off-by Brian Norris <[email protected]>--- drivers/mtd/mtdchar.c | 42 +++++++++++++++++++++++++++++++++--------- include/uapi/linux/blkpg.h | 10 ++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-)
I also suspect it might make more sense to move the compat definitions to a new non-UAPI include/linux/blkpg.h. I'll probably put that in v2.
Brian
PATCH 1/1 mtd: provide proper 32/64-bit compat_ioctl() support for BLKPG
In reply to Brian NorrisAfter a bit of poking around wondering why my 32-bit user-space can't seem to send a proper ioctl(BLKPG) to an MTD on my 64-bit kernel (ARM64), I noticed that struct blkpg_ioctl_arg is actually pretty unsuitable for use in the ioctl() ABI, due to its use of raw pointers, and its lack of alignment/packing restrictions (32-bit arch'es tend to pack the 4 fields into 4 32-bit words, whereas 64-bit arch'es would add padding after the third int, and make this 6 32-bit words).
Anyway, this means BLKPG deserves some special compat_ioctl handling. Do the conversion in a small shim for MTD.
block/compat_ioctl.c already has compat support for the block subsystem, but it does so by a re-marshalling data to/from user-space (see compat_blkpg_ioctl()). Personally, I think this approach is cleaner.
Tested only on MTD, with an ARM32 user space on an ARM64 kernel.
---
You can find the initial bug report / patch here:
http://article.gmane.org/gmane.linux.kernel/2028927
https://lkml.org/lkml/2015/8/28/594
Changes since RFC:
* create new non-UAPI header, instead of cluttering the UAPI header with compat stuff
* remove mention of "same bug in block/", since block/ solves this problem already, just in a slightly different way
drivers/mtd/mtdchar.c | 42 +++++++++++++++++++++++++++++++++--------- include/linux/blkpg.h | 21 +++++++++++++++++++++ include/uapi/linux/blkpg.h | 6 +++--- 3 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 include/linux/blkpg.h
drivers/mtd/mtdchar.c +33 -9
@@ -498,21 +498,17 @@ static int shrink_ecclayout(const struct nand_ecclayout *from, }static int mtdchar_blkpg_ioctl (struct mtd_info *mtd, -struct blkpg_ioctl_arg __user *arg) +struct blkpg_ioctl_arg *arg) { -struct blkpg_ioctl_arg a;struct blkpg_partition p;if (!capable (CAP_SYS_ADMIN ))return -EPERM ; -if (copy_from_user (&a, arg,sizeof (struct blkpg_ioctl_arg ))) +if (copy_from_user (&p, arg->data,sizeof (p)))return -EFAULT ; -if (copy_from_user (&p, a.data,sizeof (struct blkpg_partition ))) -return -EFAULT ; - -switch (a.op) { +switch (arg->op) {case BLKPG_ADD_PARTITION :/* Only master mtd device must be used to add partitions */
@@ -966,8 +962,13 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)case BLKPG : { - ret =mtdchar_blkpg_ioctl (mtd, - (struct blkpg_ioctl_arg __user *)arg); +struct blkpg_ioctl_arg __user *blk_arg = argp; +struct blkpg_ioctl_arg a; + +if (copy_from_user (&a, blk_arg,sizeof (a))) + ret = -EFAULT ; +else + ret =mtdchar_blkpg_ioctl (mtd, &a);break ; }
@@ -1046,6 +1047,29 @@ static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd, &buf_user->start);break ; } + +case BLKPG : + { +/* Convert from blkpg_compat_ioctl_arg to blkpg_ioctl_arg */ +struct blkpg_compat_ioctl_arg __user *uarg = argp; +struct blkpg_compat_ioctl_arg arg; +struct blkpg_ioctl_arg a; + +if (copy_from_user (&arg, uarg,sizeof (arg))) { + ret = -EFAULT ; +break ; + } + +memset (&a,0 ,sizeof (a)); + a.op = arg.op; + a.flags = arg.flags; + a.datalen = arg.datalen; + a.data =compat_ptr (arg.data); + + ret =mtdchar_blkpg_ioctl (mtd, &a); +break ; + } +default : ret =mtdchar_ioctl (file, cmd, (unsigned long )argp); }
include/linux/blkpg.h new file +21 -0
@@ -0,0 +1,21 @@ +#ifndef _LINUX_BLKPG_H +#define _LINUX_BLKPG_H + +/* +* Partition table and disk geometry handling +*/ + +#include <linux/compat.h> +#include <uapi/linux/blkpg.h> + +#ifdef CONFIG_COMPAT +/* For 32-bit/64-bit compatibility of struct blkpg_ioctl_arg */ +struct blkpg_compat_ioctl_arg { +compat_int_t op; +compat_int_t flags; +compat_int_t datalen; +compat_uptr_t data; +}; +#endif + +#endif /* _LINUX_BLKPG_H */
include/uapi/linux/blkpg.h +3 -3
@@ -1,5 +1,5 @@ -#ifndef _LINUX_BLKPG_H -#define _LINUX_BLKPG_H +#ifndef _UAPI__LINUX_BLKPG_H +#define _UAPI__LINUX_BLKPG_H/* * Partition table and disk geometry handling
@@ -56,4 +56,4 @@ struct blkpg_partition { char volname[BLKPG_VOLNAMELTH]; /* volume label */ }; -#endif /* _LINUX_BLKPG_H */ +#endif /* _UAPI__LINUX_BLKPG_H */
Signature · 1 line
2.6.0.rc0.131.gf624c3d
Brian Norris
AppliedQuoted 21 lines · Brian Norris
After a bit of poking around wondering why my 32-bit user-space can't seem to send a proper ioctl(BLKPG) to an MTD on my 64-bit kernel (ARM64), I noticed that struct
blkpg_ioctl_argis actually pretty unsuitable for use in theioctl()ABI, due to its use of raw pointers, and its lack of alignment/packing restrictions (32-bit arch'es tend to pack the 4 fields into 4 32-bit words, whereas 64-bit arch'es would add padding after the third int, and make this 6 32-bit words).Anyway, this means BLKPG deserves some special
compat_ioctlhandling. Do the conversion in a small shim for MTD.
block/compat_ioctl.calready has compat support for the block subsystem, but it does so by a re-marshalling data to/from user-space (seecompat_blkpg_ioctl()). Personally, I think this approach is cleaner.Tested only on MTD, with an ARM32 user space on an ARM64 kernel.
Signed-off-by Brian Norris <[email protected]>---
You can find the initial bug report / patch here:
Quoted 8 lines
http://article.gmane.org/gmane.linux.kernel/2028927
https://lkml.org/lkml/2015/8/28/594Changes since RFC:
* create new non-UAPI header, instead of cluttering the UAPI header with compat stuff
* remove mention of "same bug inblock/", sinceblock/solves this problem already, just in a slightly different way
Pushed to l2-mtd.git