/* * enlargePQExpBuffer * Make sure there is enough space for 'needed' more bytes in the buffer * ('needed' does not include the terminating null). * * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case * the buffer is left in "broken" state.) */ int enlargePQExpBuffer(PQExpBuffer str, size_t needed) { size_t newlen; char *newdata; if (PQExpBufferBroken(str)) return 0; /* already failed */ /* * Guard against ridiculous "needed" values, which can occur if we're fed * bogus data. Without this, we can get an overflow or infinite loop in * the following. */ if (needed >= ((size_t) INT_MAX - str->len)) { markPQExpBufferBroken(str); return 0; } needed += str->len + 1; /* total space required now */ /* Because of the above test, we now have needed <= INT_MAX */ if (needed <= str->maxlen) return 1; /* got enough space already */ /* * We don't want to allocate just a little more space with each append; * for efficiency, double the buffer size each time it overflows. * Actually, we might need to more than double it if 'needed' is big... */ newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64; while (needed > newlen) newlen = 2 * newlen; /* * Clamp to INT_MAX in case we went past it. Note we are assuming here * that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We * will still have newlen >= needed. */ if (newlen > (size_t) INT_MAX) newlen = (size_t) INT_MAX; newdata = (char *) realloc(str->data, newlen); if (newdata != NULL) { str->data = newdata; str->maxlen = newlen; return 1; } markPQExpBufferBroken(str); return 0; }
/* * appendPQExpBufferVA * Shared guts of printfPQExpBuffer/appendPQExpBuffer. * Attempt to format data and append it to str. Returns true if done * (either successful or hard failure), false if need to retry. */ static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) { size_t avail; size_t needed; int nprinted; /* * Try to format the given string into the available space; but if there's * hardly any space, don't bother trying, just enlarge the buffer first. */ if (str->maxlen > str->len + 16) { /* * Note: we intentionally leave one byte unused, as a guard against * old broken versions of vsnprintf. */ avail = str->maxlen - str->len - 1; errno = 0; nprinted = vsnprintf(str->data + str->len, avail, fmt, args); /* * If vsnprintf reports an error other than ENOMEM, fail. */ if (nprinted < 0 && errno != 0 && errno != ENOMEM) { markPQExpBufferBroken(str); return true; } /* * Note: some versions of vsnprintf return the number of chars * actually stored, not the total space needed as C99 specifies. And * at least one returns -1 on failure. Be conservative about * believing whether the print worked. */ if (nprinted >= 0 && (size_t) nprinted < avail - 1) { /* Success. Note nprinted does not include trailing null. */ str->len += nprinted; return true; } if (nprinted >= 0 && (size_t) nprinted > avail) { /* * This appears to be a C99-compliant vsnprintf, so believe its * estimate of the required space. (If it's wrong, the logic will * still work, but we may loop multiple times.) Note that the * space needed should be only nprinted+1 bytes, but we'd better * allocate one more than that so that the test above will succeed * next time. * * In the corner case where the required space just barely * overflows, fail. */ if (nprinted > INT_MAX - 2) { markPQExpBufferBroken(str); return true; } needed = nprinted + 2; } else { /* * Buffer overrun, and we don't know how much space is needed. * Estimate twice the previous buffer size, but not more than * INT_MAX. */ if (avail >= INT_MAX / 2) needed = INT_MAX; else needed = avail * 2; } } else { /* * We have to guess at how much to enlarge, since we're skipping the * formatting work. */ needed = 32; } /* Increase the buffer size and try again. */ if (!enlargePQExpBuffer(str, needed)) return true; /* oops, out of memory */ return false; }
/* * appendPQExpBufferVA * Shared guts of printfPQExpBuffer/appendPQExpBuffer. * Attempt to format data and append it to str. Returns true if done * (either successful or hard failure), false if need to retry. * * Caution: callers must be sure to preserve their entry-time errno * when looping, in case the fmt contains "%m". */ static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) { size_t avail; size_t needed; int nprinted; /* * Try to format the given string into the available space; but if there's * hardly any space, don't bother trying, just enlarge the buffer first. */ if (str->maxlen > str->len + 16) { avail = str->maxlen - str->len; nprinted = vsnprintf(str->data + str->len, avail, fmt, args); /* * If vsnprintf reports an error, fail (we assume this means there's * something wrong with the format string). */ if (unlikely(nprinted < 0)) { markPQExpBufferBroken(str); return true; } if ((size_t) nprinted < avail) { /* Success. Note nprinted does not include trailing null. */ str->len += nprinted; return true; } /* * We assume a C99-compliant vsnprintf, so believe its estimate of the * required space, and add one for the trailing null. (If it's wrong, * the logic will still work, but we may loop multiple times.) * * Choke if the required space would exceed INT_MAX, since str->maxlen * can't represent more than that. */ if (unlikely(nprinted > INT_MAX - 1)) { markPQExpBufferBroken(str); return true; } needed = nprinted + 1; } else { /* * We have to guess at how much to enlarge, since we're skipping the * formatting work. Fortunately, because of enlargePQExpBuffer's * preference for power-of-2 sizes, this number isn't very sensitive; * the net effect is that we'll double the buffer size before trying * to run vsnprintf, which seems sensible. */ needed = 32; } /* Increase the buffer size and try again. */ if (!enlargePQExpBuffer(str, needed)) return true; /* oops, out of memory */ return false; }