Merge "diag: Perform CRC check on incoming HDLC encoded packet"
diff --git a/drivers/char/diag/diagchar_hdlc.c b/drivers/char/diag/diagchar_hdlc.c
index 2369c4d..7b24591 100644
--- a/drivers/char/diag/diagchar_hdlc.c
+++ b/drivers/char/diag/diagchar_hdlc.c
@@ -18,6 +18,7 @@
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
#include <linux/crc-ccitt.h>
#include "diagchar_hdlc.h"
#include "diagchar.h"
@@ -234,3 +235,37 @@
return pkt_bnd;
}
+
+int crc_check(uint8_t *buf, uint16_t len)
+{
+ uint16_t crc = CRC_16_L_SEED;
+ uint8_t sent_crc[2] = {0, 0};
+
+ /*
+ * The minimum length of a valid incoming packet is 4. 1 byte
+ * of data and 3 bytes for CRC
+ */
+ if (!buf || len < 4) {
+ pr_err_ratelimited("diag: In %s, invalid packet or length, buf: 0x%x, len: %d",
+ __func__, (int)buf, len);
+ return -EIO;
+ }
+
+ /*
+ * Run CRC check for the original input. Skip the last 3 CRC
+ * bytes
+ */
+ crc = crc_ccitt(crc, buf, len-3);
+ crc ^= CRC_16_L_SEED;
+
+ /* Check the computed CRC against the original CRC bytes. */
+ sent_crc[0] = buf[len-3];
+ sent_crc[1] = buf[len-2];
+ if (crc != *((uint16_t *)sent_crc)) {
+ pr_debug("diag: In %s, crc mismatch. expected: %x, sent %x.\n",
+ __func__, crc, *((uint16_t *)sent_crc));
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/drivers/char/diag/diagchar_hdlc.h b/drivers/char/diag/diagchar_hdlc.h
index e3378ac..2ba46f5 100644
--- a/drivers/char/diag/diagchar_hdlc.h
+++ b/drivers/char/diag/diagchar_hdlc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2009, 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2009, 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -53,6 +53,8 @@
int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc);
+int crc_check(uint8_t *buf, uint16_t len);
+
#define ESC_CHAR 0x7D
#define ESC_MASK 0x20
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index a1f6b2c..1e8b0ba 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1514,7 +1514,7 @@
void diag_process_hdlc(void *data, unsigned len)
{
struct diag_hdlc_decode_type hdlc;
- int ret, type = 0;
+ int ret, type = 0, crc_chk = 0;
mutex_lock(&driver->diag_hdlc_mutex);
@@ -1528,6 +1528,16 @@
hdlc.escaping = 0;
ret = diag_hdlc_decode(&hdlc);
+ if (ret) {
+ crc_chk = crc_check(hdlc.dest_ptr, hdlc.dest_idx);
+ if (crc_chk) {
+ /* CRC check failed. */
+ pr_err_ratelimited("diag: In %s, bad CRC. Dropping packet\n",
+ __func__);
+ mutex_unlock(&driver->diag_hdlc_mutex);
+ return;
+ }
+ }
/*
* If the message is 3 bytes or less in length then the message is
@@ -1550,9 +1560,8 @@
return;
}
} else if (driver->debug_flag) {
- printk(KERN_ERR "Packet dropped due to bad HDLC coding/CRC"
- " errors or partial packet received, packet"
- " length = %d\n", len);
+ pr_err("diag: In %s, partial packet received, dropping packet, len: %d\n",
+ __func__, len);
print_hex_dump(KERN_DEBUG, "Dropped Packet Data: ", 16, 1,
DUMP_PREFIX_ADDRESS, data, len, 1);
driver->debug_flag = 0;