aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2017-11-27 06:29:50 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-11-28 10:54:00 -0500
commit9a450484089dfa8b6348eff2a918f3c8f38585b9 (patch)
tree91cecd53a41c70f00531226cccf1303a168ca6d1
parent02543a4e96760a347fb9733bc8a0506a19270ec2 (diff)
lp: support 64-bit time_t user space
Once we get a glibc with 64-bit time_t, the LPSETTIMEOUT ioctl stops working, since the command number and data structure no longer match. To work around that, this introduces a new command number LPSETTIMEOUT_NEW that is used whenever the modified user space evaluates the LPSETTIMEOUT macro. The trick we use is a bit convoluted but necessary: we cannot check for any macros set by the C library in linux/lp.h, because this particular header can be included before including sys/time.h. However, we can assume that by the time that LPSETTIMEOUT is seen in the code, the definition for 'timeval' and 'time_t' has been seen as well, so we can use the sizeof() operator to determine whether we should use the old or the new definition. We use the old one not only for traditional 32-bit user space with 32-bit time_t, but also for all 64-bit architectures and x32, which always use a 64-bit time_t, the new definition will be used only for 32-bit user space with 64-bit time_t, which also requires a newer kernel. The compat_ioctl() handler now implements both commands, but has to use a special case for existing x32 binaries. The native ioctl handler now implements both command numbers on both 32-bit and 64-bit, though the latter version use the same interpretation for both. This is based on an earlier patch from Bamvor. Cc: Bamvor Jian Zhang <bamv2005@gmail.com> Link: http://www.spinics.net/lists/y2038/msg01162.html Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/char/lp.c67
-rw-r--r--include/uapi/linux/lp.h12
2 files changed, 62 insertions, 17 deletions
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 8249762192d5..be14abf70da1 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -659,17 +659,31 @@ static int lp_do_ioctl(unsigned int minor, unsigned int cmd,
659 return retval; 659 return retval;
660} 660}
661 661
662static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout) 662static int lp_set_timeout(unsigned int minor, s64 tv_sec, long tv_usec)
663{ 663{
664 long to_jiffies; 664 long to_jiffies;
665 665
666 /* Convert to jiffies, place in lp_table */ 666 /* Convert to jiffies, place in lp_table */
667 if ((par_timeout->tv_sec < 0) || 667 if (tv_sec < 0 || tv_usec < 0)
668 (par_timeout->tv_usec < 0)) {
669 return -EINVAL; 668 return -EINVAL;
669
670 /*
671 * we used to not check, so let's not make this fatal,
672 * but deal with user space passing a 32-bit tv_nsec in
673 * a 64-bit field, capping the timeout to 1 second
674 * worth of microseconds, and capping the total at
675 * MAX_JIFFY_OFFSET.
676 */
677 if (tv_usec > 999999)
678 tv_usec = 999999;
679
680 if (tv_sec >= MAX_SEC_IN_JIFFIES - 1) {
681 to_jiffies = MAX_JIFFY_OFFSET;
682 } else {
683 to_jiffies = DIV_ROUND_UP(tv_usec, 1000000/HZ);
684 to_jiffies += tv_sec * (long) HZ;
670 } 685 }
671 to_jiffies = DIV_ROUND_UP(par_timeout->tv_usec, 1000000/HZ); 686
672 to_jiffies += par_timeout->tv_sec * (long) HZ;
673 if (to_jiffies <= 0) { 687 if (to_jiffies <= 0) {
674 return -EINVAL; 688 return -EINVAL;
675 } 689 }
@@ -677,23 +691,43 @@ static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout)
677 return 0; 691 return 0;
678} 692}
679 693
694static int lp_set_timeout32(unsigned int minor, void __user *arg)
695{
696 s32 karg[2];
697
698 if (copy_from_user(karg, arg, sizeof(karg)))
699 return -EFAULT;
700
701 return lp_set_timeout(minor, karg[0], karg[1]);
702}
703
704static int lp_set_timeout64(unsigned int minor, void __user *arg)
705{
706 s64 karg[2];
707
708 if (copy_from_user(karg, arg, sizeof(karg)))
709 return -EFAULT;
710
711 return lp_set_timeout(minor, karg[0], karg[1]);
712}
713
680static long lp_ioctl(struct file *file, unsigned int cmd, 714static long lp_ioctl(struct file *file, unsigned int cmd,
681 unsigned long arg) 715 unsigned long arg)
682{ 716{
683 unsigned int minor; 717 unsigned int minor;
684 struct timeval par_timeout;
685 int ret; 718 int ret;
686 719
687 minor = iminor(file_inode(file)); 720 minor = iminor(file_inode(file));
688 mutex_lock(&lp_mutex); 721 mutex_lock(&lp_mutex);
689 switch (cmd) { 722 switch (cmd) {
690 case LPSETTIMEOUT: 723 case LPSETTIMEOUT_OLD:
691 if (copy_from_user(&par_timeout, (void __user *)arg, 724 if (BITS_PER_LONG == 32) {
692 sizeof (struct timeval))) { 725 ret = lp_set_timeout32(minor, (void __user *)arg);
693 ret = -EFAULT;
694 break; 726 break;
695 } 727 }
696 ret = lp_set_timeout(minor, &par_timeout); 728 /* fallthrough for 64-bit */
729 case LPSETTIMEOUT_NEW:
730 ret = lp_set_timeout64(minor, (void __user *)arg);
697 break; 731 break;
698 default: 732 default:
699 ret = lp_do_ioctl(minor, cmd, arg, (void __user *)arg); 733 ret = lp_do_ioctl(minor, cmd, arg, (void __user *)arg);
@@ -709,18 +743,19 @@ static long lp_compat_ioctl(struct file *file, unsigned int cmd,
709 unsigned long arg) 743 unsigned long arg)
710{ 744{
711 unsigned int minor; 745 unsigned int minor;
712 struct timeval par_timeout;
713 int ret; 746 int ret;
714 747
715 minor = iminor(file_inode(file)); 748 minor = iminor(file_inode(file));
716 mutex_lock(&lp_mutex); 749 mutex_lock(&lp_mutex);
717 switch (cmd) { 750 switch (cmd) {
718 case LPSETTIMEOUT: 751 case LPSETTIMEOUT_OLD:
719 if (compat_get_timeval(&par_timeout, compat_ptr(arg))) { 752 if (!COMPAT_USE_64BIT_TIME) {
720 ret = -EFAULT; 753 ret = lp_set_timeout32(minor, (void __user *)arg);
721 break; 754 break;
722 } 755 }
723 ret = lp_set_timeout(minor, &par_timeout); 756 /* fallthrough for x32 mode */
757 case LPSETTIMEOUT_NEW:
758 ret = lp_set_timeout64(minor, (void __user *)arg);
724 break; 759 break;
725#ifdef LP_STATS 760#ifdef LP_STATS
726 case LPGETSTATS: 761 case LPGETSTATS:
diff --git a/include/uapi/linux/lp.h b/include/uapi/linux/lp.h
index dafcfe4e4834..8589a27037d7 100644
--- a/include/uapi/linux/lp.h
+++ b/include/uapi/linux/lp.h
@@ -8,6 +8,8 @@
8#ifndef _UAPI_LINUX_LP_H 8#ifndef _UAPI_LINUX_LP_H
9#define _UAPI_LINUX_LP_H 9#define _UAPI_LINUX_LP_H
10 10
11#include <linux/types.h>
12#include <linux/ioctl.h>
11 13
12/* 14/*
13 * Per POSIX guidelines, this module reserves the LP and lp prefixes 15 * Per POSIX guidelines, this module reserves the LP and lp prefixes
@@ -88,7 +90,15 @@
88#define LPGETSTATS 0x060d /* get statistics (struct lp_stats) */ 90#define LPGETSTATS 0x060d /* get statistics (struct lp_stats) */
89#endif 91#endif
90#define LPGETFLAGS 0x060e /* get status flags */ 92#define LPGETFLAGS 0x060e /* get status flags */
91#define LPSETTIMEOUT 0x060f /* set parport timeout */ 93#define LPSETTIMEOUT_OLD 0x060f /* set parport timeout */
94#define LPSETTIMEOUT_NEW \
95 _IOW(0x6, 0xf, __s64[2]) /* set parport timeout */
96#if __BITS_PER_LONG == 64
97#define LPSETTIMEOUT LPSETTIMEOUT_OLD
98#else
99#define LPSETTIMEOUT (sizeof(time_t) > sizeof(__kernel_long_t) ? \
100 LPSETTIMEOUT_NEW : LPSETTIMEOUT_OLD)
101#endif
92 102
93/* timeout for printk'ing a timeout, in jiffies (100ths of a second). 103/* timeout for printk'ing a timeout, in jiffies (100ths of a second).
94 This is also used for re-checking error conditions if LP_ABORT is 104 This is also used for re-checking error conditions if LP_ABORT is