[7/8] ppdev: fix PPGETTIME/PPSETTIME ioctls

Message ID 20191108203435.112759-8-arnd@arndb.de
State Accepted
Commit 998174042da229e2cf5841f574aba4a743e69650
Headers show
Series
  • [1/8] y2038: timex: remove incorrect time_t truncation
Related show

Commit Message

Arnd Bergmann Nov. 8, 2019, 8:34 p.m.
Going through the uses of timeval in the user space API,
I noticed two bugs in ppdev that were introduced in the y2038
conversion:

* The range check was accidentally moved from ppsettime to
  ppgettime

* On sparc64, the microseconds are in the other half of the
  64-bit word.

Fix both, and mark the fix for stable backports.

Cc: stable@vger.kernel.org
Fixes: 3b9ab374a1e6 ("ppdev: convert to y2038 safe")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

---
 drivers/char/ppdev.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

-- 
2.20.0

Comments

Ben Hutchings Nov. 20, 2019, 7:29 p.m. | #1
On Fri, 2019-11-08 at 21:34 +0100, Arnd Bergmann wrote:
> Going through the uses of timeval in the user space API,

> I noticed two bugs in ppdev that were introduced in the y2038

> conversion:

> 

> * The range check was accidentally moved from ppsettime to

>   ppgettime

> 

> * On sparc64, the microseconds are in the other half of the

>   64-bit word.

> 

> Fix both, and mark the fix for stable backports.


Like the patch for lpdev, this also doesn't completely fix sparc64.

Ben.

> Cc: stable@vger.kernel.org

> Fixes: 3b9ab374a1e6 ("ppdev: convert to y2038 safe")

> Signed-off-by: Arnd Bergmann <arnd@arndb.de>

> ---

>  drivers/char/ppdev.c | 16 ++++++++++++----

>  1 file changed, 12 insertions(+), 4 deletions(-)

> 

> diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c

> index c86f18aa8985..34bb88fe0b0a 100644

> --- a/drivers/char/ppdev.c

> +++ b/drivers/char/ppdev.c

> @@ -619,20 +619,27 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

>  		if (copy_from_user(time32, argp, sizeof(time32)))

>  			return -EFAULT;

>  

> +		if ((time32[0] < 0) || (time32[1] < 0))

> +			return -EINVAL;

> +

>  		return pp_set_timeout(pp->pdev, time32[0], time32[1]);

>  

>  	case PPSETTIME64:

>  		if (copy_from_user(time64, argp, sizeof(time64)))

>  			return -EFAULT;

>  

> +		if ((time64[0] < 0) || (time64[1] < 0))

> +			return -EINVAL;

> +

> +		if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())

> +			time64[1] >>= 32;

> +

>  		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;

>  

>  		if (copy_to_user(argp, time32, sizeof(time32)))

>  			return -EFAULT;

> @@ -643,8 +650,9 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

>  		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 (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())

> +			time64[1] <<= 32;

>  

>  		if (copy_to_user(argp, time64, sizeof(time64)))

>  			return -EFAULT;

-- 
Ben Hutchings, Software Developer                         Codethink Ltd
https://www.codethink.co.uk/                 Dale House, 35 Dale Street
                                     Manchester, M1 2HF, United Kingdom
Arnd Bergmann Nov. 21, 2019, 2:06 p.m. | #2
On Wed, Nov 20, 2019 at 8:29 PM Ben Hutchings
<ben.hutchings@codethink.co.uk> wrote:
>

> On Fri, 2019-11-08 at 21:34 +0100, Arnd Bergmann wrote:

> > Going through the uses of timeval in the user space API,

> > I noticed two bugs in ppdev that were introduced in the y2038

> > conversion:

> >

> > * The range check was accidentally moved from ppsettime to

> >   ppgettime

> >

> > * On sparc64, the microseconds are in the other half of the

> >   64-bit word.

> >

> > Fix both, and mark the fix for stable backports.

>

> Like the patch for lpdev, this also doesn't completely fix sparc64.


I think the same applies as in the other patch:

- it actually works correctly because of the alignment
- it's already merged in linux-next
- if you wish, I can add another cleanup patch on top, but I'd
  prefer to just leave it as it is.

      Arnd

Patch

diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index c86f18aa8985..34bb88fe0b0a 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -619,20 +619,27 @@  static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		if (copy_from_user(time32, argp, sizeof(time32)))
 			return -EFAULT;
 
+		if ((time32[0] < 0) || (time32[1] < 0))
+			return -EINVAL;
+
 		return pp_set_timeout(pp->pdev, time32[0], time32[1]);
 
 	case PPSETTIME64:
 		if (copy_from_user(time64, argp, sizeof(time64)))
 			return -EFAULT;
 
+		if ((time64[0] < 0) || (time64[1] < 0))
+			return -EINVAL;
+
+		if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
+			time64[1] >>= 32;
+
 		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;
 
 		if (copy_to_user(argp, time32, sizeof(time32)))
 			return -EFAULT;
@@ -643,8 +650,9 @@  static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		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 (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
+			time64[1] <<= 32;
 
 		if (copy_to_user(argp, time64, sizeof(time64)))
 			return -EFAULT;