From ccd1bb1679 Mon Sep 17 00:00:00 2001 From: Nathan Scott Subject: [PATCH] libpcp: fix OOB read in __pmLogLoadLabelSet (CWE-125) __pmLogLoadLabelSet() reads timestamp, type, ident, and nsets fields from tbuf at sequential offsets without checking that rlen is large enough to contain them. When the pmproxy logger servlet delivers a TYPE_LABEL record with hdr.len=13 (minimum accepted by the dispatcher), rlen=1 and the function reads 20-24 bytes from a 1-byte buffer. Fix: add minimum-length guard at the top of __pmLogLoadLabelSet() using LABELSET_V3_MINRLEN / LABELSET_V2_MINRLEN macros derived from the on-disk __pmExtLabelSet_v3/v2 struct sizes (minus the len+type header that rlen excludes). Test coverage will be added in a consolidated pducrash.c extension covering vulns 9-13. 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) --- diff --git a/src/libpcp/src/e_labels.c b/src/libpcp/src/e_labels.c index 342cb9163d..0e18c72aed 100644 --- a/src/libpcp/src/e_labels.c +++ b/src/libpcp/src/e_labels.c @@ -56,6 +56,10 @@ typedef struct { /* will be expanded if nsets > 0 */ } __pmExtLabelSet_v2; +/* Minimum rlen (record body without len+type header) to read fixed fields */ +#define LABELSET_V3_MINRLEN (sizeof(__pmExtLabelSet_v3) - 2 * sizeof(__int32_t)) +#define LABELSET_V2_MINRLEN (sizeof(__pmExtLabelSet_v2) - 2 * sizeof(__int32_t)) + /* * pack a set of labels into a physical metadata record * - lcp required to provide archive version @@ -210,6 +214,23 @@ __pmLogLoadLabelSet(char *tbuf, int rlen, int rtype, __pmTimestamp *stamp, *nsetsp = 0; *labelsetsp = NULL; + if (rtype == TYPE_LABEL_V2) { + if (rlen < (int)LABELSET_V2_MINRLEN) { + if (pmDebugOptions.logmeta) + fprintf(stderr, "%s: v2 rlen=%d too small (min=%d)\n", + __FUNCTION__, rlen, (int)LABELSET_V2_MINRLEN); + return PM_ERR_LOGREC; + } + } + else { + if (rlen < (int)LABELSET_V3_MINRLEN) { + if (pmDebugOptions.logmeta) + fprintf(stderr, "%s: v3 rlen=%d too small (min=%d)\n", + __FUNCTION__, rlen, (int)LABELSET_V3_MINRLEN); + return PM_ERR_LOGREC; + } + } + k = 0; if (rtype == TYPE_LABEL_V2) { __pmLoadTimeval((__int32_t *)&tbuf[k], stamp); diff --git a/src/libpcp3/src/e_labels.c b/src/libpcp3/src/e_labels.c index 8e94a112b0..169817266b 100644 --- a/src/libpcp3/src/e_labels.c +++ b/src/libpcp3/src/e_labels.c @@ -56,6 +56,10 @@ typedef struct { /* will be expanded if nsets > 0 */ } __pmExtLabelSet_v2; +/* Minimum rlen (record body without len+type header) to read fixed fields */ +#define LABELSET_V3_MINRLEN (sizeof(__pmExtLabelSet_v3) - 2 * sizeof(__int32_t)) +#define LABELSET_V2_MINRLEN (sizeof(__pmExtLabelSet_v2) - 2 * sizeof(__int32_t)) + /* * pack a set of labels into a physical metadata record * - lcp required to provide archive version @@ -215,6 +219,23 @@ __pmLogLoadLabelSet(char *tbuf, int rlen, int rtype, __pmTimestamp *stamp, *nsetsp = 0; *labelsetsp = NULL; + if (rtype == TYPE_LABEL_V2) { + if (rlen < (int)LABELSET_V2_MINRLEN) { + if (pmDebugOptions.logmeta) + fprintf(stderr, "%s: v2 rlen=%d too small (min=%d)\n", + __FUNCTION__, rlen, (int)LABELSET_V2_MINRLEN); + return PM_ERR_LOGREC; + } + } + else { + if (rlen < (int)LABELSET_V3_MINRLEN) { + if (pmDebugOptions.logmeta) + fprintf(stderr, "%s: v3 rlen=%d too small (min=%d)\n", + __FUNCTION__, rlen, (int)LABELSET_V3_MINRLEN); + return PM_ERR_LOGREC; + } + } + k = 0; if (rtype == TYPE_LABEL_V2) { __pmLoadTimeval((__int32_t *)&tbuf[k], stamp);