Compare commits

...

No commits in common. 'i9c-beta' and 'c9' have entirely different histories.
i9c-beta ... c9

@ -0,0 +1,27 @@
commit 3bde240a2acc85e63e2f7813330713dd9b59386e
Author: Nathan Scott <nathans@redhat.com>
Date: Wed Mar 27 14:51:28 2024 +1100
pmproxy: disable Redis protocol proxying by default
If a redis-server has been locked down in terms of connections,
we want to prevent pmproxy from being allowed to send arbitrary
RESP commands to it.
This protocol proxying doesn't affect PCP functionality at all,
its more of a developer/sysadmin convenience when Redis used in
cluster mode (relatively uncommon compared to localhost mode).
diff --git a/src/pmproxy/pmproxy.conf b/src/pmproxy/pmproxy.conf
index e54891792e..4cbc1c96af 100644
--- a/src/pmproxy/pmproxy.conf
+++ b/src/pmproxy/pmproxy.conf
@@ -29,7 +29,7 @@ pcp.enabled = true
http.enabled = true
# support Redis protocol proxying
-redis.enabled = true
+redis.enabled = false
# support SSL/TLS protocol wrapping
secure.enabled = true

@ -0,0 +1,320 @@
diff -Naurp pcp-5.3.7.orig/qa/src/test_pcp_sockets.python pcp-5.3.7/qa/src/test_pcp_sockets.python
--- pcp-5.3.7.orig/qa/src/test_pcp_sockets.python 1970-01-01 10:00:00.000000000 +1000
+++ pcp-5.3.7/qa/src/test_pcp_sockets.python 2024-09-09 13:47:06.848083320 +1000
@@ -0,0 +1,23 @@
+import socket
+import cpmapi as api
+from pcp import pmapi
+
+address = 'localhost'
+port = 44321
+
+c = []
+for i in range(0, 1234):
+ print('context', i)
+ ctx = pmapi.pmContext(api.PM_CONTEXT_HOST, "local:")
+ print('created', i)
+ c.append(ctx)
+
+s = []
+for i in range(0, 1234):
+ sock = socket.socket()
+ print('socket', i)
+ sock.connect((address, port))
+ print('connect', i)
+ sock.send(b"abba\r") # -- gives a too-large PDU
+ print('send', i)
+ # s.append(sock) # -- exercise pduread: timeout
diff -Naurp pcp-5.3.7.orig/src/libpcp/src/pdu.c pcp-5.3.7/src/libpcp/src/pdu.c
--- pcp-5.3.7.orig/src/libpcp/src/pdu.c 2022-04-05 09:05:43.000000000 +1000
+++ pcp-5.3.7/src/libpcp/src/pdu.c 2024-09-09 13:47:06.869083364 +1000
@@ -186,10 +186,7 @@ pduread(int fd, char *buf, int len, int
* Need all parts of the PDU to be received by dead_hand
* This enforces a low overall timeout for the whole PDU
* (as opposed to just a timeout for individual calls to
- * recv). A more invasive alternative (better) approach
- * would see all I/O performed in the main event loop,
- * and I/O routines transformed to continuation-passing
- * style.
+ * recv).
*/
gettimeofday(&dead_hand, NULL);
dead_hand.tv_sec += wait.tv_sec;
@@ -499,9 +496,10 @@ PM_FAULT_RETURN(PM_ERR_TIMEOUT);
if (len == -1) {
if (! __pmSocketClosed()) {
char errmsg[PM_MAXERRMSGLEN];
- pmNotifyErr(LOG_ERR, "%s: fd=%d hdr read: len=%d: %s",
- "__pmGetPDU", fd, len,
- pmErrStr_r(-oserror(), errmsg, sizeof(errmsg)));
+ if (pmDebugOptions.pdu)
+ pmNotifyErr(LOG_ERR, "%s: fd=%d hdr read: len=%d: %s",
+ "__pmGetPDU", fd, len,
+ pmErrStr_r(-oserror(), errmsg, sizeof(errmsg)));
}
}
else if (len >= (int)sizeof(php->len)) {
@@ -520,15 +518,17 @@ PM_FAULT_RETURN(PM_ERR_TIMEOUT);
}
else if (len < 0) {
char errmsg[PM_MAXERRMSGLEN];
- pmNotifyErr(LOG_ERR, "%s: fd=%d hdr read: len=%d: %s",
- "__pmGetPDU", fd, len,
- pmErrStr_r(len, errmsg, sizeof(errmsg)));
+ if (pmDebugOptions.pdu)
+ pmNotifyErr(LOG_ERR, "%s: fd=%d hdr read: len=%d: %s",
+ "__pmGetPDU", fd, len,
+ pmErrStr_r(len, errmsg, sizeof(errmsg)));
__pmUnpinPDUBuf(pdubuf);
return PM_ERR_IPC;
}
else if (len > 0) {
- pmNotifyErr(LOG_ERR, "%s: fd=%d hdr read: bad len=%d",
- "__pmGetPDU", fd, len);
+ if (pmDebugOptions.pdu)
+ pmNotifyErr(LOG_ERR, "%s: fd=%d hdr read: bad len=%d",
+ "__pmGetPDU", fd, len);
__pmUnpinPDUBuf(pdubuf);
return PM_ERR_IPC;
}
@@ -547,8 +547,9 @@ check_read_len:
* PDU length indicates insufficient bytes for a PDU header
* ... looks like DOS attack like PV 935490
*/
- pmNotifyErr(LOG_ERR, "%s: fd=%d illegal PDU len=%d in hdr",
- "__pmGetPDU", fd, php->len);
+ if (pmDebugOptions.pdu)
+ pmNotifyErr(LOG_ERR, "%s: fd=%d illegal PDU len=%d in hdr",
+ "__pmGetPDU", fd, php->len);
__pmUnpinPDUBuf(pdubuf);
return PM_ERR_IPC;
}
@@ -559,16 +560,18 @@ check_read_len:
* (note, pmcd and pmdas have to be able to _send_ large PDUs,
* e.g. for a pmResult or instance domain enquiry)
*/
- if (len < (int)(sizeof(php->len) + sizeof(php->type)))
- /* PDU too short to provide a valid type */
- pmNotifyErr(LOG_ERR, "%s: fd=%d bad PDU len=%d in hdr "
- "exceeds maximum client PDU size (%d)",
- "__pmGetPDU", fd, php->len, ceiling);
- else
- pmNotifyErr(LOG_ERR, "%s: fd=%d type=0x%x bad PDU len=%d in hdr "
- "exceeds maximum client PDU size (%d)",
- "__pmGetPDU", fd, (unsigned)ntohl(php->type),
- php->len, ceiling);
+ if (pmDebugOptions.pdu) {
+ if (len < (int)(sizeof(php->len) + sizeof(php->type)))
+ /* PDU too short to provide a valid type */
+ pmNotifyErr(LOG_ERR, "%s: fd=%d bad PDU len=%d in hdr"
+ " exceeds maximum client PDU size (%d)",
+ "__pmGetPDU", fd, php->len, ceiling);
+ else
+ pmNotifyErr(LOG_ERR, "%s: fd=%d type=0x%x bad PDU len=%d in hdr"
+ " exceeds maximum client PDU size (%d)",
+ "__pmGetPDU", fd, (unsigned)ntohl(php->type),
+ php->len, ceiling);
+ }
__pmUnpinPDUBuf(pdubuf);
return PM_ERR_TOOBIG;
}
@@ -608,6 +611,10 @@ check_read_len:
__pmUnpinPDUBuf(pdubuf);
return PM_ERR_TIMEOUT;
}
+ else if (!pmDebugOptions.pdu) {
+ __pmUnpinPDUBuf(pdubuf);
+ return PM_ERR_IPC;
+ }
else if (len < 0) {
char errmsg[PM_MAXERRMSGLEN];
pmNotifyErr(LOG_ERR, "%s: fd=%d data read: len=%d: %s",
@@ -641,7 +648,8 @@ check_read_len:
* PDU type is bad ... could be a possible mem leak attack like
* https://bugzilla.redhat.com/show_bug.cgi?id=841319
*/
- pmNotifyErr(LOG_ERR, "%s: fd=%d illegal PDU type=%d in hdr",
+ if (pmDebugOptions.pdu)
+ pmNotifyErr(LOG_ERR, "%s: fd=%d illegal PDU type=%d in hdr",
"__pmGetPDU", fd, php->type);
__pmUnpinPDUBuf(pdubuf);
return PM_ERR_IPC;
diff -Naurp pcp-5.3.7.orig/src/libpcp_web/src/load.h pcp-5.3.7/src/libpcp_web/src/load.h
--- pcp-5.3.7.orig/src/libpcp_web/src/load.h 2021-02-17 15:27:41.000000000 +1100
+++ pcp-5.3.7/src/libpcp_web/src/load.h 2024-09-09 13:45:56.531933622 +1000
@@ -42,8 +42,9 @@ typedef struct context {
unsigned int setup : 1; /* context established */
unsigned int cached : 1; /* context/source in cache */
unsigned int garbage : 1; /* context pending removal */
+ unsigned int inactive: 1; /* context removal deferred */
unsigned int updated : 1; /* context labels are updated */
- unsigned int padding : 4; /* zero-filled struct padding */
+ unsigned int padding : 3; /* zero-filled struct padding */
unsigned int refcount : 16; /* currently-referenced counter */
unsigned int timeout; /* context timeout in milliseconds */
uv_timer_t timer;
diff -Naurp pcp-5.3.7.orig/src/libpcp_web/src/webgroup.c pcp-5.3.7/src/libpcp_web/src/webgroup.c
--- pcp-5.3.7.orig/src/libpcp_web/src/webgroup.c 2024-09-09 13:44:34.166748200 +1000
+++ pcp-5.3.7/src/libpcp_web/src/webgroup.c 2024-09-09 13:45:56.531933622 +1000
@@ -134,9 +134,18 @@ webgroup_timeout_context(uv_timer_t *arg
* is returned to zero by the caller, or background cleanup
* finds this context and cleans it.
*/
- if (cp->refcount == 0 && cp->garbage == 0) {
- cp->garbage = 1;
- uv_timer_stop(&cp->timer);
+ if (cp->refcount == 0) {
+ if (cp->garbage == 0) {
+ cp->garbage = 1;
+ uv_timer_stop(&cp->timer);
+ }
+ } else {
+ /*
+ * Context timed out but still referenced, must wait
+ * until the caller releases its reference (shortly)
+ * before beginning garbage collection process.
+ */
+ cp->inactive = 1;
}
}
@@ -298,20 +307,28 @@ webgroup_garbage_collect(struct webgroup
dictIterator *iterator;
dictEntry *entry;
context_t *cp;
- unsigned int count = 0, drops = 0;
+ unsigned int count = 0, drops = 0, garbageset = 0, inactiveset = 0;
if (pmDebugOptions.http || pmDebugOptions.libweb)
- fprintf(stderr, "%s: started\n", "webgroup_garbage_collect");
+ fprintf(stderr, "%s: started for groups %p\n",
+ "webgroup_garbage_collect", groups);
/* do context GC if we get the lock (else don't block here) */
if (uv_mutex_trylock(&groups->mutex) == 0) {
iterator = dictGetSafeIterator(groups->contexts);
for (entry = dictNext(iterator); entry;) {
cp = (context_t *)dictGetVal(entry);
+ if (cp->privdata != groups)
+ continue;
entry = dictNext(iterator);
- if (cp->garbage && cp->privdata == groups) {
+ if (cp->garbage)
+ garbageset++;
+ if (cp->inactive && cp->refcount == 0)
+ inactiveset++;
+ if (cp->garbage || (cp->inactive && cp->refcount == 0)) {
if (pmDebugOptions.http || pmDebugOptions.libweb)
- fprintf(stderr, "GC context %u (%p)\n", cp->randomid, cp);
+ fprintf(stderr, "GC dropping context %u (%p)\n",
+ cp->randomid, cp);
uv_mutex_unlock(&groups->mutex);
webgroup_drop_context(cp, groups);
uv_mutex_lock(&groups->mutex);
@@ -324,7 +341,8 @@ webgroup_garbage_collect(struct webgroup
/* if dropping the last remaining context, do cleanup */
if (groups->active && drops == count) {
if (pmDebugOptions.http || pmDebugOptions.libweb)
- fprintf(stderr, "%s: freezing\n", "webgroup_garbage_collect");
+ fprintf(stderr, "%s: freezing groups %p\n",
+ "webgroup_garbage_collect", groups);
webgroup_timers_stop(groups);
}
uv_mutex_unlock(&groups->mutex);
@@ -334,8 +352,10 @@ webgroup_garbage_collect(struct webgroup
mmv_set(groups->map, groups->metrics[WEBGROUP_GC_COUNT], &count);
if (pmDebugOptions.http || pmDebugOptions.libweb)
- fprintf(stderr, "%s: finished [%u drops from %u entries]\n",
- "webgroup_garbage_collect", drops, count);
+ fprintf(stderr, "%s: finished [%u drops from %u entries,"
+ " %u garbageset, %u inactiveset]\n",
+ "webgroup_garbage_collect", drops, count,
+ garbageset, inactiveset);
}
static void
@@ -354,7 +374,7 @@ webgroup_use_context(struct context *cp,
int sts;
struct webgroups *gp = (struct webgroups *)cp->privdata;
- if (cp->garbage == 0) {
+ if (cp->garbage == 0 && cp->inactive == 0) {
if (cp->setup == 0) {
if ((sts = pmReconnectContext(cp->context)) < 0) {
infofmt(*message, "cannot reconnect context: %s",
@@ -424,7 +444,7 @@ webgroup_lookup_context(pmWebGroupSettin
*status = -ENOTCONN;
return NULL;
}
- if (cp->garbage == 0) {
+ if (cp->garbage == 0 && cp->inactive == 0) {
access.username = cp->username;
access.password = cp->password;
access.realm = cp->realm;
diff -Naurp pcp-5.3.7.orig/src/pmcd/src/client.c pcp-5.3.7/src/pmcd/src/client.c
--- pcp-5.3.7.orig/src/pmcd/src/client.c 2018-01-15 15:49:13.000000000 +1100
+++ pcp-5.3.7/src/pmcd/src/client.c 2024-09-09 13:47:06.870083366 +1000
@@ -74,7 +74,8 @@ NotifyEndContext(int ctx)
ClientInfo *
AcceptNewClient(int reqfd)
{
- static unsigned int seq = 0;
+ static unsigned int seq, saved, count;
+ static struct timeval then;
int i, fd;
__pmSockLen addrlen;
struct timeval now;
@@ -83,21 +84,30 @@ AcceptNewClient(int reqfd)
addrlen = __pmSockAddrSize();
fd = __pmAccept(reqfd, client[i].addr, &addrlen);
if (fd == -1) {
- if (neterror() == EPERM) {
- pmNotifyErr(LOG_NOTICE, "AcceptNewClient(%d): "
- "Permission Denied\n", reqfd);
- }
- else if (neterror() == ECONNABORTED) {
+ if (neterror() == ECONNABORTED) {
/* quietly ignore this one ... */
;
}
else {
- /*
- * unexpected ... ignore the client (we used to kill off pmcd
- * but that seems way too extreme)
+ /* Permission denied or an unexpected error (e.g. EMFILE)
+ * - rate limit the logging and make this client go away.
*/
- pmNotifyErr(LOG_ERR, "AcceptNewClient(%d): Unexpected error from __pmAccept: %d: %s\n",
- reqfd, neterror(), netstrerror());
+ pmtimevalNow(&now);
+ if (neterror() != saved || now.tv_sec > then.tv_sec + 60) {
+ if (neterror() == EPERM)
+ pmNotifyErr(LOG_NOTICE, "AcceptNewClient(%d): "
+ "Permission Denied (%d suppressed)\n",
+ reqfd, count);
+ else
+ pmNotifyErr(LOG_ERR, "AcceptNewClient(%d): "
+ "Accept error (%d suppressed): %d: %s\n",
+ reqfd, count, neterror(), netstrerror());
+ saved = neterror();
+ count = 0;
+ } else {
+ count++;
+ }
+ then = now;
}
client[i].fd = -1;
DeleteClient(&client[i]);
diff -Naurp pcp-5.3.7.orig/src/pmcd/src/pmcd.c pcp-5.3.7/src/pmcd/src/pmcd.c
--- pcp-5.3.7.orig/src/pmcd/src/pmcd.c 2021-10-13 10:48:23.000000000 +1100
+++ pcp-5.3.7/src/pmcd/src/pmcd.c 2024-09-09 13:47:06.871083368 +1000
@@ -685,7 +685,7 @@ HandleReadyAgents(__pmFdSet *readyFds)
}
static void
-CheckNewClient(__pmFdSet * fdset, int rfd, int family)
+CheckNewClient(__pmFdSet *fdset, int rfd, int family)
{
int s, sts, accepted = 1;
__uint32_t challenge;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,418 @@
diff -Naurp pcp-6.2.0.orig/qa/1518 pcp-6.2.0/qa/1518
--- pcp-6.2.0.orig/qa/1518 1970-01-01 10:00:00.000000000 +1000
+++ pcp-6.2.0/qa/1518 2024-09-17 10:11:45.805874610 +1000
@@ -0,0 +1,75 @@
+#!/bin/sh
+# PCP QA Test No. 1518
+# SUSE Issue A)
+# __pmDecodeValueSet() Miscalculates Available Buffer Space
+# Leading to a Possible Heap Corruption
+#
+# Copyright (c) 2024 Ken McDonell. All Rights Reserved.
+# Copyright (c) 2024 Matthias Gerstner. All Rights Reserved.
+#
+
+if [ $# -eq 0 ]
+then
+ seq=`basename $0`
+ echo "QA output created by $seq"
+else
+ # use $seq from caller, unless not set
+ [ -n "$seq" ] || seq=`basename $0`
+ echo "QA output created by `basename $0` $*"
+fi
+
+# get standard environment, filters and checks
+. ./common.product
+. ./common.filter
+. ./common.check
+
+$sudo rm -rf $tmp $tmp.* $seq.full
+
+which nc >/dev/null 2>&1 || _notrun "no nc executable installed"
+_check_valgrind
+
+_cleanup()
+{
+ cat pmcd.log >>$here/$seq.full
+ cd $here
+ $sudo rm -rf $tmp $tmp.*
+}
+
+status=0 # success is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_filter()
+{
+ sed \
+ -e '/^Command: /d' \
+ # end
+}
+
+mkdir $tmp || exit 1
+cd $tmp
+grep sampledso $PCP_PMCDCONF_PATH >pmcd.conf
+cat pmcd.conf >>$here/$seq.full
+port=`_find_free_port`
+echo "port=$port" >>$here/$seq.full
+
+# real QA test starts here
+valgrind $PCP_BINADM_DIR/pmcd -f -Dpdu -c ./pmcd.conf -s ./pmcd.socket -p $port >out 2>err &
+valgrind_pid=$!
+sleep 2
+pmcd_pid=`$PCP_PS_PROG $PCP_PS_ALL_FLAGS | grep '[p]mcd -f -Dpdu' | $PCP_AWK_PROG '{ print $2 }'`
+echo "pmcd_pid=$pmcd_pid" >>$here/$seq.full
+nc -N -U ./pmcd.socket <$here/binary/decode-value-set-out-of-bound-write 2>&1 \
+| od -c >>$here/$seq.full
+sleep 2
+kill -TERM $pmcd_pid
+wait
+
+echo "expect error to be logged ..."
+grep __pmDecodeValueSet pmcd.log
+
+echo
+echo "and no valgrind badness ..."
+cat out err | _filter_valgrind | _filter
+
+# success, all done
+exit
diff -Naurp pcp-6.2.0.orig/qa/1518.out pcp-6.2.0/qa/1518.out
--- pcp-6.2.0.orig/qa/1518.out 1970-01-01 10:00:00.000000000 +1000
+++ pcp-6.2.0/qa/1518.out 2024-09-17 10:11:45.806874611 +1000
@@ -0,0 +1,11 @@
+QA output created by 1518
+expect error to be logged ...
+__pmDecodeValueSet: PM_ERR_IPC: pmid[0] value[0] vindex=1020 (max=255)
+
+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
+Rerun with --leak-check=full to see details of leaked memory
+ERROR SUMMARY: 0 errors from 0 contexts ...
diff -Naurp pcp-6.2.0.orig/qa/group pcp-6.2.0/qa/group
--- pcp-6.2.0.orig/qa/group 2024-02-11 23:48:09.000000000 +1100
+++ pcp-6.2.0/qa/group 2024-09-17 10:11:45.815874621 +1000
@@ -1951,6 +1951,7 @@ x11
1503 pcp pidstat ps python local
1511 pmcd local pmda.sample
1515 pmda.denki local valgrind
+1518 pmcd libpcp local
1530 pmda.zfs local valgrind
1531 pmda.zfs local valgrind
1532 pmda.zfs local
diff -Naurp pcp-6.2.0.orig/src/libpcp/src/endian.c pcp-6.2.0/src/libpcp/src/endian.c
--- pcp-6.2.0.orig/src/libpcp/src/endian.c 2023-11-16 17:51:39.000000000 +1100
+++ pcp-6.2.0/src/libpcp/src/endian.c 2024-09-17 10:11:45.820874627 +1000
@@ -275,13 +275,17 @@ ntohEventArray(pmValueBlock * const vb,
}
void
-__ntohpmValueBlock(pmValueBlock * const vb)
+__ntohpmValueBlock_hdr(pmValueBlock * const vb)
{
unsigned int *ip = (unsigned int *)vb;
/* Swab the first word, which contain vtype and vlen */
*ip = ntohl(*ip);
+}
+void
+__ntohpmValueBlock_buf(pmValueBlock * const vb)
+{
switch (vb->vtype) {
case PM_TYPE_U64:
case PM_TYPE_64:
@@ -305,6 +309,13 @@ __ntohpmValueBlock(pmValueBlock * const
break;
}
}
+
+void
+__ntohpmValueBlock(pmValueBlock * const vb)
+{
+ __ntohpmValueBlock_hdr(vb);
+ __ntohpmValueBlock_buf(vb);
+}
#endif
#ifndef __htonpmPDUInfo
diff -Naurp pcp-6.2.0.orig/src/libpcp/src/internal.h pcp-6.2.0/src/libpcp/src/internal.h
--- pcp-6.2.0.orig/src/libpcp/src/internal.h 2023-11-16 17:51:39.000000000 +1100
+++ pcp-6.2.0/src/libpcp/src/internal.h 2024-09-17 10:11:45.823874630 +1000
@@ -60,6 +60,8 @@ extern int __pmGetDate(struct timespec *
#define __ntohpmLabel(a) /* noop */
#define __htonpmValueBlock(a) /* noop */
#define __ntohpmValueBlock(a) /* noop */
+#define __ntohpmValueBlock_hdr(a) /* noop */
+#define __ntohpmValueBlock_buf(a) /* noop */
#define __htonf(a) /* noop */
#define __ntohf(a) /* noop */
#define __htond(a) /* noop */
@@ -90,6 +92,8 @@ extern void __htonpmLabel(pmLabel * cons
extern void __ntohpmLabel(pmLabel * const) _PCP_HIDDEN;
extern void __htonpmValueBlock(pmValueBlock * const) _PCP_HIDDEN;
extern void __ntohpmValueBlock(pmValueBlock * const) _PCP_HIDDEN;
+extern void __ntohpmValueBlock_hdr(pmValueBlock * const) _PCP_HIDDEN;
+extern void __ntohpmValueBlock_buf(pmValueBlock * const) _PCP_HIDDEN;
extern void __htonf(char *) _PCP_HIDDEN; /* float */
#define __ntohf(v) __htonf(v)
#define __htond(v) __htonll(v) /* double */
diff -Naurp pcp-6.2.0.orig/src/libpcp/src/p_result.c pcp-6.2.0/src/libpcp/src/p_result.c
--- pcp-6.2.0.orig/src/libpcp/src/p_result.c 2023-11-16 17:51:39.000000000 +1100
+++ pcp-6.2.0/src/libpcp/src/p_result.c 2024-09-17 10:18:17.264314112 +1000
@@ -323,6 +323,135 @@ __pmSendHighResResult(int fd, int from,
return __pmSendHighResResult_ctx(NULL, fd, from, result);
}
+/* Check that a network encoded event array is within a given buffer size */
+int
+__pmEventArrayCheck(pmValueBlock * const vb, int highres, int pmid, int value, size_t check)
+{
+ char *base;
+ int r; /* records */
+ int p; /* parameters in a record ... */
+ int nrecords;
+ int nparams;
+
+ if (highres) {
+ pmHighResEventArray *hreap = (pmHighResEventArray *)vb;
+ base = (char *)&hreap->ea_record[0];
+ if (base > (char *)vb + check) {
+ if (pmDebugOptions.pdu)
+ fprintf(stderr, "__pmEventArrayCheck #1: PM_ERR_IPC: pmid[%d] value[%d] highres event records past end of PDU buffer\n",
+ pmid, value);
+ return PM_ERR_IPC;
+ }
+ nrecords = ntohl(hreap->ea_nrecords);
+ }
+ else {
+ pmEventArray *eap = (pmEventArray *)vb;
+ base = (char *)&eap->ea_record[0];
+ if (base > (char *)vb + check) {
+ if (pmDebugOptions.pdu)
+ fprintf(stderr, "__pmEventArrayCheck #2: PM_ERR_IPC: pmid[%d] value[%d] event records past end of PDU buffer\n",
+ pmid, value);
+ return PM_ERR_IPC;
+ }
+ nrecords = ntohl(eap->ea_nrecords);
+ }
+
+ /* walk packed event record array */
+ for (r = 0; r < nrecords; r++) {
+ unsigned int flags, type;
+ size_t size, remaining;
+
+ remaining = check - (base - (char *)vb);
+ if (highres) {
+ pmHighResEventRecord *hrerp = (pmHighResEventRecord *)base;
+ size = sizeof(hrerp->er_timestamp) + sizeof(hrerp->er_flags) +
+ sizeof(hrerp->er_nparams);
+ if (size > remaining) {
+ if (pmDebugOptions.pdu)
+ fprintf(stderr, "__pmEventArrayCheck #3: PM_ERR_IPC: pmid[%d] value[%d] record[%d] highres event record past end of PDU buffer\n",
+ pmid, value, r);
+ return PM_ERR_IPC;
+ }
+ nparams = ntohl(hrerp->er_nparams);
+ flags = ntohl(hrerp->er_flags);
+ }
+ else {
+ pmEventRecord *erp = (pmEventRecord *)base;
+ size = sizeof(erp->er_timestamp) + sizeof(erp->er_flags) +
+ sizeof(erp->er_nparams);
+ if (size > remaining) {
+ if (pmDebugOptions.pdu)
+ fprintf(stderr, "__pmEventArrayCheck #4: PM_ERR_IPC: pmid[%d] value[%d] record[%d] event record past end of PDU buffer\n",
+ pmid, value, r);
+ return PM_ERR_IPC;
+ }
+ nparams = ntohl(erp->er_nparams);
+ flags = ntohl(erp->er_flags);
+ }
+
+ if (flags & PM_EVENT_FLAG_MISSED)
+ nparams = 0;
+
+ base += size;
+ remaining = check - (base - (char *)vb);
+
+ for (p = 0; p < nparams; p++) {
+ __uint32_t *tp; /* points to int holding vtype/vlen */
+ pmEventParameter *epp = (pmEventParameter *)base;
+
+ if (sizeof(pmEventParameter) > remaining) {
+ if (pmDebugOptions.pdu)
+ fprintf(stderr, "__pmEventArrayCheck #5: PM_ERR_IPC: pmid[%d] value[%d] record[%d] param[%d] event record past end of PDU buffer\n",
+ pmid, value, r, p);
+ return PM_ERR_IPC;
+ }
+
+ tp = (__uint32_t *)&epp->ep_pmid;
+ tp++; /* now points to ep_type/ep_len */
+ *tp = ntohl(*tp);
+ type = epp->ep_type;
+ size = epp->ep_len;
+ *tp = htonl(*tp); /* leave the buffer how we found it */
+
+ if (sizeof(pmID) + size > remaining) {
+ if (pmDebugOptions.pdu)
+ fprintf(stderr, "__pmEventArrayCheck #6: PM_ERR_IPC: pmid[%d] value[%d] record[%d] param[%d] event record past end of PDU buffer\n",
+ pmid, value, r, p);
+ return PM_ERR_IPC;
+ }
+
+ base += sizeof(pmID) + PM_PDU_SIZE_BYTES(size);
+
+ /*
+ * final check for the types below, ep_len should be 4 or
+ * 8, but a malformed PDU could have smaller ep_len values
+ * and then unpacking these types risk going past the end
+ * of the PDU buffer
+ */
+ size = 0;
+ switch (type) {
+ case PM_TYPE_32:
+ case PM_TYPE_U32:
+ case PM_TYPE_FLOAT:
+ size = 4; /* 32-bit types */
+ break;
+ case PM_TYPE_64:
+ case PM_TYPE_U64:
+ case PM_TYPE_DOUBLE:
+ size = 8; /* 64-bit types */
+ break;
+ }
+ if (size > 0 && sizeof(pmID) + size > remaining) {
+ if (pmDebugOptions.pdu)
+ fprintf(stderr, "__pmEventArrayCheck #7: PM_ERR_IPC: pmid[%d] value[%d] record[%d] param[%d] event record past end of PDU buffer\n",
+ pmid, value, r, p);
+ return PM_ERR_IPC;
+ }
+ }
+ }
+ return 0;
+}
+
#if defined(HAVE_64BIT_PTR)
int
__pmDecodeValueSet(__pmPDU *pdubuf, int pdulen, __pmPDU *data, char *pduend,
@@ -336,7 +465,7 @@ __pmDecodeValueSet(__pmPDU *pdubuf, int
int i, j;
/*
* Note: all sizes are in units of bytes ... beware that 'data' is in
- * units of __pmPDU
+ * units of __pmPDU (four bytes)
*/
int vsize; /* size of vlist_t's in PDU buffer */
int nvsize; /* size of pmValue's after decode */
@@ -414,11 +543,10 @@ __pmDecodeValueSet(__pmPDU *pdubuf, int
return PM_ERR_IPC;
}
vindex = ntohl(pduvp->value.lval);
- if (vindex < 0 || vindex > pdulen) {
+ if (vindex < 0 || (char *)&pdubuf[vindex] >= pduend) {
if (pmDebugOptions.pdu && pmDebugOptions.desperate)
- fprintf(stderr, "%s: Bad: pmid[%d] value[%d] "
- "vindex=%d\n",
- "__pmDecodeValueSet", i, j, vindex);
+ fprintf(stderr, "__pmDecodeValueSet: PM_ERR_IPC: pmid[%d] value[%d] vindex=%d (max=%ld)\n",
+ i, j, vindex, (long)((pduend-(char *)pdubuf) / sizeof(pdubuf[0])-1));
return PM_ERR_IPC;
}
pduvbp = (pmValueBlock *)&pdubuf[vindex];
@@ -427,29 +555,31 @@ __pmDecodeValueSet(__pmPDU *pdubuf, int
check = (size_t)(pduend - (char *)pduvbp);
if (sizeof(unsigned int) > check) {
if (pmDebugOptions.pdu && pmDebugOptions.desperate)
- fprintf(stderr, "%s: Bad: pmid[%d] value[%d] "
- "second pduvp past end of "
- "PDU buffer\n",
- "__pmDecodeValueSet", i, j);
+ fprintf(stderr, "__pmDecodeValueSet: PM_ERR_IPC: pmid[%d] value[%d] second pduvp past end of PDU buffer\n",
+ i, j);
return PM_ERR_IPC;
}
-
- __ntohpmValueBlock(pduvbp);
+ __ntohpmValueBlock_hdr(pduvbp);
if (pduvbp->vlen < PM_VAL_HDR_SIZE ||
pduvbp->vlen > pdulen) {
- if (pmDebugOptions.pdu && pmDebugOptions.desperate)
- fprintf(stderr, "%s: Bad: pmid[%d] value[%d] "
- "vlen=%d\n", "__pmDecodeValueSet",
- i, j, pduvbp->vlen);
+ if (pmDebugOptions.pdu)
+ fprintf(stderr, "__pmDecodeValueSet: PM_ERR_IPC: pmid[%d] value[%d] vlen=%d\n",
+ i, j, pduvbp->vlen);
return PM_ERR_IPC;
}
- if (pduvbp->vlen > (size_t)(pduend - (char *)pduvbp)) {
- if (pmDebugOptions.pdu && pmDebugOptions.desperate)
- fprintf(stderr, "%s: Bad: pmid[%d] value[%d] "
- "pduvp past end of PDU buffer\n",
- "__pmDecodeValueSet", i, j);
+ if (pduvbp->vlen > check) {
+ if (pmDebugOptions.pdu)
+ fprintf(stderr, "__pmDecodeValueSet: PM_ERR_IPC: pmid[%d] value[%d] pduvp past end of PDU buffer\n",
+ i, j);
return PM_ERR_IPC;
}
+ if (pduvbp->vtype == PM_TYPE_HIGHRES_EVENT ||
+ pduvbp->vtype == PM_TYPE_EVENT) {
+ vindex = (pduvbp->vtype == PM_TYPE_HIGHRES_EVENT);
+ if (__pmEventArrayCheck(pduvbp, vindex, i, j, check) < 0)
+ return PM_ERR_IPC;
+ }
+ __ntohpmValueBlock_buf(pduvbp);
vbsize += PM_PDU_SIZE_BYTES(pduvbp->vlen);
if (pmDebugOptions.pdu && pmDebugOptions.desperate) {
fprintf(stderr, " len: %d type: %d",
@@ -682,11 +812,10 @@ __pmDecodeValueSet(__pmPDU *pdubuf, int
} else {
/* salvage pmValueBlocks from end of PDU */
vindex = ntohl(pduvp->value.lval);
- if (vindex < 0 || vindex > pdulen) {
+ if (vindex < 0 || (char *)&pdubuf[vindex] >= pduend) {
if (pmDebugOptions.pdu && pmDebugOptions.desperate)
- fprintf(stderr, "%s: Bad: pmid[%d] value[%d] "
- "vindex=%d\n",
- "__pmDecodeValueSet", i, j, vindex);
+ fprintf(stderr, "__pmDecodeValueSet: PM_ERR_IPC: pmid[%d] value[%d] vindex=%d (max=%ld)\n",
+ i, j, vindex, (long)((pduend-(char *)pdubuf) / sizeof(pdubuf[0])-1));
return PM_ERR_IPC;
}
pduvbp = (pmValueBlock *)&pdubuf[vindex];
@@ -701,7 +830,7 @@ __pmDecodeValueSet(__pmPDU *pdubuf, int
"__pmDecodeValueSet", i, j);
return PM_ERR_IPC;
}
- __ntohpmValueBlock(pduvbp);
+ __ntohpmValueBlock_hdr(pduvbp);
if (pduvbp->vlen < PM_VAL_HDR_SIZE ||
pduvbp->vlen > pdulen) {
if (pmDebugOptions.pdu && pmDebugOptions.desperate)
@@ -710,13 +839,20 @@ __pmDecodeValueSet(__pmPDU *pdubuf, int
i, j, pduvbp->vlen);
return PM_ERR_IPC;
}
- if (pduvbp->vlen > (size_t)(pduend - (char *)pduvbp)) {
+ if (pduvbp->vlen > check) {
if (pmDebugOptions.pdu && pmDebugOptions.desperate)
fprintf(stderr, "%s: Bad: pmid[%d] value[%d] "
"pduvp past end of PDU buffer\n",
"__pmDecodeValueSet", i, j);
return PM_ERR_IPC;
}
+ if (pduvbp->vtype == PM_TYPE_HIGHRES_EVENT ||
+ pduvbp->vtype == PM_TYPE_EVENT) {
+ vindex = (pduvbp->vtype == PM_TYPE_HIGHRES_EVENT);
+ if (__pmEventArrayCheck(pduvbp, vindex, i, j, check) < 0)
+ return PM_ERR_IPC;
+ }
+ __ntohpmValueBlock_buf(pduvbp);
pduvp->value.pval = pduvbp;
}
}

@ -0,0 +1,99 @@
diff -Naurp pcp-5.3.7.orig/qa/640 pcp-5.3.7/qa/640
--- pcp-5.3.7.orig/qa/640 2017-08-17 10:54:57.000000000 +1000
+++ pcp-5.3.7/qa/640 2024-09-09 13:41:12.440235947 +1000
@@ -6,6 +6,10 @@
# years; so we now simply check the right permissions are in place
# and move right along...
#
+# Aug 2024 update
+# SuSE Issue G identifies another possible exploit, so try that
+# as well.
+#
# Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved.
#
@@ -17,13 +21,54 @@ echo "QA output created by $seq"
. ./common.filter
. ./common.check
-status=0 # success is the default!
-trap "$sudo rm -f $tmp.*; exit \$status" 0 1 2 3 15
+rm -f $seq.full
+ls -li $PCP_LOG_DIR/NOTICES $tmp/badness >>$seq.full 2>&1
+
+_cleanup()
+{
+ if [ -f $PCP_LOG_DIR/NOTICES.$seq ]
+ then
+ $sudo rm -f $PCP_LOG_DIR/NOTICES
+ $sudo mv $PCP_LOG_DIR/NOTICES.$seq $PCP_LOG_DIR/NOTICES
+ fi
+ ls -li $PCP_LOG_DIR/NOTICES $tmp/badness >>$seq.full 2>&1
+ $sudo rm -rf $tmp $tmp.*
+}
+
+status=1 # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_filter()
+{
+ sed \
+ -e "s@$PCP_LOG_DIR@PCP_LOG_DIR@g" \
+ -e '/^pmpost:/s/\[.*]/[DATE]/' \
+ # end
+}
# real QA test starts here
pmpost=$PCP_BINADM_DIR/pmpost
-echo "Using pmpost binary: $pmpost" > $seq.full
+echo "Using pmpost binary: $pmpost" >>$seq.full
test -u "$pmpost" && echo "FAIL: pmpost has setuid bit set"
test -g "$pmpost" && echo "FAIL: pmpost has setgid bit set"
+
+$sudo mkdir $tmp || exit
+$sudo chmod 700 $tmp || exit
+$sudo -u $PCP_USER mv $PCP_LOG_DIR/NOTICES $PCP_LOG_DIR/NOTICES.$seq
+
+$sudo -u $PCP_USER ln -s $tmp/badness $PCP_LOG_DIR/NOTICES >>$seq.full
+$pmpost ordinary user 2>&1 | _filter
+$sudo ls -li $PCP_LOG_DIR/NOTICES $tmp/badness >>$seq.full 2>&1
+$sudo -u pcp $pmpost pcp user 2>&1 | _filter
+$sudo ls -li $PCP_LOG_DIR/NOTICES $tmp/badness >>$seq.full 2>&1
+$sudo $pmpost root user 2>&1 | _filter
+$sudo ls -li $PCP_LOG_DIR/NOTICES $tmp/badness >>$seq.full 2>&1
+if $sudo test -f $tmp/badness
+then
+ $sudo cat $tmp/badness
+fi
+
echo "Test complete"
+
+status=0
exit
diff -Naurp pcp-5.3.7.orig/qa/640.out pcp-5.3.7/qa/640.out
--- pcp-5.3.7.orig/qa/640.out 2017-08-17 10:54:57.000000000 +1000
+++ pcp-5.3.7/qa/640.out 2024-09-09 13:41:12.440235947 +1000
@@ -1,2 +1,5 @@
QA output created by 640
+pmpost: unposted message: [DATE] ordinary user
+pmpost: unposted message: [DATE] pcp user
+pmpost: unposted message: [DATE] root user
Test complete
diff -Naurp pcp-5.3.7.orig/src/pmpost/pmpost.c pcp-5.3.7/src/pmpost/pmpost.c
--- pcp-5.3.7.orig/src/pmpost/pmpost.c 2021-02-17 15:27:41.000000000 +1100
+++ pcp-5.3.7/src/pmpost/pmpost.c 2024-09-09 13:41:12.440235947 +1000
@@ -141,8 +141,12 @@ main(int argc, char **argv)
goto oops;
}
- if ((fd = open(notices, O_WRONLY|O_APPEND, 0)) < 0) {
- if ((fd = open(notices, O_WRONLY|O_CREAT|O_APPEND, 0664)) < 0) {
+ if ((fd = open(notices, O_WRONLY|O_APPEND|O_NOFOLLOW, 0)) < 0) {
+ if (oserror() == ELOOP) {
+ /* last component is symlink => attack? ... bail! */
+ goto oops;
+ }
+ if ((fd = open(notices, O_WRONLY|O_CREAT|O_APPEND|O_NOFOLLOW, 0664)) < 0) {
fprintf(stderr, "pmpost: cannot open or create file \"%s\": %s\n",
notices, osstrerror());
goto oops;

@ -1,6 +1,6 @@
Name: pcp
Version: 6.2.0
Release: 1%{?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
@ -8,6 +8,11 @@ URL: https://pcp.io
Source0: https://github.com/performancecopilot/pcp/releases/pcp-%{version}.src.tar.gz
Patch1: redhat-issues-RHEL-2317-default-archive-version.patch
Patch2: redhat-issues-RHEL-30719-pmproxy-resp-proxy-disabled.patch
Patch3: redhat-issues-RHEL-57796-pmcd-pmstore-corruption.patch
Patch4: redhat-issues-RHEL-57799-pmpost-symlink-handling.patch
Patch5: redhat-issues-RHEL-34586-pmproxy-pmcd-fd-leak.patch
Patch6: redhat-issues-RHEL-57788-pmdahacluster-update.patch
%if 0%{?fedora} >= 40 || 0%{?rhel} >= 10
ExcludeArch: %{ix86}
@ -3492,6 +3497,18 @@ fi
%files zeroconf -f pcp-zeroconf-files.rpm
%changelog
* Tue Sep 17 2024 Nathan Scott <nathans@redhat.com> - 6.2.0-5
- Fix buffer sizing checks in pmstore PDU handling (RHEL-57805)
- Guard against symlink attacks in pmpost program (RHEL-57810)
- Fix libpcp_web webgroup slow request refcounting (RHEL-58306)
- Updated pmdahacluster for newer crm_mon versions (RHEL-50693)
* Thu Aug 08 2024 Nathan Scott <nathans@redhat.com> - 6.2.0-3
- Update hacluster PMDA for pacemaker 2.1.6 crm_mon (RHEL-50693)
* Wed Apr 17 2024 Nathan Scott <nathans@redhat.com> - 6.2.0-2
- Disable RESP proxying by default in pmproxy (RHEL-30719)
* Mon Feb 12 2024 Nathan Scott <nathans@redhat.com> - 6.2.0-1
- Rebase to latest stable version of PCP (RHEL-2317)
@ -3532,9 +3549,6 @@ fi
- Add support for pmieconf webhook action (BZ 2185803)
- Rebase to latest stable version of PCP (BZ 2175602)
* Fri Apr 14 2023 MSVSphere Packaging Team <packager@msvsphere.ru> - 6.0.1-4
- Rebuilt for MSVSphere 9.2 beta
* Tue Jan 17 2023 Nathan Scott <nathans@redhat.com> - 6.0.1-4
- Rebuild for dependency on new version of libbpf (BZ 2159276)

Loading…
Cancel
Save