/* * Copyright (c) 2017, The Linux Foundation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include "AEEBufBound.h" #include "AEEstd.h" /* Note on bounds-checking logic and saturation: Simple pointer comparisons are not adequate for bounds checking. pcBuf and pcEnd are assumed to be valid pointers in the address space. But pcWrite is not ... it is a theoretical value that can exceed pcEnd, and may in fact wrap around the end of the address space. In that case the test for (pcWrite < pcEnd) will yield TRUE, although pcWrite is outside the buffer. Use (pcEnd-pcWrite) > 0 to be accurate. In order to ensure this works in all cases, we need to avoid integer overflows. We do this by restricting pcWrite to the range [pcBuf..pcBuf+INT_MAX]. The ensures that pcWrite-pcBuf and pcWrite-pcBuf will always be valid integers. It also allows us to ensure that BufBound_Wrote() will not return wildly misleading results. PCSAT pcBuf pcEnd pcBuf+MAXINT |-------------------| . . . . . . . . . | ^ ^ pcWrite: (a) (b) */ #define PCSAT(me) ((me)->pcBuf + INT_MAX) /* Advance me->pcWrite, saturating. On entry: *pnLen = number of bytes to be written (non-negative) On exit: return value = where to write (pointer into the buffer) *pnLen = number of bytes to write */ static char *BufBound_ValidateWrite(BufBound *me, int *pnLen) { int nLen = *pnLen; char *pcWrite = me->pcWrite; int nMaxCopy = me->pcEnd - pcWrite; /* could be negative! */ if (nMaxCopy < nLen) { /* Must check PCSAT to validate advance */ int nMaxAdvance = PCSAT(me) - pcWrite; /* max amount to advance */ if (nLen > nMaxAdvance) { nLen = nMaxAdvance; } if (nMaxCopy < 0) { nMaxCopy = 0; } } else { /* Simple case: all fits in the buffer */ nMaxCopy = nLen; } *pnLen = nMaxCopy; me->pcWrite = pcWrite + nLen; return pcWrite; } void BufBound_Write(BufBound *me, const char *pc, int nLen) { if (nLen > 0) { char *pcDest = BufBound_ValidateWrite(me, &nLen); while (--nLen >= 0) { pcDest[nLen] = pc[nLen]; } } } void BufBound_Putnc(BufBound *me, char c, int nLen) { if (nLen > 0) { char *pcDest = BufBound_ValidateWrite(me, &nLen); while (--nLen >= 0) { pcDest[nLen] = c; } } } void BufBound_Advance(BufBound *me, int nLen) { uint32 uOffset = (uint32)((me->pcWrite - me->pcBuf) + nLen); if (uOffset > INT_MAX) { uOffset = INT_MAX; if (nLen < 0) { uOffset = 0; } } me->pcWrite = me->pcBuf + uOffset; } void BufBound_Init(BufBound *me, char *pBuf, int nLen) { if (nLen < 0) { nLen = 0; } me->pcWrite = me->pcBuf = pBuf; me->pcEnd = pBuf + nLen; } void BufBound_Putc(BufBound *me, char c) { if ((me->pcEnd - me->pcWrite) > 0) { *me->pcWrite++ = c; } else if (me->pcWrite != PCSAT(me)) { ++me->pcWrite; } } void BufBound_ForceNullTerm(BufBound *me) { if ((me->pcEnd - me->pcWrite) > 0) { *me->pcWrite++ = '\0'; } else { if (me->pcWrite != PCSAT(me)) { ++me->pcWrite; } /* Ensure null termination if non-empty buffer */ if (me->pcEnd != me->pcBuf) { me->pcEnd[-1] = '\0'; } } } void BufBound_Puts(BufBound *me, const char *cpsz) { BufBound_Write(me, cpsz, std_strlen(cpsz)); } int BufBound_BufSize(BufBound *me) { return me->pcEnd - me->pcBuf; } int BufBound_Left(BufBound *me) { return (me->pcEnd - me->pcWrite); } int BufBound_ReallyWrote(BufBound *me) { return STD_MIN(me->pcEnd - me->pcBuf, me->pcWrite - me->pcBuf); } int BufBound_Wrote(BufBound *me) { return (me->pcWrite - me->pcBuf); } void BufBound_WriteLE(BufBound *me, const void *pvSrc, int nSrcSize, const char *pszFields) { if (nSrcSize > 0) { int nLen = nSrcSize; char *pcDest = BufBound_ValidateWrite(me, &nLen); (void)std_CopyLE(pcDest, nLen, pvSrc, nSrcSize, pszFields); } } void BufBound_WriteBE(BufBound *me, const void *pvSrc, int nSrcSize, const char *pszFields) { if (nSrcSize > 0) { int nLen = nSrcSize; char *pcDest = BufBound_ValidateWrite(me, &nLen); (void)std_CopyBE(pcDest, nLen, pvSrc, nSrcSize, pszFields); } }