[PATCH v2 2/3] perf hisi-ptt: Strengthen auxtrace event handling and packet type detection

Sizhe Liu <[email protected]>
Newsgroups org.kernel.vger.linux-pci,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-perf-users
Message-ID <[email protected]>
Fix pre-existing robustness issues in the hisi-ptt auxtrace decoder
reported by Sashiko:

1. Endianness in hisi_ptt_check_packet_type(): The first 32-bit word was
read with a host-endian memory cast (*(uint32_t *)buf). On big-endian
hosts analyzing a little-endian trace, the bit[31:11] 8DW magic check
fails and every 8DW packet is misclassified as 4DW. Read the header
with get_unaligned_le32().

2. Heap out-of-bounds read: hisi_ptt_dump() called
hisi_ptt_check_packet_type() which dereferenced 4 bytes of the buffer
without any size check. A malformed or truncated event with
auxtrace.size in {0,1,2,3} may cause a heap OOB read. Pass the buffer
length to hisi_ptt_check_packet_type() and return (defaulting to
4DW) when the buffer is shorter than HISI_PTT_FIELD_LENTH.

3. Integer truncation: event->auxtrace.size is __u64 but was stored in
an int. Traces larger than 2GB became negative (malloc failure), and
huge sizes wrapping to a small positive caused a short readn() that
left unread payload in the pipe and permanently desynchronized the
stream. Use u64 for the size, reject anything larger than SSIZE_MAX
before malloc (same bound used by auxtrace_copy_data()), and compare
readn()'s return value against (ssize_t)size to detect truncation.

Signed-off-by: Sizhe Liu <[email protected]>
---
 tools/perf/util/hisi-ptt.c | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/hisi-ptt.c b/tools/perf/util/hisi-ptt.c
index e4cc4785f744..d6f48993fdd6 100644
--- a/tools/perf/util/hisi-ptt.c
+++ b/tools/perf/util/hisi-ptt.c
@@ -8,10 +8,12 @@
 #include <endian.h>
 #include <errno.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <linux/bitops.h>
 #include <linux/kernel.h>
 #include <linux/log2.h>
 #include <linux/types.h>
+#include <linux/unaligned.h>
 #include <linux/zalloc.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -35,9 +37,15 @@ struct hisi_ptt {
 	u32 pmu_type;
 };
 
-static enum hisi_ptt_pkt_type hisi_ptt_check_packet_type(unsigned char *buf)
+static enum hisi_ptt_pkt_type hisi_ptt_check_packet_type(unsigned char *buf,
+							 size_t len)
 {
-	uint32_t head = *(uint32_t *)buf;
+	uint32_t head;
+
+	if (len < HISI_PTT_FIELD_LENTH)
+		return HISI_PTT_4DW_PKT;
+
+	head = get_unaligned_le32(buf);
 
 	if ((HISI_PTT_8DW_CHECK_MASK & head) == HISI_PTT_IS_8DW_PKT)
 		return HISI_PTT_8DW_PKT;
@@ -53,7 +61,7 @@ static void hisi_ptt_dump(struct hisi_ptt *ptt __maybe_unused,
 	size_t pos = 0;
 	int pkt_len;
 
-	type = hisi_ptt_check_packet_type(buf);
+	type = hisi_ptt_check_packet_type(buf, len);
 	len = round_down(len, hisi_ptt_pkt_size[type]);
 	color_fprintf(stdout, color, ". ... HISI PTT data: size %zu bytes\n",
 		      len);
@@ -91,11 +99,15 @@ static int hisi_ptt_process_auxtrace_event(struct perf_session *session,
 	struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt,
 					    auxtrace);
 	int fd = perf_data__fd(session->data);
-	int size = event->auxtrace.size;
-	void *data = malloc(size);
+	u64 size = event->auxtrace.size;
 	off_t data_offset;
-	int err;
+	ssize_t err;
+	void *data;
+
+	if (size > SSIZE_MAX)
+		return -EINVAL;
 
+	data = malloc(size);
 	if (!data)
 		return -errno;
 
-- 
2.33.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.