Re: [Y2038] [PATCH 6/8] lp: fix sparc64 LPSETTIMEOUT ioctl

From: Arnd Bergmann
Date: Thu Nov 21 2019 - 09:04:23 EST


On Wed, Nov 20, 2019 at 11:10 PM Ben Hutchings
<ben.hutchings@xxxxxxxxxxxxxxx> wrote:
> On Wed, 2019-11-20 at 20:46 +0100, Arnd Bergmann wrote:
> > On Wed, Nov 20, 2019 at 8:27 PM Ben Hutchings <ben.hutchings@xxxxxxxxxxxxxxx> wrote:
> > -
> > return lp_set_timeout(minor, karg[0], karg[1]);
> > }
> >
> > +static int lp_set_timeout(unsigned int minor, void __user *arg)
>
> That function name is already used! Maybe this should be
> lp_set_timeout_old()?

Yes, that's what I used after actually compile-testing and running
into a couple of issues with my draft.

> > @@ -730,11 +736,8 @@ static long lp_ioctl(struct file *file, unsigned int cmd,
> > mutex_lock(&lp_mutex);
> > switch (cmd) {
> > case LPSETTIMEOUT_OLD:
> > - if (BITS_PER_LONG == 32) {
> > - ret = lp_set_timeout32(minor, (void __user *)arg);
> > - break;
> > - }
> > - /* fall through - for 64-bit */
> > + ret = lp_set_timeout(minor, (void __user *)arg);
> > + break;
> > case LPSETTIMEOUT_NEW:
> > ret = lp_set_timeout64(minor, (void __user *)arg);
> > break;
> >
> > Do you like that better?
>
> Yes. Aside from the duplicate function name, it looks correct and
> cleaner than the current version.

As Greg has already merged the original patch, and that version works
just as well, I'd probably just leave what I did at first. One benefit is
that in case we decide to kill off sparc64 support before drivers/char/lp.c,
the special case can be removed more easily.

I don't think either of them is going any time soon, but working on y2038
patches has made me think ahead longer term ;-)

If you still think we should change it I can send the below patch (now
actually build-tested) with your Ack.

Arnd
---
commit 93efbb1768a5071a0a98bf4627f0104075cf83a6 (HEAD -> y2038)
Author: Arnd Bergmann <arnd@xxxxxxxx>
Date: Thu Nov 21 14:45:14 2019 +0100

lp: clean up set_timeout handling

As Ben Hutchings noticed, we can avoid the special case for sparc64
by dealing with '__kernel_old_timeval' arguments separately from
the fixed-length 32-bit and 64-bit arguments.

Note that the behavior for LPSETTIMEOUT_NEW changes on sparc64 to
expect the same argument as other architectures, but this is ok
because sparc64 users would pass LPSETTIMEOUT_OLD anyway.

Suggested-by: Ben Hutchings <ben.hutchings@xxxxxxxxxxxxxxx>
Signed-off-by: Arnd Bergmann <arnd@xxxxxxxx>

diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index bd95aba1f9fe..cc17d5a387c5 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -696,14 +696,14 @@ static int lp_set_timeout(unsigned int minor,
s64 tv_sec, long tv_usec)
return 0;
}

-static int lp_set_timeout32(unsigned int minor, void __user *arg)
+static int lp_set_timeout_old(unsigned int minor, void __user *arg)
{
- s32 karg[2];
+ struct __kernel_old_timeval tv;

- if (copy_from_user(karg, arg, sizeof(karg)))
+ if (copy_from_user(&tv, arg, sizeof(tv)))
return -EFAULT;

- return lp_set_timeout(minor, karg[0], karg[1]);
+ return lp_set_timeout(minor, tv.tv_sec, tv.tv_usec);
}

static int lp_set_timeout64(unsigned int minor, void __user *arg)
@@ -713,10 +713,6 @@ static int lp_set_timeout64(unsigned int minor,
void __user *arg)
if (copy_from_user(karg, arg, sizeof(karg)))
return -EFAULT;

- /* sparc64 suseconds_t is 32-bit only */
- if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
- karg[1] >>= 32;
-
return lp_set_timeout(minor, karg[0], karg[1]);
}

@@ -730,11 +726,8 @@ static long lp_ioctl(struct file *file, unsigned int cmd,
mutex_lock(&lp_mutex);
switch (cmd) {
case LPSETTIMEOUT_OLD:
- if (BITS_PER_LONG == 32) {
- ret = lp_set_timeout32(minor, (void __user *)arg);
- break;
- }
- /* fall through - for 64-bit */
+ ret = lp_set_timeout_old(minor, (void __user *)arg);
+ break;
case LPSETTIMEOUT_NEW:
ret = lp_set_timeout64(minor, (void __user *)arg);
break;
@@ -748,6 +741,16 @@ static long lp_ioctl(struct file *file, unsigned int cmd,
}

#ifdef CONFIG_COMPAT
+static int lp_set_timeout32(unsigned int minor, void __user *arg)
+{
+ s32 karg[2];
+
+ if (copy_from_user(karg, arg, sizeof(karg)))
+ return -EFAULT;
+
+ return lp_set_timeout(minor, karg[0], karg[1]);
+}
+
static long lp_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{