Here is the sixth version for converting parport device(ppdev) to y2038 safe. The first four could found at [1][2][3][4][5].
An y2038 safe application/kernel use 64bit time_t(aka time64_t) instead of 32bit time_t. Given that some time relative struct(e.g. timeval in ppdev.c) is mainly the offset of the real time, the old 32bit time_t in such application is safe. We need to handle the 32bit time_t and 64bit time_t application at the same time.
My approach here is handle them as different ioctl command for different size of timeval.
Build successful on arm64 and arm.
Changes since v5: 1. Replace PP[GS]ETTIME_safe/unsafe with PP[GS]ETTIME32/64. 2. Rewirte PPSETTIME ioctl with jiffies_to_timespec64 in order to replace user fake HZ(TICK_USEC) to kernel HZ(TICK_NSEC). 3. define tv_sec as long and tv_usec as int in pp_set_timeout. It should be enough for the timeout.
Change since v4: 1. change type of tv_sec and tv_usec to s64 in pp_set_timeout. Use s64 could avoid s64 cast to s32 in arm 32bit.
Changes since V3: 1. create pp_set_timeout, pp_get_timeout to reduce the duplicated code in my patch according to the suggestion of arnd. I use div_u64_rem instead of jiffies_to_timespec64 because it could save a divide operaion.
[1] https://lists.linaro.org/pipermail/y2038/2015-June/000522.html [2] https://lists.linaro.org/pipermail/y2038/2015-June/000567.html [3] https://lists.linaro.org/pipermail/y2038/2015-November/001093.html [4] https://lists.linaro.org/pipermail/y2038/2015-November/001132.html [5] https://lists.linaro.org/pipermail/y2038/2015-December/001201.html
Bamvor Jian Zhang (2): ppdev: convert to y2038 safe ppdev: add support for compat ioctl
drivers/char/ppdev.c | 86 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 20 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 32bit and 64bit time_t application at the same time. And, introduce pp_set_timeout to remove some duplicated code.
Signed-off-by: Bamvor Jian Zhang bamvor.zhangjian@linaro.org --- drivers/char/ppdev.c | 75 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 20 deletions(-)
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index ae0b42b..19a4d6e 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 migration */ +#define PPGETTIME32 _IOR(PP_IOCTL, 0x95, s32[2]) +#define PPSETTIME32 _IOW(PP_IOCTL, 0x96, s32[2]) +#define PPGETTIME64 _IOR(PP_IOCTL, 0x95, s64[2]) +#define PPSETTIME64 _IOW(PP_IOCTL, 0x96, s64[2]) + static inline void pp_enable_irq (struct pp_struct *pp) { struct parport *port = pp->pdev->port; @@ -322,6 +329,22 @@ static enum ieee1284_phase init_phase (int mode) return IEEE1284_PH_FWD_IDLE; }
+static int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec) +{ + long to_jiffies; + + if ((tv_sec < 0) || (tv_usec < 0)) + return -EINVAL; + + to_jiffies = usecs_to_jiffies(tv_usec); + to_jiffies += tv_sec * HZ; + if (to_jiffies <= 0) + return -EINVAL; + + pdev->timeout = to_jiffies; + return 0; +} + static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = iminor(file_inode(file)); @@ -495,9 +518,10 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) unsigned char reg; unsigned char mask; int mode; + s32 time32[2]; + s64 time64[2]; + struct timespec64 ts; int ret; - struct timeval par_timeout; - long to_jiffies;
case PPRSTATUS: reg = parport_read_status (port); @@ -592,29 +616,40 @@ 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 PPSETTIME32: + 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)) { - return -EINVAL; - } - to_jiffies = ROUND_UP(par_timeout.tv_usec, 1000000/HZ); - to_jiffies += par_timeout.tv_sec * (long)HZ; - if (to_jiffies <= 0) { + + return pp_set_timeout(pp->pdev, time32[0], time32[1]); + + case PPSETTIME64: + if (copy_from_user(time64, argp, sizeof(time64))) + return -EFAULT; + + return pp_set_timeout(pp->pdev, time64[0], time64[1]); + + case PPGETTIME32: + jiffies_to_timespec64(pp->pdev->timeout, &ts); + time32[0] = ts.tv_sec; + time32[1] = ts.tv_nsec / NSEC_PER_USEC; + if ((time32[0] < 0) || (time32[1] < 0)) return -EINVAL; - } - pp->pdev->timeout = to_jiffies; + + if (copy_to_user(time32, argp, sizeof(time32))) + return -EFAULT; + return 0;
- case PPGETTIME: - 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))) + case PPGETTIME64: + jiffies_to_timespec64(pp->pdev->timeout, &ts); + time64[0] = ts.tv_sec; + time64[1] = ts.tv_nsec / NSEC_PER_USEC; + if ((time64[0] < 0) || (time64[1] < 0)) + return -EINVAL; + + if (copy_to_user(time64, argp, sizeof(time64))) return -EFAULT; + return 0;
default:
On Monday 07 December 2015 22:24:48 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 32bit and 64bit time_t application at the same time. And, introduce pp_set_timeout to remove some duplicated code.
Signed-off-by: Bamvor Jian Zhang bamvor.zhangjian@linaro.org
Looks all good now, please send it to the appropriate lists and maintainer(s). Greg should be able to pick up the two patches with my Ack.
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 Reviewed-by: Arnd Bergmann arnd@arndb.de --- drivers/char/ppdev.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 19a4d6e..7390166 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -670,6 +670,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); @@ -779,6 +787,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, };