pcp/pcp-RHEL-213736.patch
Jan Kurik 211742c239
Fix gating QA tests for CVE-2026-16524 and CVE-2026-16529 on 6.3.7
QA 2101 referenced undefined $seq_full on the 6.3.7 testsuite, and QA 2105
shipped an empty pdu-getpdu-overflow file so the integer overflow case was
never exercised.

Resolves: RHEL-213747 CVE-2026-16530
Resolves: RHEL-213736 CVE-2026-16529
Resolves: RHEL-213711 CVE-2026-16527
Resolves: RHEL-213687 CVE-2026-16526
Resolves: RHEL-213658 CVE-2026-16524
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 13:35:05 +02:00

129 lines
3.8 KiB
Diff

From ef848fb978 Mon Sep 17 00:00:00 2001
From: Nathan Scott <nathans@redhat.com>
Subject: [PATCH] libpcp: fix integer overflow in __pmGetPDU() (CWE-190)
When php->len is near INT_MAX (e.g. 0x7FFFFFFF), the buffer size
computation PDU_CHUNK * (1 + php->len / PDU_CHUNK) overflows signed
int, producing a negative value that permanently corrupts the static
maxsize variable. Every subsequent __pmFindPDUBuf() call returns NULL,
rendering the affected daemon (pmlogger, pmcd) unable to process any
further PDUs for the remainder of its lifetime — a persistent denial
of service requiring a restart.
Fix: add an overflow guard (php->len > INT_MAX - PDU_CHUNK) before
the multiplication, returning PM_ERR_TOOBIG for absurdly large PDU
lengths. This protects the NO_LIMIT code path used by pmcd and
pmlogger that is not covered by the existing ceiling check.
Also add _filter_pmcd() to qa/common.pmcd.pdu to normalize fd=N in
pmcd log output, and qa/2105 with a crafted PDU exercising the
overflow.
Reported-by: Francisco Alisson Bezerra, TIM Security Red Team
Reported-by: Lucas Gabriel Alves, TIM Security Red Team
Reported-by: Massimiliano Brolli, TIM Security Red Team
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolves: RHEL-213736 CVE-2026-16529
---
diff --git a/qa/2105 b/qa/2105
new file mode 100755
index 0000000000..44b849ad39
--- /dev/null
+++ b/qa/2105
@@ -0,0 +1,20 @@
+#!/bin/sh
+# PCP QA Test No. 2105
+# Verify __pmGetPDU rejects PDU with len near INT_MAX
+# (integer overflow in buffer size computation, CWE-190)
+#
+# Copyright (c) 2026 Red Hat. All Rights Reserved.
+#
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+mkdir -p pdudata
+printf '\177\377\377\377\000\000\200\000' >pdudata/pdu-getpdu-overflow
+
+pdu_data=pdudata/pdu-getpdu-overflow
+grep_pattern="bad PDU len=.*exceeds maximum|PDU len=.*too large"
+
+# this is one of the generic pmcd PDU exerciser tests ...
+#
+. ./common.pmcd.pdu
diff --git a/qa/2105.out b/qa/2105.out
new file mode 100644
index 0000000000..a304f9d913
--- /dev/null
+++ b/qa/2105.out
@@ -0,0 +1,10 @@
+QA output created by 2105
+expect error(s) to be logged ...
+__pmGetPDU: fd=N type=0x8000 bad PDU len=2147483647 in hdr exceeds maximum client PDU size (65536)
+
+and no valgrind badness ...
+Memcheck, a memory error detector
+LEAK SUMMARY:
+definitely lost: 0 bytes in 0 blocks
+indirectly lost: 0 bytes in 0 blocks
+ERROR SUMMARY: 0 errors from 0 contexts ...
diff --git a/qa/common.pmcd.pdu b/qa/common.pmcd.pdu
index d9e3c6c5c9..9882284b5c 100644
--- a/qa/common.pmcd.pdu
+++ b/qa/common.pmcd.pdu
@@ -55,6 +55,14 @@ _filter()
# end
}
+_filter_pmcd()
+{
+ sed \
+ -e 's/fd=[0-9][0-9]*/fd=N/g' \
+ -e 's/^\[.*\] pmcd([0-9]*) [A-Za-z]*: //' \
+ # end
+}
+
mkdir $tmp || exit 1
cd $tmp
grep sampledso $PCP_PMCDCONF_PATH >pmcd.conf
@@ -91,7 +99,7 @@ wait
[ -s $tmp.err ] && cat $tmp.err
echo "expect error(s) to be logged ..."
-grep -E "$grep_pattern" pmcd.log
+grep -E "$grep_pattern" pmcd.log | _filter_pmcd
echo
echo "and no valgrind badness ..."
diff --git a/qa/group b/qa/group
index d4da513ce2..80a861c5f6 100644
--- a/qa/group
+++ b/qa/group
@@ -2216,4 +2216,5 @@ pmcd.pdu
1990 pcp buddyinfo python local
1991 pcp netstat python local
1992 pmda.uwsgi local
+2105 libpcp local security
4751 libpcp threads valgrind local pcp helgrind
diff --git a/src/libpcp/src/pdu.c b/src/libpcp/src/pdu.c
index 5845932be1..0a4ae75b25 100644
--- a/src/libpcp/src/pdu.c
+++ b/src/libpcp/src/pdu.c
@@ -658,6 +658,14 @@ check_read_len:
PM_LOCK(pdu_lock);
if (php->len > maxsize) {
+ if (php->len > INT_MAX - PDU_CHUNK) {
+ PM_UNLOCK(pdu_lock);
+ if (pmDebugOptions.pdu)
+ pmNotifyErr(LOG_ERR, "%s: fd=%d PDU len=%d too large",
+ __FUNCTION__, fd, php->len);
+ __pmUnpinPDUBuf(pdubuf);
+ return PM_ERR_TOOBIG;
+ }
tmpsize = PDU_CHUNK * ( 1 + php->len / PDU_CHUNK);
maxsize = tmpsize;
}