OSDN Git Service

[PATCH] Fix jiffies overflow in delay.h
authorWilly Tarreau <willy@w.ods.org>
Tue, 11 Oct 2005 22:16:28 +0000 (00:16 +0200)
committerMarcelo Tosatti <marcelo@pegasos.cnet>
Tue, 18 Oct 2005 11:58:50 +0000 (06:58 -0500)
There are several multiply overflows in delay.h:msecs_to_jiffies(). The
first one is the call to jiffies_to_msecs(MAX_JIFFY_OFFSET) which will
multiply MAX_JIFFY_OFFSET by (1000/HZ) or by 1000 during conversion,
while it was already high (~0UL>>1)-1 ... Needless to say that it's
wrong below 500 HZ and for all values not multiple of 1000 or which
don't divide 1000.

The second overflow can happen a few lines later, but this time on the
argument. The fix consists in defining a constant (macro) which depends
on HZ and fixes the absolute maximal value which we guarantee will not
produce an overflow. Fortunately, I've found no user of msecs_to_jiffies()
in mainline, although sys_poll() could benefit from it in order to avoid
a useless divide in the fast path.

But I think that the code needs be fixed anyway, considering that it
had been inherited by 2.6 for which I proposed the same fix. And it
is possible that some external patches use it.

include/linux/delay.h

index 7bfd469..13ed648 100644 (file)
@@ -14,6 +14,24 @@ extern unsigned long loops_per_jiffy;
 #include <asm/delay.h>
 
 /*
+ * We define MAX_MSEC_OFFSET as the maximal value that can be accepted by
+ * msecs_to_jiffies() without risking a multiply overflow. This function
+ * returns MAX_JIFFY_OFFSET for arguments above those values.
+ */
+
+#if HZ <= 1000 && !(1000 % HZ)
+#  define MAX_MSEC_OFFSET \
+       (ULONG_MAX - (1000 / HZ) + 1)
+#elif HZ > 1000 && !(HZ % 1000)
+#  define MAX_MSEC_OFFSET \
+       (ULONG_MAX / (HZ / 1000))
+#else
+#  define MAX_MSEC_OFFSET \
+       ((ULONG_MAX - 999) / HZ)
+#endif
+
+
+/*
  * Convert jiffies to milliseconds and back.
  *
  * Avoid unnecessary multiplications/divisions in the
@@ -43,14 +61,14 @@ static inline unsigned int jiffies_to_usecs(const unsigned long j)
 
 static inline unsigned long msecs_to_jiffies(const unsigned int m)
 {
-       if (m > jiffies_to_msecs(MAX_JIFFY_OFFSET))
+       if (MAX_MSEC_OFFSET < UINT_MAX && m > (unsigned int)MAX_MSEC_OFFSET)
                return MAX_JIFFY_OFFSET;
 #if HZ <= 1000 && !(1000 % HZ)
-       return (m + (1000 / HZ) - 1) / (1000 / HZ);
+       return ((unsigned long)m + (1000 / HZ) - 1) / (1000 / HZ);
 #elif HZ > 1000 && !(HZ % 1000)
-       return m * (HZ / 1000);
+       return (unsigned long)m * (HZ / 1000);
 #else
-       return (m * HZ + 999) / 1000;
+       return ((unsigned long)m * HZ + 999) / 1000;
 #endif
 }