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: