]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/select.c
select: deal with math overflow from borderline valid userland data
[linux-2.6-omap-h63xx.git] / fs / select.c
index f6dceb56793fbe3f20b2a8313f9196bcb9e56ac6..87df51eadcf2bd170ab0271e189cf8a580aa1adb 100644 (file)
 
 #include <asm/uaccess.h>
 
+
+/*
+ * Estimate expected accuracy in ns from a timeval.
+ *
+ * After quite a bit of churning around, we've settled on
+ * a simple thing of taking 0.1% of the timeout as the
+ * slack, with a cap of 100 msec.
+ * "nice" tasks get a 0.5% slack instead.
+ *
+ * Consider this comment an open invitation to come up with even
+ * better solutions..
+ */
+
+static long __estimate_accuracy(struct timespec *tv)
+{
+       long slack;
+       int divfactor = 1000;
+
+       if (task_nice(current) > 0)
+               divfactor = divfactor / 5;
+
+       slack = tv->tv_nsec / divfactor;
+       slack += tv->tv_sec * (NSEC_PER_SEC/divfactor);
+
+       if (slack > 100 * NSEC_PER_MSEC)
+               slack =  100 * NSEC_PER_MSEC;
+
+       if (slack < 0)
+               slack = 0;
+       return slack;
+}
+
+static long estimate_accuracy(struct timespec *tv)
+{
+       unsigned long ret;
+       struct timespec now;
+
+       /*
+        * Realtime tasks get a slack of 0 for obvious reasons.
+        */
+
+       if (rt_task(current))
+               return 0;
+
+       ktime_get_ts(&now);
+       now = timespec_sub(*tv, now);
+       ret = __estimate_accuracy(&now);
+       if (ret < current->timer_slack_ns)
+               return current->timer_slack_ns;
+       return ret;
+}
+
+
+
 struct poll_table_page {
        struct poll_table_page * next;
        struct poll_table_entry * entry;
@@ -262,6 +316,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
        struct poll_wqueues table;
        poll_table *wait;
        int retval, i, timed_out = 0;
+       unsigned long slack = 0;
 
        rcu_read_lock();
        retval = max_select_fd(n, fds);
@@ -278,6 +333,9 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
                timed_out = 1;
        }
 
+       if (end_time && !timed_out)
+               slack = estimate_accuracy(end_time);
+
        retval = 0;
        for (;;) {
                unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
@@ -353,7 +411,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
                        to = &expire;
                }
 
-               if (!schedule_hrtimeout(to, HRTIMER_MODE_ABS))
+               if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
                        timed_out = 1;
        }
        __set_current_state(TASK_RUNNING);
@@ -461,8 +519,9 @@ asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp,
                        return -EFAULT;
 
                to = &end_time;
-               if (poll_select_set_timeout(to, tv.tv_sec,
-                                           tv.tv_usec * NSEC_PER_USEC))
+               if (poll_select_set_timeout(to,
+                               tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
+                               (tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
                        return -EINVAL;
        }
 
@@ -593,6 +652,7 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
        poll_table* pt = &wait->pt;
        ktime_t expire, *to = NULL;
        int timed_out = 0, count = 0;
+       unsigned long slack = 0;
 
        /* Optimise the no-wait case */
        if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
@@ -600,6 +660,9 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
                timed_out = 1;
        }
 
+       if (end_time && !timed_out)
+               slack = estimate_accuracy(end_time);
+
        for (;;) {
                struct poll_list *walk;
 
@@ -646,7 +709,7 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
                        to = &expire;
                }
 
-               if (!schedule_hrtimeout(to, HRTIMER_MODE_ABS))
+               if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
                        timed_out = 1;
        }
        __set_current_state(TASK_RUNNING);