Here is the third version for converting parport device(ppdev) to y2038 safe. The first two version could found at [1],[2].
An y2038 safe application/kernel use 64bit time_t(aka time64_t) instead of 32bit time_t. There are the 5 cases need to support:
summary |u:arch |u:tv_sec |k:arch |k:tv_sec -------------------|-------|---------|-------|-------- 32_y2038_unsafe |32 |32 |32 |32 32_y2038_safe |32 |64 |32 |64 compat_y2038_unsafe|32 |32 |64 |64 compat_y2038_safe |32 |64 |64 |64 64_y2038_safe |64 |64 |64 |64
notes: 1. xxx_y2038_safe/unsafe. 32 means app running on the 32bit kernel. compat means 32bit app running on 64bit kernel. 64 means 64bit app running on 64bit kernel. 2. 1.3.5 are the original one, we need keep the compatability. 2,4 is new one we need to support.
There are different ways to do this. Convert to 64bit time and/or define COMPAT_USE_64BIT_TIME 0 or 1 to indicate y2038 safe or unsafe.
But it is not mean that we need to convert all the time relative struct to 64bit. Because some time relative struct(e.g. timeval in ppdev.c) is mainly the offset of the real time.
The main issue in ppdev.c is PPSETTIME/PPGETTIME which transfer timeval between user space and kernel. My approach here is handle them as different ioctl command.
Build successful on arm64 and arm.
[1] https://lists.linaro.org/pipermail/y2038/2015-June/000522.html [2] https://lists.linaro.org/pipermail/y2038/2015-June/000567.html
Bamvor Jian Zhang (2): ppdev: convert to y2038 safe ppdev: add support for compat ioctl
drivers/char/ppdev.c | 85 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 16 deletions(-)
The y2038 issue for ppdev is changes of timeval in the ioctl (PPSETTIME and PPGETTIME). The size of struct timeval changes from 8bytes to 16bytes due to the changes of time_t. It lead to the changes of the command of ioctl, e.g. for PPGETTIME, We have:
on 32-bit (old): 0x80087095 on 32-bit (new): 0x80107095 on 64-bit : 0x80107095
This patch define these two ioctl commands to support the y2038 safe and y2038 unsafe application at the same time.
Signed-off-by: Bamvor Jian Zhang bamvor.zhangjian@linaro.org --- drivers/char/ppdev.c | 74 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 16 deletions(-)
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index ae0b42b..4b55df2 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -98,6 +98,13 @@ struct pp_struct { #define ROUND_UP(x,y) (((x)+(y)-1)/(y))
static DEFINE_MUTEX(pp_do_mutex); + +/* define fixed sized ioctl cmd for y2038 issue */ +#define PPGETTIME_unsafe _IOR(PP_IOCTL, 0x95, s32[2]) +#define PPSETTIME_unsafe _IOW(PP_IOCTL, 0x96, s32[2]) +#define PPGETTIME_safe _IOR(PP_IOCTL, 0x95, s64[2]) +#define PPSETTIME_safe _IOW(PP_IOCTL, 0x96, s64[2]) + static inline void pp_enable_irq (struct pp_struct *pp) { struct parport *port = pp->pdev->port; @@ -496,7 +503,8 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) unsigned char mask; int mode; int ret; - struct timeval par_timeout; + s32 time32[2]; + s64 time64[2]; long to_jiffies;
case PPRSTATUS: @@ -592,29 +600,63 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) atomic_sub (ret, &pp->irqc); return 0;
- case PPSETTIME: - if (copy_from_user (&par_timeout, argp, sizeof(struct timeval))) { + case PPSETTIME_unsafe: + if (copy_from_user(time32, argp, sizeof(time32))) return -EFAULT; - } - /* Convert to jiffies, place in pp->pdev->timeout */ - if ((par_timeout.tv_sec < 0) || (par_timeout.tv_usec < 0)) { + + if ((time32[0] < 0) || (time32[1] < 0)) return -EINVAL; - } - to_jiffies = ROUND_UP(par_timeout.tv_usec, 1000000/HZ); - to_jiffies += par_timeout.tv_sec * (long)HZ; - if (to_jiffies <= 0) { + + to_jiffies = ROUND_UP(time32[1], 1000000/HZ); + to_jiffies += time32[0] * (long)HZ; + if (to_jiffies <= 0) return -EINVAL; - } + pp->pdev->timeout = to_jiffies; return 0;
- case PPGETTIME: + case PPSETTIME_safe: + if (copy_from_user(time64, argp, sizeof(time64))) + return -EFAULT; + + if ((time64[0] < 0) || (time64[1] < 0)) + return -EINVAL; + + /* + * Avoid 64bit division on 32bit platform: + * timeval here is an offset of the real time. It should be + * safe to use 32bit time. + */ + if (!IS_ENABLED(CONFIG_64BIT)) + to_jiffies = (long)ROUND_UP((s32)time64[1], 1000000/HZ); + else + to_jiffies = (long)ROUND_UP(time64[1], 1000000/HZ); + + to_jiffies += (long)(time64[0] * (long)HZ); + if (to_jiffies <= 0) + return -EINVAL; + + pp->pdev->timeout = to_jiffies; + return 0; + + case PPGETTIME_unsafe: to_jiffies = pp->pdev->timeout; - memset(&par_timeout, 0, sizeof(par_timeout)); - par_timeout.tv_sec = to_jiffies / HZ; - par_timeout.tv_usec = (to_jiffies % (long)HZ) * (1000000/HZ); - if (copy_to_user (argp, &par_timeout, sizeof(struct timeval))) + memset(time32, 0, sizeof(time32)); + time32[0] = (s32)(to_jiffies / HZ); + time32[1] = (s32)((to_jiffies % (long)HZ) * (1000000/HZ)); + if (copy_to_user(time32, argp, sizeof(time32))) return -EFAULT; + + return 0; + + case PPGETTIME_safe: + to_jiffies = pp->pdev->timeout; + memset(time64, 0, sizeof(time64)); + time64[0] = (s64)(to_jiffies / HZ); + time64[1] = (s64)((to_jiffies % (long)HZ) * (1000000/HZ)); + if (copy_to_user(time64, argp, sizeof(time64))) + return -EFAULT; + return 0;
default:
On Tuesday 10 November 2015 23:07:42 Bamvor Jian Zhang wrote:
The y2038 issue for ppdev is changes of timeval in the ioctl (PPSETTIME and PPGETTIME). The size of struct timeval changes from 8bytes to 16bytes due to the changes of time_t. It lead to the changes of the command of ioctl, e.g. for PPGETTIME, We have:
on 32-bit (old): 0x80087095 on 32-bit (new): 0x80107095 on 64-bit : 0x80107095
This patch define these two ioctl commands to support the y2038 safe and y2038 unsafe application at the same time.
Signed-off-by: Bamvor Jian Zhang bamvor.zhangjian@linaro.org
Looks all correct to me, nice work!
A few minor comments on coding style:
to_jiffies = ROUND_UP(time32[1], 1000000/HZ);
This could be simplified using usecs_to_jiffies.
/*
* Avoid 64bit division on 32bit platform:
* timeval here is an offset of the real time. It should be
* safe to use 32bit time.
*/
if (!IS_ENABLED(CONFIG_64BIT))
to_jiffies = (long)ROUND_UP((s32)time64[1], 1000000/HZ);
else
to_jiffies = (long)ROUND_UP(time64[1], 1000000/HZ);
Here too, and you can also avoid the condition that way.
to_jiffies += (long)(time64[0] * (long)HZ);
if (to_jiffies <= 0)
return -EINVAL;
pp->pdev->timeout = to_jiffies;
return 0;
As this part is duplicated, you could perhaps move it into a separate function that takes the pdev, seconds and microseconds as arguments.
- case PPGETTIME_unsafe: to_jiffies = pp->pdev->timeout;
memset(&par_timeout, 0, sizeof(par_timeout));
par_timeout.tv_sec = to_jiffies / HZ;
par_timeout.tv_usec = (to_jiffies % (long)HZ) * (1000000/HZ);
if (copy_to_user (argp, &par_timeout, sizeof(struct timeval)))
memset(time32, 0, sizeof(time32));
time32[0] = (s32)(to_jiffies / HZ);
time32[1] = (s32)((to_jiffies % (long)HZ) * (1000000/HZ));
if (copy_to_user(time32, argp, sizeof(time32))) return -EFAULT;
The memset isn't really required here. Again, jiffies_to_usecs or possibly jiffies_to_timespec64 might make this more understandable.
Arnd
The arg of ioctl in ppdev is the pointer of integer except the timeval in PPSETTIME, PPGETTIME. Different size of timeval is already supported by the previous patches. So, it is safe to add compat support.
Signed-off-by: Bamvor Jian Zhang bamvor.zhangjian@linaro.org --- drivers/char/ppdev.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 4b55df2..0aa5d82 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -677,6 +677,14 @@ static long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ret; }
+#ifdef CONFIG_COMPAT +static long pp_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return pp_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + static int pp_open (struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); @@ -786,6 +794,9 @@ static const struct file_operations pp_fops = { .write = pp_write, .poll = pp_poll, .unlocked_ioctl = pp_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = pp_compat_ioctl, +#endif .open = pp_open, .release = pp_release, };
On Tuesday 10 November 2015 23:07:43 Bamvor Jian Zhang wrote:
The arg of ioctl in ppdev is the pointer of integer except the timeval in PPSETTIME, PPGETTIME. Different size of timeval is already supported by the previous patches. So, it is safe to add compat support.
Signed-off-by: Bamvor Jian Zhang bamvor.zhangjian@linaro.org
Reviewed-by: Arnd Bergmann arnd@arndb.de