Backport remaining PCP security CVE fixes for 7.1.5-5
Add patches for CVE-2026-16524, CVE-2026-16526, CVE-2026-16527, and CVE-2026-16529 so all TIM security fixes tracked for RHEL 10.3 are present alongside the previously shipped CVE-2026-16530/16531 fixes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
3168b52f62
commit
54531fc403
257
pcp-7.1.5-CVE-2026-16524.patch
Normal file
257
pcp-7.1.5-CVE-2026-16524.patch
Normal file
@ -0,0 +1,257 @@
|
||||
From c5cbeceb7d Mon Sep 17 00:00:00 2001
|
||||
From: Nathan Scott <nathans@redhat.com>
|
||||
Subject: [PATCH] linux_sockets pmda: fix command injection via network.persocket.filter (CWE-78)
|
||||
|
||||
The sockets_check_filter() validation helper returns 1 for safe input
|
||||
and 0 for unsafe input. The guard in sockets_store() tested
|
||||
if (sockets_check_filter(av.cp)) — rejecting safe input and accepting
|
||||
malicious input containing shell metacharacters. The accepted filter
|
||||
was later passed to popen() via shell interpretation, enabling arbitrary
|
||||
command execution as the PMDA process user.
|
||||
|
||||
Fix:
|
||||
- Invert the guard: if (!sockets_check_filter(av.cp))
|
||||
- Replace popen()/pclose() in ss_open_stream() with the libpcp
|
||||
__pmProcessAddArg()/__pmProcessPipe()/__pmProcessPipeClose() API
|
||||
which uses execvp() internally, eliminating shell interpretation
|
||||
of the filter string entirely
|
||||
- Add qa/2101 verifying that valid filters are accepted and shell
|
||||
metacharacters (semicolons, backticks, pipes) are rejected
|
||||
|
||||
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-213661
|
||||
---
|
||||
diff --git a/qa/2101 b/qa/2101
|
||||
new file mode 100755
|
||||
index 0000000000..733b7f70c9
|
||||
--- /dev/null
|
||||
+++ b/qa/2101
|
||||
@@ -0,0 +1,70 @@
|
||||
+#!/bin/sh
|
||||
+# PCP QA Test No. 2101
|
||||
+# Verify linux_sockets PMDA filter validation rejects shell metacharacters
|
||||
+# and accepts valid filter expressions (CWE-78 fix verification)
|
||||
+#
|
||||
+# Copyright (c) 2026 Red Hat. All Rights Reserved.
|
||||
+#
|
||||
+
|
||||
+seq=`basename $0`
|
||||
+echo "QA output created by $seq"
|
||||
+
|
||||
+# get standard environment, filters and checks
|
||||
+. ./common.product
|
||||
+. ./common.filter
|
||||
+. ./common.check
|
||||
+
|
||||
+[ $PCP_PLATFORM = linux ] || _notrun "Linux-specific sockets testing"
|
||||
+[ -f $PCP_PMDAS_DIR/sockets/pmdasockets ] || _notrun "sockets PMDA not installed"
|
||||
+
|
||||
+_cleanup()
|
||||
+{
|
||||
+ _cleanup_pmda sockets
|
||||
+ cd $here
|
||||
+ $sudo rm -rf $tmp $tmp.*
|
||||
+}
|
||||
+
|
||||
+status=0 # success is the default!
|
||||
+trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
+
|
||||
+_prepare_pmda sockets
|
||||
+_stop_auto_restart pmcd
|
||||
+
|
||||
+# install the sockets PMDA
|
||||
+cd $PCP_PMDAS_DIR/sockets
|
||||
+$sudo ./Remove >/dev/null 2>&1
|
||||
+$sudo ./Install </dev/null >$tmp.out 2>&1
|
||||
+cat $tmp.out >>$seq_full
|
||||
+
|
||||
+# check the PMDA is alive
|
||||
+pmprobe -v network.persocket.filter >$tmp.probe 2>&1
|
||||
+grep -q 'No PMCD agent' $tmp.probe && _notrun "sockets PMDA failed to install"
|
||||
+
|
||||
+# real QA test starts here
|
||||
+
|
||||
+echo "=== valid filter should be accepted ==="
|
||||
+pmstore network.persocket.filter "sport == 22" 2>&1 \
|
||||
+| grep -q 'Bad input' && echo "FAIL: valid filter rejected" || echo "valid filter accepted"
|
||||
+
|
||||
+echo
|
||||
+echo "=== shell metacharacter semicolon should be rejected ==="
|
||||
+pmstore network.persocket.filter ';id' 2>&1 \
|
||||
+| grep -q 'Bad input' && echo "metacharacter rejected" || echo "FAIL: metacharacter not rejected"
|
||||
+
|
||||
+echo
|
||||
+echo "=== shell metacharacter backtick should be rejected ==="
|
||||
+pmstore network.persocket.filter '`id`' 2>&1 \
|
||||
+| grep -q 'Bad input' && echo "metacharacter rejected" || echo "FAIL: metacharacter not rejected"
|
||||
+
|
||||
+echo
|
||||
+echo "=== shell metacharacter pipe should be rejected ==="
|
||||
+pmstore network.persocket.filter '|cat /etc/passwd' 2>&1 \
|
||||
+| grep -q 'Bad input' && echo "metacharacter rejected" || echo "FAIL: metacharacter not rejected"
|
||||
+
|
||||
+echo
|
||||
+echo "=== shell metacharacter dollar should be rejected ==="
|
||||
+pmstore network.persocket.filter '${IFS}id' 2>&1 \
|
||||
+| grep -q 'Bad input' && echo "metacharacter rejected" || echo "FAIL: metacharacter not rejected"
|
||||
+
|
||||
+# success, all done
|
||||
+exit
|
||||
diff --git a/qa/2101.out b/qa/2101.out
|
||||
new file mode 100644
|
||||
index 0000000000..02f9ac5655
|
||||
--- /dev/null
|
||||
+++ b/qa/2101.out
|
||||
@@ -0,0 +1,15 @@
|
||||
+QA output created by 2101
|
||||
+=== valid filter should be accepted ===
|
||||
+valid filter accepted
|
||||
+
|
||||
+=== shell metacharacter semicolon should be rejected ===
|
||||
+metacharacter rejected
|
||||
+
|
||||
+=== shell metacharacter backtick should be rejected ===
|
||||
+metacharacter rejected
|
||||
+
|
||||
+=== shell metacharacter pipe should be rejected ===
|
||||
+metacharacter rejected
|
||||
+
|
||||
+=== shell metacharacter dollar should be rejected ===
|
||||
+metacharacter rejected
|
||||
diff --git a/qa/group b/qa/group
|
||||
index 1533ad5720..2c6529b718 100644
|
||||
--- a/qa/group
|
||||
+++ b/qa/group
|
||||
@@ -2375,5 +2375,6 @@ suse
|
||||
2105 libpcp pmcd local security pmcd.pdu
|
||||
2100 pmproxy local security
|
||||
2104 libpcp local security
|
||||
+2101 pmda.sockets local security
|
||||
4751 libpcp threads valgrind local pcp helgrind
|
||||
9000 other local
|
||||
diff --git a/src/pmdas/linux_sockets/pmda.c b/src/pmdas/linux_sockets/pmda.c
|
||||
index 4d51207275..2cb46d8e89 100644
|
||||
--- a/src/pmdas/linux_sockets/pmda.c
|
||||
+++ b/src/pmdas/linux_sockets/pmda.c
|
||||
@@ -162,11 +162,9 @@ sockets_check_filter(const char *string)
|
||||
const char *p;
|
||||
|
||||
for (p = string; *p; p++) {
|
||||
- if (isspace(*p))
|
||||
+ if (isspace(*p) || isalnum(*p))
|
||||
continue;
|
||||
- if (isalnum(*p))
|
||||
- continue;
|
||||
- if (*p == '(' || *p == ')')
|
||||
+ if (strchr("()=!<>:.*/-,", *p) != NULL)
|
||||
continue;
|
||||
return 0; /* disallow */
|
||||
}
|
||||
@@ -191,7 +189,7 @@ sockets_store(pmdaResult *result, pmdaExt *pmda)
|
||||
case 0: /* network.persocket.filter */
|
||||
if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0],
|
||||
PM_TYPE_STRING, &av, PM_TYPE_STRING)) >= 0) {
|
||||
- if (sockets_check_filter(av.cp)) {
|
||||
+ if (!sockets_check_filter(av.cp)) {
|
||||
sts = PM_ERR_BADSTORE;
|
||||
free(av.cp);
|
||||
break;
|
||||
diff --git a/src/pmdas/linux_sockets/ss_stream.c b/src/pmdas/linux_sockets/ss_stream.c
|
||||
index 421c65fd16..833fc275a0 100644
|
||||
--- a/src/pmdas/linux_sockets/ss_stream.c
|
||||
+++ b/src/pmdas/linux_sockets/ss_stream.c
|
||||
@@ -14,18 +14,19 @@
|
||||
|
||||
#include <pcp/pmapi.h>
|
||||
#include <pcp/pmda.h>
|
||||
+#include <pcp/libpcp.h>
|
||||
#include "ss_stats.h"
|
||||
|
||||
#define SS_OPTIONS "-noemitauO"
|
||||
|
||||
-char *ss_filter = NULL; /* storable: network.persocket.filter */
|
||||
+char *ss_filter; /* storable: network.persocket.filter */
|
||||
+static int using_pipe; /* pipe is normal operation, QA uses files */
|
||||
|
||||
FILE *
|
||||
ss_open_stream()
|
||||
{
|
||||
- FILE *fp;
|
||||
+ FILE *fp = NULL;
|
||||
char *path;
|
||||
- char cmd[MAXPATHLEN];
|
||||
|
||||
if (ss_filter == NULL) {
|
||||
/* pmstore to network.persocket.filter frees this if changing */
|
||||
@@ -38,17 +39,51 @@ ss_open_stream()
|
||||
fp = fopen(path, "r");
|
||||
if (pmDebugOptions.appl0)
|
||||
fprintf(stderr, "ss_open_stream: open PCPQA_PMDA_SOCKETS=%s\n", path);
|
||||
+ using_pipe = 0;
|
||||
} else {
|
||||
+ __pmExecCtl_t *argp = NULL;
|
||||
+ int sts;
|
||||
+
|
||||
if (access((path = "/usr/sbin/ss"), X_OK) != 0) {
|
||||
if (access((path = "/usr/bin/ss"), X_OK) != 0) {
|
||||
fprintf(stderr, "Error: no \"ss\" binary found\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
- pmsprintf(cmd, sizeof(cmd), "%s %s %s", path, SS_OPTIONS, ss_filter);
|
||||
- fp = popen(cmd, "r");
|
||||
+ if ((sts = __pmProcessAddArg(&argp, path)) < 0 ||
|
||||
+ (sts = __pmProcessAddArg(&argp, SS_OPTIONS)) < 0) {
|
||||
+ if (pmDebugOptions.appl0)
|
||||
+ fprintf(stderr, "ss_open_stream: __pmProcessAddArg failed: %s\n",
|
||||
+ pmErrStr(sts));
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ if (ss_filter[0] != '\0') {
|
||||
+ char *s, *tok, *saveptr;
|
||||
+
|
||||
+ if ((s = strdup(ss_filter)) == NULL)
|
||||
+ return NULL;
|
||||
+ for (tok = strtok_r(s, " \t", &saveptr); tok != NULL;
|
||||
+ tok = strtok_r(NULL, " \t", &saveptr)) {
|
||||
+ if ((sts = __pmProcessAddArg(&argp, tok)) < 0) {
|
||||
+ free(s);
|
||||
+ if (pmDebugOptions.appl0)
|
||||
+ fprintf(stderr, "ss_open_stream: __pmProcessAddArg failed: %s\n",
|
||||
+ pmErrStr(sts));
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ }
|
||||
+ free(s);
|
||||
+ }
|
||||
+ if ((sts = __pmProcessPipe(&argp, "r", PM_EXEC_TOSS_NONE, &fp)) < 0) {
|
||||
+ if (pmDebugOptions.appl0)
|
||||
+ fprintf(stderr, "ss_open_stream: __pmProcessPipe failed: %s\n",
|
||||
+ pmErrStr(sts));
|
||||
+ return NULL;
|
||||
+ }
|
||||
if (pmDebugOptions.appl0)
|
||||
- fprintf(stderr, "ss_open_stream: popen %s\n", cmd);
|
||||
+ fprintf(stderr, "ss_open_stream: exec %s %s %s\n",
|
||||
+ path, SS_OPTIONS, ss_filter);
|
||||
+ using_pipe = 1;
|
||||
}
|
||||
|
||||
return fp;
|
||||
@@ -57,8 +92,8 @@ ss_open_stream()
|
||||
void
|
||||
ss_close_stream(FILE *fp)
|
||||
{
|
||||
- if (getenv("PCPQA_PMDA_SOCKETS") != NULL)
|
||||
- fclose(fp);
|
||||
+ if (using_pipe)
|
||||
+ __pmProcessPipeClose(fp);
|
||||
else
|
||||
- pclose(fp);
|
||||
+ fclose(fp);
|
||||
}
|
||||
1717
pcp-7.1.5-CVE-2026-16526.patch
Normal file
1717
pcp-7.1.5-CVE-2026-16526.patch
Normal file
File diff suppressed because it is too large
Load Diff
197
pcp-7.1.5-CVE-2026-16527.patch
Normal file
197
pcp-7.1.5-CVE-2026-16527.patch
Normal file
@ -0,0 +1,197 @@
|
||||
From d96ba5a716 Mon Sep 17 00:00:00 2001
|
||||
From: Nathan Scott <nathans@redhat.com>
|
||||
Subject: [PATCH] pmproxy: fix missing -Q and -S authentication flags (CWE-306)
|
||||
|
||||
The pmproxy -Q (require client certificate) and -S (require
|
||||
authenticated clients) flags existed as case blocks in the option
|
||||
parser but were absent from the short_options string and the longopts
|
||||
table, making them permanently unreachable. An unauthenticated HTTP
|
||||
client could access all REST API endpoints including /store and /derive.
|
||||
|
||||
Fix:
|
||||
- Add Q and S to short_options so pmgetopt_r() delivers them
|
||||
- Add --certreqd and --reqauth entries to the longopts table
|
||||
- Document both flags in the pmproxy(1) man page
|
||||
- Add qa/2100 verifying the flags are accepted and that -S correctly
|
||||
rejects unauthenticated REST API requests with HTTP 403
|
||||
|
||||
Note: -S enforcement in the REST API path already exists in http.c and
|
||||
webapi.c. -Q (CERT_REQD) enforcement is only implemented for the
|
||||
legacy PCP wire protocol path, not the REST API; this is a pre-existing
|
||||
limitation to be addressed separately.
|
||||
|
||||
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-213717
|
||||
---
|
||||
diff --git a/man/man1/pmproxy.1 b/man/man1/pmproxy.1
|
||||
index 6fa0ba94c1..af173edb91 100644
|
||||
--- a/man/man1/pmproxy.1
|
||||
+++ b/man/man1/pmproxy.1
|
||||
@@ -234,6 +234,9 @@ Specify an alternate
|
||||
number to listen on for client connections.
|
||||
The default value is 44322.
|
||||
.TP
|
||||
+\fB\-Q\f1, \fB\-\-certreqd\f1
|
||||
+Require that all client connections provide a trusted client certificate.
|
||||
+.TP
|
||||
\f3\-r\f1 \f2port\f1, \f3\-\-keyport\f1=\f2port\f1
|
||||
Specify an alternate key-value server
|
||||
.I port
|
||||
@@ -248,6 +251,9 @@ The default value is
|
||||
.IR $PCP_RUN_DIR/pmproxy.socket .
|
||||
This option implies \f3pmproxy\f1 is running in \f3timeseries\f1 mode.
|
||||
.TP
|
||||
+\fB\-S\f1, \fB\-\-reqauth\f1
|
||||
+Require that all client connections be authenticated.
|
||||
+.TP
|
||||
\fB\-t\f1, \fB\-\-timeseries\f1
|
||||
Operate in automatic archive timeseries discovery mode.
|
||||
This mode of operation will enable the
|
||||
diff --git a/qa/2100 b/qa/2100
|
||||
new file mode 100755
|
||||
index 0000000000..1de957a2f1
|
||||
--- /dev/null
|
||||
+++ b/qa/2100
|
||||
@@ -0,0 +1,85 @@
|
||||
+#!/bin/sh
|
||||
+# PCP QA Test No. 2100
|
||||
+# Verify pmproxy -Q and -S authentication flags are accepted
|
||||
+# and that -S (reqauth) enforces authentication on REST API
|
||||
+#
|
||||
+# Copyright (c) 2026 Red Hat. All Rights Reserved.
|
||||
+#
|
||||
+
|
||||
+seq=`basename $0`
|
||||
+echo "QA output created by $seq"
|
||||
+
|
||||
+# get standard environment, filters and checks
|
||||
+. ./common.product
|
||||
+. ./common.filter
|
||||
+. ./common.check
|
||||
+
|
||||
+which curl >/dev/null 2>&1 || _notrun "no curl executable installed"
|
||||
+
|
||||
+_cleanup()
|
||||
+{
|
||||
+ [ -n "$__pid" ] && kill $__pid 2>/dev/null
|
||||
+ wait $__pid 2>/dev/null
|
||||
+ cd $here
|
||||
+ $sudo rm -rf $tmp $tmp.*
|
||||
+}
|
||||
+
|
||||
+status=0 # success is the default!
|
||||
+__pid=""
|
||||
+trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
+
|
||||
+# real QA test starts here
|
||||
+
|
||||
+echo "=== checking -Q and -S appear in usage ==="
|
||||
+pmproxy --help 2>&1 | grep -E '\-[QS]' | sed -e 's/^ *//'
|
||||
+
|
||||
+echo
|
||||
+echo "=== checking -S enforces authentication on REST API ==="
|
||||
+__port=`_find_free_port`
|
||||
+$PCP_BINADM_DIR/pmproxy -S -f -p $__port -l $tmp.log &
|
||||
+__pid=$!
|
||||
+sleep 1
|
||||
+if kill -0 $__pid 2>/dev/null; then
|
||||
+ echo "pmproxy with -S started"
|
||||
+
|
||||
+ # unauthenticated request should be rejected
|
||||
+ __code=`curl -s -o /dev/null -w '%{http_code}' "http://localhost:$__port/pmapi/context?hostspec=localhost" 2>/dev/null`
|
||||
+ if [ "$__code" = "403" ]; then
|
||||
+ echo "unauthenticated request correctly rejected (HTTP $__code)"
|
||||
+ else
|
||||
+ echo "FAIL: expected HTTP 403, got HTTP $__code"
|
||||
+ fi
|
||||
+
|
||||
+ kill $__pid
|
||||
+ wait $__pid 2>/dev/null
|
||||
+ __pid=""
|
||||
+else
|
||||
+ echo "FAIL: pmproxy with -S did not start"
|
||||
+fi
|
||||
+
|
||||
+echo
|
||||
+echo "=== checking without -S allows unauthenticated access ==="
|
||||
+__port=`_find_free_port`
|
||||
+$PCP_BINADM_DIR/pmproxy -f -p $__port -l $tmp.log2 &
|
||||
+__pid=$!
|
||||
+sleep 1
|
||||
+if kill -0 $__pid 2>/dev/null; then
|
||||
+ echo "pmproxy without -S started"
|
||||
+
|
||||
+ # unauthenticated request should succeed
|
||||
+ __code=`curl -s -o /dev/null -w '%{http_code}' "http://localhost:$__port/pmapi/context?hostspec=localhost" 2>/dev/null`
|
||||
+ if [ "$__code" = "200" ]; then
|
||||
+ echo "unauthenticated request correctly allowed (HTTP $__code)"
|
||||
+ else
|
||||
+ echo "FAIL: expected HTTP 200, got HTTP $__code"
|
||||
+ fi
|
||||
+
|
||||
+ kill $__pid
|
||||
+ wait $__pid 2>/dev/null
|
||||
+ __pid=""
|
||||
+else
|
||||
+ echo "FAIL: pmproxy without -S did not start"
|
||||
+fi
|
||||
+
|
||||
+# success, all done
|
||||
+exit
|
||||
diff --git a/qa/2100.out b/qa/2100.out
|
||||
new file mode 100644
|
||||
index 0000000000..5302aea77a
|
||||
--- /dev/null
|
||||
+++ b/qa/2100.out
|
||||
@@ -0,0 +1,12 @@
|
||||
+QA output created by 2100
|
||||
+=== checking -Q and -S appear in usage ===
|
||||
+-Q, --certreqd require client certificate authentication
|
||||
+-S, --reqauth require all client connections to be authenticated
|
||||
+
|
||||
+=== checking -S enforces authentication on REST API ===
|
||||
+pmproxy with -S started
|
||||
+unauthenticated request correctly rejected (HTTP 403)
|
||||
+
|
||||
+=== checking without -S allows unauthenticated access ===
|
||||
+pmproxy without -S started
|
||||
+unauthenticated request correctly allowed (HTTP 200)
|
||||
diff --git a/qa/group b/qa/group
|
||||
index 3590d90dc2..92c27dc49f 100644
|
||||
--- a/qa/group
|
||||
+++ b/qa/group
|
||||
@@ -2373,5 +2373,6 @@ suse
|
||||
1998 pmda.rds local python
|
||||
2106 pmproxy local security
|
||||
2105 libpcp pmcd local security pmcd.pdu
|
||||
+2100 pmproxy local security
|
||||
4751 libpcp threads valgrind local pcp helgrind
|
||||
9000 other local
|
||||
diff --git a/src/pmproxy/src/pmproxy.c b/src/pmproxy/src/pmproxy.c
|
||||
index ce2a9ebf53..5db0d5fdb9 100644
|
||||
--- a/src/pmproxy/src/pmproxy.c
|
||||
+++ b/src/pmproxy/src/pmproxy.c
|
||||
@@ -82,7 +82,9 @@ static pmLongOptions longopts[] = {
|
||||
PMAPI_OPTIONS_HEADER("Connection options"),
|
||||
{ "interface", 1, 'i', "ADDR", "accept connections on this IP address" },
|
||||
{ "port", 1, 'p', "PORT", "accept connections on this port" },
|
||||
+ { "certreqd", 0, 'Q', 0, "require client certificate authentication" },
|
||||
{ "socket", 1, 's', "PATH", "Unix domain socket file [default $PCP_RUN_DIR/pmproxy.socket]" },
|
||||
+ { "reqauth", 0, 'S', 0, "require all client connections to be authenticated" },
|
||||
{ "keyport", 1, 'r', "PORT", "Connect to key server on this TCP/IP port (implies --timeseries)" },
|
||||
{ "keyhost", 1, 'h', "HOST", "Connect to key server on this host name (implies --timeseries)" },
|
||||
{ "redisport", 1, 'r', "PORT", "Backwards-compatibility option, do not use" },
|
||||
@@ -95,7 +97,7 @@ static pmLongOptions longopts[] = {
|
||||
};
|
||||
|
||||
static pmOptions opts = {
|
||||
- .short_options = "Ac:dD:Ffh:i:l:L:p:r:s:tT:U:x:?",
|
||||
+ .short_options = "Ac:dD:Ffh:i:l:L:p:Qr:s:StT:U:x:?",
|
||||
.long_options = longopts,
|
||||
};
|
||||
|
||||
149
pcp-7.1.5-CVE-2026-16529.patch
Normal file
149
pcp-7.1.5-CVE-2026-16529.patch
Normal file
@ -0,0 +1,149 @@
|
||||
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-213726
|
||||
---
|
||||
diff --git a/qa/2105 b/qa/2105
|
||||
new file mode 100755
|
||||
index 0000000000..44b849ad39
|
||||
--- /dev/null
|
||||
+++ b/qa/2105
|
||||
@@ -0,0 +1,17 @@
|
||||
+#!/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"
|
||||
+
|
||||
+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 4ab98775e0..8fa832aa82 100644
|
||||
--- a/qa/common.pmcd.pdu
|
||||
+++ b/qa/common.pmcd.pdu
|
||||
@@ -54,6 +54,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
|
||||
@@ -90,7 +98,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 51e9e4d761..3590d90dc2 100644
|
||||
--- a/qa/group
|
||||
+++ b/qa/group
|
||||
@@ -2372,5 +2372,6 @@ suse
|
||||
1997 pcp nfsiostat python local
|
||||
1998 pmda.rds local python
|
||||
2106 pmproxy local security
|
||||
+2105 libpcp pmcd local security pmcd.pdu
|
||||
4751 libpcp threads valgrind local pcp helgrind
|
||||
9000 other local
|
||||
diff --git a/qa/pdudata/pdu-getpdu-overflow b/qa/pdudata/pdu-getpdu-overflow
|
||||
new file mode 100644
|
||||
index 0000000000..5e57cd4b7a
|
||||
Binary files /dev/null and b/qa/pdudata/pdu-getpdu-overflow differ
|
||||
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;
|
||||
}
|
||||
diff --git a/src/libpcp3/src/pdu.c b/src/libpcp3/src/pdu.c
|
||||
index 5845932be1..0a4ae75b25 100644
|
||||
--- a/src/libpcp3/src/pdu.c
|
||||
+++ b/src/libpcp3/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;
|
||||
}
|
||||
24
pcp.spec
24
pcp.spec
@ -1,6 +1,6 @@
|
||||
Name: pcp
|
||||
Version: 7.1.5
|
||||
Release: 4%{?dist}
|
||||
Release: 5%{?dist}
|
||||
Summary: System-level performance monitoring and performance management
|
||||
License: GPL-2.0-or-later AND LGPL-2.1-or-later AND CC-BY-3.0
|
||||
URL: https://pcp.io
|
||||
@ -19,6 +19,22 @@ Patch0: pcp-7.1.5-CVE-2026-16530.patch
|
||||
# https://github.com/performancecopilot/pcp/commit/dd6ed05f143f97e00198cb4b0180c6375d758cbe
|
||||
Patch1: pcp-7.1.5-CVE-2026-16531.patch
|
||||
|
||||
# https://issues.redhat.com/browse/RHEL-213726
|
||||
# https://github.com/performancecopilot/pcp/commit/ef848fb978d26335f9676933f541c73b58f130a6
|
||||
Patch2: pcp-7.1.5-CVE-2026-16529.patch
|
||||
|
||||
# https://issues.redhat.com/browse/RHEL-213717
|
||||
# https://github.com/performancecopilot/pcp/commit/d96ba5a716eeff7840138eb08fbab0d11a57f641
|
||||
Patch3: pcp-7.1.5-CVE-2026-16527.patch
|
||||
|
||||
# https://issues.redhat.com/browse/RHEL-213692
|
||||
# https://github.com/performancecopilot/pcp/commit/7e27614006ff6fc4925991edbedaf1eab6b14731
|
||||
Patch4: pcp-7.1.5-CVE-2026-16526.patch
|
||||
|
||||
# https://issues.redhat.com/browse/RHEL-213661
|
||||
# https://github.com/performancecopilot/pcp/commit/c5cbeceb7d3c2af357c04065cdd911efdc270de0
|
||||
Patch5: pcp-7.1.5-CVE-2026-16524.patch
|
||||
|
||||
# The additional linker flags break out-of-tree PMDAs.
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2043092
|
||||
%undefine _package_note_flags
|
||||
@ -3432,6 +3448,12 @@ fi
|
||||
%files zeroconf -f pcp-zeroconf-files.rpm
|
||||
|
||||
%changelog
|
||||
* Wed Aug 12 2026 Jan Kuřík <jkurik@redhat.com> - 7.1.5-5
|
||||
- Fix CVE-2026-16524: linux_sockets PMDA command injection (RHEL-213661)
|
||||
- Fix CVE-2026-16526: FD_CLOEXEC privilege escalation via pmdaroot (RHEL-213692)
|
||||
- Fix CVE-2026-16527: pmproxy unauthenticated /store access (RHEL-213717)
|
||||
- Fix CVE-2026-16529: __pmGetPDU signed integer overflow DoS (RHEL-213726)
|
||||
|
||||
* Thu Jul 30 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 7.1.5-4
|
||||
- Fix CVE-2026-16531: path traversal via hostname in pmproxy (RHEL-213756)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user