Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 488e551a authored by Hardik Arya's avatar Hardik Arya
Browse files

diag: Add protection while processing non-hdlc packets



Currently, there is possibility of out-of-bound accesses during
handling of data in non-hdlc path. The patch adds proper protection
when processing non-hdlc packet information to fix the issue.

CRs-Fixed: 2029216
Change-Id: I07c466f85bd8ac08226948fea86b1d8567e68431
Signed-off-by: default avatarHardik Arya <harya@codeaurora.org>
parent bfd96c27
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -538,6 +538,7 @@ struct diagchar_dev {
	unsigned char *buf_feature_mask_update;
	uint8_t hdlc_disabled;
	struct mutex hdlc_disable_mutex;
	struct mutex hdlc_recovery_mutex;
	struct timer_list hdlc_reset_timer;
	struct mutex diag_hdlc_mutex;
	unsigned char *hdlc_buf;
+1 −0
Original line number Diff line number Diff line
@@ -3448,6 +3448,7 @@ static int __init diagchar_init(void)
	mutex_init(&driver->delayed_rsp_mutex);
	mutex_init(&apps_data_mutex);
	mutex_init(&driver->msg_mask_lock);
	mutex_init(&driver->hdlc_recovery_mutex);
	for (i = 0; i < NUM_PERIPHERALS; i++)
		mutex_init(&driver->diagfwd_channel_mutex[i]);
	init_waitqueue_head(&driver->wait_q);
+34 −10
Original line number Diff line number Diff line
/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2008-2017, 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
@@ -1364,7 +1364,9 @@ static void diag_hdlc_start_recovery(unsigned char *buf, int len,

	if (start_ptr) {
		/* Discard any partial packet reads */
		mutex_lock(&driver->hdlc_recovery_mutex);
		driver->incoming_pkt.processing = 0;
		mutex_unlock(&driver->hdlc_recovery_mutex);
		diag_process_non_hdlc_pkt(start_ptr, len - i, info);
	}
}
@@ -1378,18 +1380,24 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len,
	const uint32_t header_len = sizeof(struct diag_pkt_frame_t);
	struct diag_pkt_frame_t *actual_pkt = NULL;
	unsigned char *data_ptr = NULL;
	struct diag_partial_pkt_t *partial_pkt = &driver->incoming_pkt;
	struct diag_partial_pkt_t *partial_pkt = NULL;

	if (!buf || len <= 0)
	mutex_lock(&driver->hdlc_recovery_mutex);
	if (!buf || len <= 0) {
		mutex_unlock(&driver->hdlc_recovery_mutex);
		return;

	if (!partial_pkt->processing)
	}
	partial_pkt = &driver->incoming_pkt;
	if (!partial_pkt->processing) {
		mutex_unlock(&driver->hdlc_recovery_mutex);
		goto start;
	}

	if (partial_pkt->remaining > len) {
		if ((partial_pkt->read_len + len) > partial_pkt->capacity) {
			pr_err("diag: Invalid length %d, %d received in %s\n",
			       partial_pkt->read_len, len, __func__);
			mutex_unlock(&driver->hdlc_recovery_mutex);
			goto end;
		}
		memcpy(partial_pkt->data + partial_pkt->read_len, buf, len);
@@ -1403,6 +1411,7 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len,
			pr_err("diag: Invalid length during partial read %d, %d received in %s\n",
			       partial_pkt->read_len,
			       partial_pkt->remaining, __func__);
			mutex_unlock(&driver->hdlc_recovery_mutex);
			goto end;
		}
		memcpy(partial_pkt->data + partial_pkt->read_len, buf,
@@ -1416,20 +1425,27 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len,
	if (partial_pkt->remaining == 0) {
		actual_pkt = (struct diag_pkt_frame_t *)(partial_pkt->data);
		data_ptr = partial_pkt->data + header_len;
		if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR)
		if (*(uint8_t *)(data_ptr + actual_pkt->length) !=
						CONTROL_CHAR) {
			mutex_unlock(&driver->hdlc_recovery_mutex);
			diag_hdlc_start_recovery(buf, len, info);
			mutex_lock(&driver->hdlc_recovery_mutex);
		}
		err = diag_process_apps_pkt(data_ptr,
					    actual_pkt->length, info);
		if (err) {
			pr_err("diag: In %s, unable to process incoming data packet, err: %d\n",
			       __func__, err);
			mutex_unlock(&driver->hdlc_recovery_mutex);
			goto end;
		}
		partial_pkt->read_len = 0;
		partial_pkt->total_len = 0;
		partial_pkt->processing = 0;
		mutex_unlock(&driver->hdlc_recovery_mutex);
		goto start;
	}
	mutex_unlock(&driver->hdlc_recovery_mutex);
	goto end;

start:
@@ -1442,14 +1458,14 @@ start:
			diag_send_error_rsp(buf, len);
			goto end;
		}

		mutex_lock(&driver->hdlc_recovery_mutex);
		if (pkt_len + header_len > partial_pkt->capacity) {
			pr_err("diag: In %s, incoming data is too large for the request buffer %d\n",
			       __func__, pkt_len);
			mutex_unlock(&driver->hdlc_recovery_mutex);
			diag_hdlc_start_recovery(buf, len, info);
			break;
		}

		if ((pkt_len + header_len) > (len - read_bytes)) {
			partial_pkt->read_len = len - read_bytes;
			partial_pkt->total_len = pkt_len + header_len;
@@ -1457,19 +1473,27 @@ start:
						 partial_pkt->read_len;
			partial_pkt->processing = 1;
			memcpy(partial_pkt->data, buf, partial_pkt->read_len);
			mutex_unlock(&driver->hdlc_recovery_mutex);
			break;
		}
		data_ptr = buf + header_len;
		if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR)
		if (*(uint8_t *)(data_ptr + actual_pkt->length) !=
						CONTROL_CHAR) {
			mutex_unlock(&driver->hdlc_recovery_mutex);
			diag_hdlc_start_recovery(buf, len, info);
			mutex_lock(&driver->hdlc_recovery_mutex);
		}
		else
			hdlc_reset = 0;
		err = diag_process_apps_pkt(data_ptr,
					    actual_pkt->length, info);
		if (err)
		if (err) {
			mutex_unlock(&driver->hdlc_recovery_mutex);
			break;
		}
		read_bytes += header_len + pkt_len + 1;
		buf += header_len + pkt_len + 1; /* advance to next pkt */
		mutex_unlock(&driver->hdlc_recovery_mutex);
	}
end:
	return;