diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index d0b8ee88e98f..9ff3eff2ac9c 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -388,16 +388,23 @@ static int diag_dci_probe(struct platform_device *pdev) int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf, int len, int index) { - int i; - int status = 0; + int i, status = 0; + unsigned int read_len = 0; - /* remove UID from user space pkt before sending to peripheral */ - buf = buf + 4; - if (len > APPS_BUF_SIZE - 10) { - pr_err("diag: dci: buffer overwrite possible since payload bigger than buf size\n"); + /* The first 4 bytes is the uid tag and the next four bytes is + the minmum packet length of a request packet */ + if (len < DCI_PKT_REQ_MIN_LEN) { + pr_err("diag: dci: Invalid pkt len %d in %s\n", len, __func__); return -EIO; } - len = len - 4; + if (len > APPS_BUF_SIZE - 10) { + pr_err("diag: dci: Invalid payload length in %s\n", __func__); + return -EIO; + } + /* remove UID from user space pkt before sending to peripheral*/ + buf = buf + sizeof(int); + read_len += sizeof(int); + len = len - sizeof(int); mutex_lock(&driver->dci_mutex); /* prepare DCI packet */ driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */ @@ -408,7 +415,13 @@ int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf, driver->req_tracking_tbl[index].tag; for (i = 0; i < len; i++) driver->apps_dci_buf[i+9] = *(buf+i); + read_len += len; driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */ + if ((read_len + 9) >= USER_SPACE_DATA) { + pr_err("diag: dci: Invalid length while forming dci pkt in %s", + __func__); + return -EIO; + } for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) { if (entry.client_id == driver->smd_dci[i].peripheral) { @@ -459,10 +472,10 @@ int diag_process_dci_transaction(unsigned char *buf, int len) { unsigned char *temp = buf; uint16_t subsys_cmd_code, log_code, item_num; - int subsys_id, cmd_code, ret = -1, index = -1, found = 0, read_len = 0; + int subsys_id, cmd_code, ret = -1, index = -1, found = 0; struct diag_master_table entry; int count, set_mask, num_codes, bit_index, event_id, offset = 0, i; - unsigned int byte_index; + unsigned int byte_index, read_len = 0; uint8_t equip_id, *log_mask_ptr, *head_log_mask_ptr, byte_mask; uint8_t *event_mask_ptr; @@ -472,15 +485,24 @@ int diag_process_dci_transaction(unsigned char *buf, int len) return DIAG_DCI_SEND_DATA_FAIL; } + if (!temp) { + pr_err("diag: Invalid buffer in %s\n", __func__); + } + /* This is Pkt request/response transaction */ if (*(int *)temp > 0) { + if (len < DCI_PKT_REQ_MIN_LEN || len > USER_SPACE_DATA) { + pr_err("diag: dci: Invalid length %d len in %s", len, + __func__); + return -EIO; + } /* enter this UID into kernel table and return index */ index = diag_register_dci_transaction(*(int *)temp); if (index < 0) { pr_alert("diag: registering new DCI transaction failed\n"); return DIAG_DCI_NO_REG; } - temp += 4; + temp += sizeof(int); /* * Check for registered peripheral and fwd pkt to * appropriate proc @@ -490,7 +512,12 @@ int diag_process_dci_transaction(unsigned char *buf, int len) subsys_id = (int)(*(char *)temp); temp++; subsys_cmd_code = *(uint16_t *)temp; - temp += 2; + temp += sizeof(uint16_t); + read_len += sizeof(int) + 2 + sizeof(uint16_t); + if (read_len >= USER_SPACE_DATA) { + pr_err("diag: dci: Invalid length in %s\n", __func__); + return -EIO; + } pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code); for (i = 0; i < diag_max_reg; i++) { @@ -524,6 +551,12 @@ int diag_process_dci_transaction(unsigned char *buf, int len) } } } else if (*(int *)temp == DCI_LOG_TYPE) { + /* Minimum length of a log mask config is 12 + 2 bytes for + atleast one log code to be set or reset */ + if (len < DCI_LOG_CON_MIN_LEN || len > USER_SPACE_DATA) { + pr_err("diag: dci: Invalid length in %s\n", __func__); + return -EIO; + } /* find client id and table */ for (i = 0; i < MAX_DCI_CLIENTS; i++) { if (driver->dci_client_tbl[i].client != NULL) { @@ -539,21 +572,33 @@ int diag_process_dci_transaction(unsigned char *buf, int len) return ret; } /* Extract each log code and put in client table */ - temp += 4; - read_len += 4; + temp += sizeof(int); + read_len += sizeof(int); set_mask = *(int *)temp; - temp += 4; - read_len += 4; + temp += sizeof(int); + read_len += sizeof(int); num_codes = *(int *)temp; - temp += 4; - read_len += 4; + temp += sizeof(int); + read_len += sizeof(int); + + if (num_codes == 0 || (num_codes >= (USER_SPACE_DATA - 8)/2)) { + pr_err("diag: dci: Invalid number of log codes %d\n", + num_codes); + return -EIO; + } head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask; + if (!head_log_mask_ptr) { + pr_err("diag: dci: Invalid Log mask pointer in %s\n", + __func__); + return -ENOMEM; + } pr_debug("diag: head of dci log mask %p\n", head_log_mask_ptr); count = 0; /* iterator for extracting log codes */ while (count < num_codes) { if (read_len >= USER_SPACE_DATA) { - pr_err("diag: dci: Log type, possible buffer overflow\n"); + pr_err("diag: dci: Invalid length for log type in %s", + __func__); return -EIO; } log_code = *(uint16_t *)temp; @@ -607,6 +652,12 @@ int diag_process_dci_transaction(unsigned char *buf, int len) /* send updated mask to peripherals */ ret = diag_send_dci_log_mask(driver->smd_cntl[MODEM_DATA].ch); } else if (*(int *)temp == DCI_EVENT_TYPE) { + /* Minimum length of a event mask config is 12 + 4 bytes for + atleast one event id to be set or reset. */ + if (len < DCI_EVENT_CON_MIN_LEN || len > USER_SPACE_DATA) { + pr_err("diag: dci: Invalid length in %s\n", __func__); + return -EIO; + } /* find client id and table */ for (i = 0; i < MAX_DCI_CLIENTS; i++) { if (driver->dci_client_tbl[i].client != NULL) { @@ -622,21 +673,36 @@ int diag_process_dci_transaction(unsigned char *buf, int len) return ret; } /* Extract each log code and put in client table */ - temp += 4; - read_len += 4; + temp += sizeof(int); + read_len += sizeof(int); set_mask = *(int *)temp; - temp += 4; - read_len += 4; + temp += sizeof(int); + read_len += sizeof(int); num_codes = *(int *)temp; - temp += 4; - read_len += 4; + temp += sizeof(int); + read_len += sizeof(int); + + /* Check for positive number of event ids. Also, the number of + event ids should fit in the buffer along with set_mask and + num_codes which are 4 bytes each */ + if (num_codes == 0 || (num_codes >= (USER_SPACE_DATA - 8)/2)) { + pr_err("diag: dci: Invalid number of event ids %d\n", + num_codes); + return -EIO; + } event_mask_ptr = driver->dci_client_tbl[i].dci_event_mask; + if (!event_mask_ptr) { + pr_err("diag: dci: Invalid event mask pointer in %s\n", + __func__); + return -ENOMEM; + } pr_debug("diag: head of dci event mask %p\n", event_mask_ptr); count = 0; /* iterator for extracting log codes */ while (count < num_codes) { if (read_len >= USER_SPACE_DATA) { - pr_err("diag: dci: Event type, possible buffer overflow\n"); + pr_err("diag: dci: Invalid length for event type in %s", + __func__); return -EIO; } event_id = *(int *)temp; diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h index cc6adcd21601..00a257c05163 100644 --- a/drivers/char/diag/diag_dci.h +++ b/drivers/char/diag/diag_dci.h @@ -23,6 +23,9 @@ #define SET_LOG_MASK 1 #define DISABLE_LOG_MASK 0 #define MAX_EVENT_SIZE 100 +#define DCI_PKT_REQ_MIN_LEN 8 +#define DCI_LOG_CON_MIN_LEN 14 +#define DCI_EVENT_CON_MIN_LEN 16 /* 16 log code categories, each has: * 1 bytes equip id + 1 dirty byte + 512 byte max log mask