Import OL pcp-6.2.0-5.0.1.el9_4.src.rpm

This commit is contained in:
eabdullin 2024-09-20 14:39:03 +03:00
parent 2cae19baeb
commit 36b34a2103
6 changed files with 2366 additions and 2 deletions

View File

@ -0,0 +1,108 @@
From f8dc87ed130a0cfe1077242a4da2d621c4016bfb Mon Sep 17 00:00:00 2001
From: Ken McDonell <kenj@kenj.id.au>
Date: Thu, 4 Apr 2024 11:34:41 +1100
Subject: [PATCH] libpcp: small derived metrics bug fix
This is a partial fix for
https://github.com/performancecopilot/pcp/issues/1921
If the operand has the semantics of PM_SEM_COUNTER and the
value goes backwards from one fetch to the next, then return
no values for rate().
There is QA to come, but this needs a final blessing from Oracle
before the reproducer archive can be included.
cherry-picked:- f8dc87ed130a0cfe1077242a4da2d621c4016bfb
[Orabug: 36538820]
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
---
man/man3/pmregisterderived.3 | 13 +++++++++----
src/libpcp/src/derive_fetch.c | 23 ++++++++++++++++++++++-
2 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/man/man3/pmregisterderived.3 b/man/man3/pmregisterderived.3
index d8719f373..32963c3b3 100644
--- a/man/man3/pmregisterderived.3
+++ b/man/man3/pmregisterderived.3
@@ -485,15 +485,20 @@ metric x with the dimension in the
.B time
domain decreased by one and scaling if required in the time utilization case
where the operand is in units of time, and the derived metric is unitless.
-This mimics the rate conversion applied to counter metrics by tools
+There is one value in the result
+for each instance that appears in both the current and the previous
+sample, except in the case where the metric x has
+the semantics of a counter, i.e. PM_SEM_COUNTER, and
+current value of an instance is smaller than the previous value
+of the same instance then no value is
+returned for this instance (this corresponds to a ``counter wrap'' or a ``counter reset'').
+These rules
+mimic the rate conversion applied to counter metrics by tools
such as
.BR pmval (1),
.BR pmie (1)
and
.BR pmchart (1).
-There is one value in the result
-for each instance that appears in both the current and the previous
-sample.
T}
_
instant(x) T{
diff --git a/src/libpcp/src/derive_fetch.c b/src/libpcp/src/derive_fetch.c
index 62921ece4..ed5f6ebcf 100644
--- a/src/libpcp/src/derive_fetch.c
+++ b/src/libpcp/src/derive_fetch.c
@@ -847,7 +847,13 @@ eval_expr(__pmContext *ctxp, node_t *np, struct timespec *stamp, int numpmid,
}
}
else {
- /* rate() conversion, type will be DOUBLE */
+ /*
+ * rate() conversion, type will be DOUBLE
+ *
+ * For COUNTER metrics, return "no value" if the counter is
+ * NOT monotonic increasing ... this matches what pmval(1)
+ * and pmie(1) do in the same circumstances.
+ */
struct timespec stampdiff;
stampdiff = np->data.info->stamp;
@@ -857,18 +863,33 @@ eval_expr(__pmContext *ctxp, node_t *np, struct timespec *stamp, int numpmid,
np->data.info->ivlist[k].value.d = (double)(np->left->data.info->ivlist[i].value.l - np->left->data.info->last_ivlist[j].value.l);
break;
case PM_TYPE_U32:
+ if (np->left->desc.sem == PM_SEM_COUNTER &&
+ np->left->data.info->ivlist[i].value.ul < np->left->data.info->last_ivlist[j].value.ul)
+ continue;
np->data.info->ivlist[k].value.d = (double)(np->left->data.info->ivlist[i].value.ul - np->left->data.info->last_ivlist[j].value.ul);
break;
case PM_TYPE_64:
+ if (np->left->desc.sem == PM_SEM_COUNTER &&
+ np->left->data.info->ivlist[i].value.ll < np->left->data.info->last_ivlist[j].value.ll)
+ continue;
np->data.info->ivlist[k].value.d = (double)(np->left->data.info->ivlist[i].value.ll - np->left->data.info->last_ivlist[j].value.ll);
break;
case PM_TYPE_U64:
+ if (np->left->desc.sem == PM_SEM_COUNTER &&
+ np->left->data.info->ivlist[i].value.ull < np->left->data.info->last_ivlist[j].value.ull)
+ continue;
np->data.info->ivlist[k].value.d = (double)(np->left->data.info->ivlist[i].value.ull - np->left->data.info->last_ivlist[j].value.ull);
break;
case PM_TYPE_FLOAT:
+ if (np->left->desc.sem == PM_SEM_COUNTER &&
+ np->left->data.info->ivlist[i].value.f < np->left->data.info->last_ivlist[j].value.f)
+ continue;
np->data.info->ivlist[k].value.d = (double)(np->left->data.info->ivlist[i].value.f - np->left->data.info->last_ivlist[j].value.f);
break;
case PM_TYPE_DOUBLE:
+ if (np->left->desc.sem == PM_SEM_COUNTER &&
+ np->left->data.info->ivlist[i].value.d < np->left->data.info->last_ivlist[j].value.d)
+ continue;
np->data.info->ivlist[k].value.d = np->left->data.info->ivlist[i].value.d - np->left->data.info->last_ivlist[j].value.d;
break;
default:
--
2.39.3

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;

View File

@ -1,6 +1,6 @@
Name: pcp
Version: 6.2.0
Release: 3%{?dist}
Release: 5.0.1%{?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
@ -9,7 +9,13 @@ Source0: https://github.com/performancecopilot/pcp/releases/pcp-%{version}.src.t
Patch1: redhat-issues-RHEL-2317-default-archive-version.patch
Patch2: redhat-issues-RHEL-30719-pmproxy-resp-proxy-disabled.patch
Patch3: redhat-issues-RHEL-50693-hacluster-metrics-update.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
#Oracle patches
Patch1001: 1001-libpcp-small-derived-metrics-bug-fix.patch
%if 0%{?fedora} >= 40 || 0%{?rhel} >= 10
@ -3495,6 +3501,15 @@ fi
%files zeroconf -f pcp-zeroconf-files.rpm
%changelog
* Thu Sep 19 2024 EL Errata <el-errata_ww@oracle.com> - 6.2.0-5.0.1
- Fixed libpcp derived metric issue for ol9 [Orabug: 36538820]
* 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)