diff options
Diffstat (limited to 'src/libpcp/src/p_result.c')
-rw-r--r-- | src/libpcp/src/p_result.c | 652 |
1 files changed, 652 insertions, 0 deletions
diff --git a/src/libpcp/src/p_result.c b/src/libpcp/src/p_result.c new file mode 100644 index 0000000..50d4850 --- /dev/null +++ b/src/libpcp/src/p_result.c @@ -0,0 +1,652 @@ +/* + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 1995-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * Thread-safe note + * + * Because __pmFindPDUBuf() returns with a pinned pdu buffer, the + * buffer passed back from __pmEncodeResult() must also remain pinned + * (otherwise another thread could clobber the buffer after returning + * from __pmEncodeResult()) ... it is the caller of __pmEncodeResult() + * who is responsible for (a) not pinning the buffer again, and (b) + * ensuring _someone_ will unpin the buffer when it is safe to do so. + * + * Similarly, __pmDecodeResult() accepts a pinned buffer and returns + * a pmResult that (on 64-bit pointer platforms) may contain pointers + * into a second underlying pinned buffer. The input buffer remains + * pinned, the second buffer will be pinned if it is used. The caller + * will typically call pmFreeResult(), but also needs to call + * __pmUnpinPDUBuf() for the input PDU buffer. When the result contains + * pointers back into the input PDU buffer, this will be pinned _twice_ + * so the pmFreeResult() and __pmUnpinPDUBuf() calls will still be + * required. + */ + +#include <ctype.h> +#include "pmapi.h" +#include "impl.h" +#include "internal.h" + +/* + * PDU for pmResult (PDU_RESULT) + */ + +typedef struct { + pmID pmid; + int numval; /* no. of vlist els to follow, or error */ + int valfmt; /* insitu or pointer */ + __pmValue_PDU vlist[1]; /* zero or more */ +} vlist_t; + +typedef struct { + __pmPDUHdr hdr; + __pmTimeval timestamp; /* when returned */ + int numpmid; /* no. of PMIDs to follow */ + __pmPDU data[1]; /* zero or more */ +} result_t; + +int +__pmEncodeResult(int targetfd, const pmResult *result, __pmPDU **pdubuf) +{ + int i; + int j; + size_t need; /* bytes for the PDU */ + size_t vneed; /* additional bytes for the pmValueBlocks on the end */ + __pmPDU *_pdubuf; + __pmPDU *vbp; + result_t *pp; + vlist_t *vlp; + + need = sizeof(result_t) - sizeof(__pmPDU); + vneed = 0; + /* now add space for each vlist_t (data in result_t) */ + for (i = 0; i < result->numpmid; i++) { + pmValueSet *vsp = result->vset[i]; + /* need space for PMID and count of values (defer valfmt until + * we know numval > 0, which means there should be a valfmt) + */ + need += sizeof(pmID) + sizeof(int); + for (j = 0; j < vsp->numval; j++) { + /* plus value, instance pair */ + need += sizeof(__pmValue_PDU); + if (vsp->valfmt != PM_VAL_INSITU) { + /* plus pmValueBlock */ + vneed += PM_PDU_SIZE_BYTES(vsp->vlist[j].value.pval->vlen); + } + } + if (j) + /* optional value format, if any values present */ + need += sizeof(int); + } + /* + * Need to reserve additonal space for trailer (an int) in case the + * PDU buffer is used by __pmLogPutResult2() + */ + if ((_pdubuf = __pmFindPDUBuf((int)(need+vneed+sizeof(int)))) == NULL) + return -oserror(); + pp = (result_t *)_pdubuf; + pp->hdr.len = (int)(need+vneed); + pp->hdr.type = PDU_RESULT; + pp->timestamp.tv_sec = htonl((__int32_t)(result->timestamp.tv_sec)); + pp->timestamp.tv_usec = htonl((__int32_t)(result->timestamp.tv_usec)); + pp->numpmid = htonl(result->numpmid); + vlp = (vlist_t *)pp->data; + /* + * Note: vbp, and hence offset in sent PDU is in units of __pmPDU + */ + vbp = _pdubuf + need/sizeof(__pmPDU); + for (i = 0; i < result->numpmid; i++) { + pmValueSet *vsp = result->vset[i]; + vlp->pmid = __htonpmID(vsp->pmid); + if (vsp->numval > 0) + vlp->valfmt = htonl(vsp->valfmt); + for (j = 0; j < vsp->numval; j++) { + vlp->vlist[j].inst = htonl(vsp->vlist[j].inst); + if (vsp->valfmt == PM_VAL_INSITU) + vlp->vlist[j].value.lval = htonl(vsp->vlist[j].value.lval); + else { + /* + * pmValueBlocks are harder! + * -- need to copy the len field (len) + len bytes (vbuf) + */ + int nb; + nb = vsp->vlist[j].value.pval->vlen; + memcpy((void *)vbp, (void *)vsp->vlist[j].value.pval, nb); +#ifdef PCP_DEBUG + if ((nb % sizeof(__pmPDU)) != 0) { + /* for Purify */ + int pad; + char *padp = (char *)vbp + nb; + for (pad = sizeof(__pmPDU) - 1; pad >= (nb % sizeof(__pmPDU)); pad--) + *padp++ = '~'; /* buffer end */ + } +#endif + __htonpmValueBlock((pmValueBlock *)vbp); + /* point to the value block at the end of the PDU */ + vlp->vlist[j].value.lval = htonl((int)(vbp - _pdubuf)); + vbp += PM_PDU_SIZE(nb); + } + } + vlp->numval = htonl(vsp->numval); + if (j > 0) + vlp = (vlist_t *)((__psint_t)vlp + sizeof(*vlp) + (j-1)*sizeof(vlp->vlist[0])); + else + vlp = (vlist_t *)((__psint_t)vlp + sizeof(vlp->pmid) + sizeof(vlp->numval)); + } + *pdubuf = _pdubuf; + + /* Note _pdubuf remains pinned ... see thread-safe comments above */ + return 0; +} + +int +__pmSendResult(int fd, int from, const pmResult *result) +{ + int sts; + __pmPDU *pdubuf; + result_t *pp; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PDU) + __pmDumpResult(stderr, result); +#endif + if ((sts = __pmEncodeResult(fd, result, &pdubuf)) < 0) + return sts; + pp = (result_t *)pdubuf; + pp->hdr.from = from; + sts = __pmXmitPDU(fd, pdubuf); + __pmUnpinPDUBuf(pdubuf); + return sts; +} + +/* + * enter here with pdubuf already pinned ... result may point into + * _another_ pdu buffer that is pinned on exit + */ +int +__pmDecodeResult(__pmPDU *pdubuf, pmResult **result) +{ + int numpmid; /* number of metrics */ + int i; /* range of metrics */ + int j; /* range over values */ + int index; + int vsize; /* size of vlist_t's in PDU buffer */ + char *pduend; /* end pointer for incoming buffer */ + char *vsplit; /* vlist/valueblock division point */ + result_t *pp; + vlist_t *vlp; + pmResult *pr; +#if defined(HAVE_64BIT_PTR) + char *newbuf; + int valfmt; + int numval; + int need; +/* + * Note: all sizes are in units of bytes ... beware that pp->data is in + * units of __pmPDU + */ + int nvsize; /* size of pmValue's after decode */ + int offset; /* differences in sizes */ + int vbsize; /* size of pmValueBlocks */ + pmValueSet *nvsp; +#elif defined(HAVE_32BIT_PTR) + pmValueSet *vsp; /* vlist_t == pmValueSet */ +#else + Bozo - unexpected sizeof pointer!! +#endif + + pp = (result_t *)pdubuf; + pduend = (char *)pdubuf + pp->hdr.len; + if (pduend - (char *)pdubuf < sizeof(result_t) - sizeof(__pmPDU)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: len=%d smaller than min %d\n", pp->hdr.len, (int)(sizeof(result_t) - sizeof(__pmPDU))); + } +#endif + return PM_ERR_IPC; + } + + numpmid = ntohl(pp->numpmid); + if (numpmid < 0 || numpmid > pp->hdr.len) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: numpmid=%d negative or not smaller than PDU len %d\n", numpmid, pp->hdr.len); + } +#endif + return PM_ERR_IPC; + } + if (numpmid >= (INT_MAX - sizeof(pmResult)) / sizeof(pmValueSet *)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: numpmid=%d larger than max %ld\n", numpmid, (long)(INT_MAX - sizeof(pmResult) / sizeof(pmValueSet *))); + } +#endif + return PM_ERR_IPC; + } + if ((pr = (pmResult *)malloc(sizeof(pmResult) + + (numpmid - 1) * sizeof(pmValueSet *))) == NULL) { + return -oserror(); + } + pr->numpmid = numpmid; + pr->timestamp.tv_sec = ntohl(pp->timestamp.tv_sec); + pr->timestamp.tv_usec = ntohl(pp->timestamp.tv_usec); + +#if defined(HAVE_64BIT_PTR) + vsplit = pduend; /* smallest observed value block pointer */ + nvsize = vsize = vbsize = 0; + for (i = 0; i < numpmid; i++) { + vlp = (vlist_t *)&pp->data[vsize/sizeof(__pmPDU)]; + + if (sizeof(*vlp) - sizeof(vlp->vlist) - sizeof(int) > (pduend - (char *)vlp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] outer vlp past end of PDU buffer\n", i); + } +#endif + goto corrupt; + } + + vsize += sizeof(vlp->pmid) + sizeof(vlp->numval); + nvsize += sizeof(pmValueSet); + numval = ntohl(vlp->numval); + valfmt = ntohl(vlp->valfmt); +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + pmID pmid = __ntohpmID(vlp->pmid); + char strbuf[20]; + fprintf(stderr, "vlist[%d] pmid: %s numval: %d", + i, pmIDStr_r(pmid, strbuf, sizeof(strbuf)), numval); + } +#endif + /* numval may be negative - it holds an error code in that case */ + if (numval > pp->hdr.len) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] numval=%d > len=%d\n", i, numval, pp->hdr.len); + } +#endif + goto corrupt; + } + if (numval > 0) { + if (sizeof(*vlp) - sizeof(vlp->vlist) > (pduend - (char *)vlp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] inner vlp past end of PDU buffer\n", i); + } +#endif + goto corrupt; + } + if (numval >= (INT_MAX - sizeof(*vlp)) / sizeof(__pmValue_PDU)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] numval=%d > max=%ld\n", i, numval, (long)((INT_MAX - sizeof(*vlp)) / sizeof(__pmValue_PDU))); + } +#endif + goto corrupt; + } + vsize += sizeof(vlp->valfmt) + numval * sizeof(__pmValue_PDU); + nvsize += (numval - 1) * sizeof(pmValue); +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, " valfmt: %s", + valfmt == PM_VAL_INSITU ? "insitu" : "ptr"); + } +#endif + if (valfmt != PM_VAL_INSITU) { + for (j = 0; j < numval; j++) { + __pmValue_PDU *pduvp; + pmValueBlock *pduvbp; + + pduvp = &vlp->vlist[j]; + if (sizeof(__pmValue_PDU) > (size_t)(pduend - (char *)pduvp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] intial pduvp past end of PDU buffer\n", i, j); + } +#endif + goto corrupt; + } + index = ntohl(pduvp->value.lval); + if (index < 0 || index > pp->hdr.len) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] index=%d\n", i, j, index); + } +#endif + goto corrupt; + } + pduvbp = (pmValueBlock *)&pdubuf[index]; + if (vsplit > (char *)pduvbp) + vsplit = (char *)pduvbp; + if (sizeof(unsigned int) > (size_t)(pduend - (char *)pduvbp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] second pduvp past end of PDU buffer\n", i, j); + } +#endif + goto corrupt; + } + __ntohpmValueBlock(pduvbp); + if (pduvbp->vlen < PM_VAL_HDR_SIZE || pduvbp->vlen > pp->hdr.len) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] vlen=%d\n", i, j, pduvbp->vlen); + } +#endif + goto corrupt; + } + if (pduvbp->vlen > (size_t)(pduend - (char *)pduvbp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] third pduvp past end of PDU buffer\n", i, j); + } +#endif + goto corrupt; + } + vbsize += PM_PDU_SIZE_BYTES(pduvbp->vlen); +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, " len: %d type: %d", + pduvbp->vlen - PM_VAL_HDR_SIZE, pduvbp->vtype); + } +#endif + } + } + } +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fputc('\n', stderr); + } +#endif + } + + need = nvsize + vbsize; + offset = sizeof(result_t) - sizeof(__pmPDU) + vsize; + +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "need: %d vsize: %d nvsize: %d vbsize: %d offset: %d hdr.len: %d pduend: %p vsplit: %p (diff %d) pdubuf: %p (diff %d)\n", need, vsize, nvsize, vbsize, offset, pp->hdr.len, pduend, vsplit, (int)(pduend-vsplit), pdubuf, (int)(pduend-(char *)pdubuf)); + } +#endif + + if (need < 0 || + vsize > INT_MAX / sizeof(__pmPDU) || + vbsize > INT_MAX / sizeof(pmValueBlock) || + offset != pp->hdr.len - (pduend - vsplit) || + offset + vbsize != pduend - (char *)pdubuf) { + goto corrupt; + } + + /* the original pdubuf is already pinned so we won't allocate that again */ + if ((newbuf = (char *)__pmFindPDUBuf(need)) == NULL) { + free(pr); + return -oserror(); + } + + /* + * At this point, we have verified the contents of the incoming PDU and + * the following is set up ... + * + * From the original PDU buffer ... + * :-----:---------:-----------:----------------:---------------------: + * : Hdr :timestamp: numpmid : ... vlists ... : .. pmValueBlocks .. : + * :-----:---------:-----------:----------------:---------------------: + * <--- vsize ---> <---- vbsize ----> + * bytes bytes + * + * and in the new PDU buffer we are going to build ... + * :---------------------:---------------------: + * : ... pmValueSets ... : .. pmValueBlocks .. : + * :---------------------:---------------------: + * <--- nvsize ---> <---- vbsize ----> + * bytes bytes + */ + + if (vbsize) { + /* pmValueBlocks (if any) are copied across "as is" */ + index = vsize / sizeof(__pmPDU); + memcpy((void *)&newbuf[nvsize], (void *)&pp->data[index], vbsize); + } + + /* + * offset is a bit tricky ... _add_ the expansion due to the + * different sizes of the vlist_t and pmValueSet, and _subtract_ + * the PDU header and pmResult fields ... + */ + offset = nvsize - vsize + - (int)sizeof(pp->hdr) - (int)sizeof(pp->timestamp) + - (int)sizeof(pp->numpmid); + nvsize = vsize = 0; + for (i = 0; i < numpmid; i++) { + vlp = (vlist_t *)&pp->data[vsize/sizeof(__pmPDU)]; + nvsp = (pmValueSet *)&newbuf[nvsize]; + pr->vset[i] = nvsp; + nvsp->pmid = __ntohpmID(vlp->pmid); + nvsp->numval = ntohl(vlp->numval); +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + char strbuf[20]; + fprintf(stderr, "new vlist[%d] pmid: %s numval: %d", + i, pmIDStr_r(nvsp->pmid, strbuf, sizeof(strbuf)), nvsp->numval); + } +#endif + + vsize += sizeof(nvsp->pmid) + sizeof(nvsp->numval); + nvsize += sizeof(pmValueSet); + if (nvsp->numval > 0) { + nvsp->valfmt = ntohl(vlp->valfmt); +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, " valfmt: %s", + nvsp->valfmt == PM_VAL_INSITU ? "insitu" : "ptr"); + } +#endif + vsize += sizeof(nvsp->valfmt) + nvsp->numval * sizeof(__pmValue_PDU); + nvsize += (nvsp->numval - 1) * sizeof(pmValue); + for (j = 0; j < nvsp->numval; j++) { + __pmValue_PDU *vp = &vlp->vlist[j]; + pmValue *nvp = &nvsp->vlist[j]; + + nvp->inst = ntohl(vp->inst); + if (nvsp->valfmt == PM_VAL_INSITU) { + nvp->value.lval = ntohl(vp->value.lval); +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, " value: %d", nvp->value.lval); + } +#endif + } + else { + /* + * in the input PDU buffer, pval is an index to the + * start of the pmValueBlock, in units of __pmPDU + */ + index = sizeof(__pmPDU) * ntohl(vp->value.pval) + offset; + nvp->value.pval = (pmValueBlock *)&newbuf[index]; +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + int k, len; + len = nvp->value.pval->vlen - PM_VAL_HDR_SIZE; + fprintf(stderr, " len: %d type: %d value: 0x", len, + nvp->value.pval->vtype); + for (k = 0; k < len; k++) + fprintf(stderr, "%02x", nvp->value.pval->vbuf[k]); + } +#endif + } + } + } +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fputc('\n', stderr); + } +#endif + } + if (numpmid == 0) + __pmUnpinPDUBuf(newbuf); + +#elif defined(HAVE_32BIT_PTR) + + pr->timestamp.tv_sec = ntohl(pp->timestamp.tv_sec); + pr->timestamp.tv_usec = ntohl(pp->timestamp.tv_usec); + vlp = (vlist_t *)pp->data; + vsplit = pduend; + vsize = 0; + + /* + * Now fix up any pointers in pmValueBlocks (currently offsets into + * the PDU buffer) by adding the base address of the PDU buffer. + */ + for (i = 0; i < numpmid; i++) { + if (sizeof(*vlp) - sizeof(vlp->vlist) - sizeof(int) > (pduend - (char *)vlp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] outer vlp past end of PDU buffer\n", i); + } +#endif + goto corrupt; + } + + vsp = pr->vset[i] = (pmValueSet *)vlp; + vsp->pmid = __ntohpmID(vsp->pmid); + vsp->numval = ntohl(vsp->numval); + /* numval may be negative - it holds an error code in that case */ + if (vsp->numval > pp->hdr.len) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] numval=%d > len=%d\n", i, vsp->numval, pp->hdr.len); + } +#endif + goto corrupt; + } + + vsize += sizeof(vsp->pmid) + sizeof(vsp->numval); + if (vsp->numval > 0) { + if (sizeof(*vlp) - sizeof(vlp->vlist) > (pduend - (char *)vlp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] inner vlp past end of PDU buffer\n", i); + } +#endif + goto corrupt; + } + if (vsp->numval >= (INT_MAX - sizeof(*vlp)) / sizeof(__pmValue_PDU)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] numval=%d > max=%ld\n", i, vsp->numval, (long)((INT_MAX - sizeof(*vlp)) / sizeof(__pmValue_PDU))); + } +#endif + goto corrupt; + } + vsp->valfmt = ntohl(vsp->valfmt); + vsize += sizeof(vsp->valfmt) + vsp->numval * sizeof(__pmValue_PDU); + for (j = 0; j < vsp->numval; j++) { + __pmValue_PDU *pduvp; + pmValueBlock *pduvbp; + + pduvp = &vsp->vlist[j]; + if (sizeof(__pmValue_PDU) > (size_t)(pduend - (char *)pduvp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] initial pduvp past end of PDU buffer\n", i, j); + } +#endif + goto corrupt; + } + + pduvp->inst = ntohl(pduvp->inst); + if (vsp->valfmt == PM_VAL_INSITU) { + pduvp->value.lval = ntohl(pduvp->value.lval); + } else { + /* salvage pmValueBlocks from end of PDU */ + index = ntohl(pduvp->value.lval); + if (index < 0 || index > pp->hdr.len) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] index=%d\n", i, j, index); + } +#endif + goto corrupt; + } + pduvbp = (pmValueBlock *)&pdubuf[index]; + if (vsplit > (char *)pduvbp) + vsplit = (char *)pduvbp; + if (sizeof(unsigned int) > (size_t)(pduend - (char *)pduvbp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] second pduvp past end of PDU buffer\n", i, j); + } +#endif + goto corrupt; + } + __ntohpmValueBlock(pduvbp); + if (pduvbp->vlen < PM_VAL_HDR_SIZE || pduvbp->vlen > pp->hdr.len) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] vlen=%d\n", i, j, pduvbp->vlen); + } +#endif + goto corrupt; + } + if (pduvbp->vlen > (size_t)(pduend - (char *)pduvbp)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: pmid[%d] value[%d] third pduvp past end of PDU buffer\n", i, j); + } +#endif + goto corrupt; + } + pduvp->value.pval = pduvbp; + } + } + vlp = (vlist_t *)((__psint_t)vlp + sizeof(*vlp) + (vsp->numval-1)*sizeof(vlp->vlist[0])); + } + else { + vlp = (vlist_t *)((__psint_t)vlp + sizeof(vlp->pmid) + sizeof(vlp->numval)); + } + } + if (numpmid > 0) { + if (sizeof(result_t) - sizeof(__pmPDU) + vsize != pp->hdr.len - (pduend - vsplit)) { +#ifdef PCP_DEBUG + if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { + fprintf(stderr, "__pmDecodeResult: Bad: vsplit past end of PDU buffer\n"); + } +#endif + goto corrupt; + } + /* + * PDU buffer already pinned on entry, pin again so that + * the caller can safely call _both_ pmFreeResult() and + * __pmUnpinPDUBuf() ... refer to thread-safe notes above. + */ + __pmPinPDUBuf(pdubuf); + } +#endif + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PDU) + __pmDumpResult(stderr, pr); +#endif + + /* + * Note we return with the input buffer (pdubuf) still pinned and + * for the 64-bit pointer case the new buffer (newbuf) also pinned - + * if numpmid != 0 see the thread-safe comments above + */ + *result = pr; + return 0; + +corrupt: + free(pr); + return PM_ERR_IPC; +} |