dp83640: Fix length check for event timestamp status messages
authorChristian Riesch <christian.riesch@omicron.at>
Thu, 21 Aug 2014 13:17:04 +0000 (15:17 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 22 Aug 2014 19:33:48 +0000 (12:33 -0700)
Event timestamp status messages have a variable length, ranging from
1 to 5 words (16 bit words). The current code however requires
a minimum message length of sizeof(*phy_txts). In most cases this
condition is fulfilled due to padding bytes. However, if several events
are signaled in a single message, padding bytes may not be present.
For short event timestamp status messages, the length check will fail,
and the event timestamp will be dropped.

Signed-off-by: Christian Riesch <christian.riesch@omicron.at>
Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/dp83640.c

index c301e4c..d5991ac 100644 (file)
@@ -721,7 +721,7 @@ static inline u16 exts_chan_to_edata(int ch)
 }
 
 static int decode_evnt(struct dp83640_private *dp83640,
-                      void *data, u16 ests)
+                      void *data, int len, u16 ests)
 {
        struct phy_txts *phy_txts;
        struct ptp_clock_event event;
@@ -729,6 +729,16 @@ static int decode_evnt(struct dp83640_private *dp83640,
        int words = (ests >> EVNT_TS_LEN_SHIFT) & EVNT_TS_LEN_MASK;
        u16 ext_status = 0;
 
+       /* calculate length of the event timestamp status message */
+       if (ests & MULT_EVNT)
+               parsed = (words + 2) * sizeof(u16);
+       else
+               parsed = (words + 1) * sizeof(u16);
+
+       /* check if enough data is available */
+       if (len < parsed)
+               return len;
+
        if (ests & MULT_EVNT) {
                ext_status = *(u16 *) data;
                data += sizeof(ext_status);
@@ -747,10 +757,7 @@ static int decode_evnt(struct dp83640_private *dp83640,
                dp83640->edata.ns_lo = phy_txts->ns_lo;
        }
 
-       if (ext_status) {
-               parsed = words + 2;
-       } else {
-               parsed = words + 1;
+       if (!ext_status) {
                i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK) - EXT_EVENT;
                ext_status = exts_chan_to_edata(i);
        }
@@ -768,7 +775,7 @@ static int decode_evnt(struct dp83640_private *dp83640,
                }
        }
 
-       return parsed * sizeof(u16);
+       return parsed;
 }
 
 static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)
@@ -905,9 +912,9 @@ static void decode_status_frame(struct dp83640_private *dp83640,
                        decode_txts(dp83640, phy_txts);
                        size = sizeof(*phy_txts);
 
-               } else if (PSF_EVNT == type && len >= sizeof(*phy_txts)) {
+               } else if (PSF_EVNT == type) {
 
-                       size = decode_evnt(dp83640, ptr, ests);
+                       size = decode_evnt(dp83640, ptr, len, ests);
 
                } else {
                        size = 0;