2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 10:10:06 +00:00
bind/lib/isc/buffer.c
Ondřej Surý bc1d4c9cb4 Clear the pointer to destroyed object early using the semantic patch
Also disable the semantic patch as the code needs tweaks here and there because
some destroy functions might not destroy the object and return early if the
object is still in use.
2020-02-09 18:00:17 -08:00

660 lines
13 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file */
#include <inttypes.h>
#include <stdbool.h>
#include <stdarg.h>
#include <isc/buffer.h>
#include <isc/mem.h>
#include <isc/print.h>
#include <isc/region.h>
#include <isc/string.h>
#include <isc/util.h>
void
isc__buffer_init(isc_buffer_t *b, void *base, unsigned int length) {
/*
* Make 'b' refer to the 'length'-byte region starting at 'base'.
* XXXDCL see the comment in buffer.h about base being const.
*/
REQUIRE(b != NULL);
ISC__BUFFER_INIT(b, base, length);
}
void
isc__buffer_initnull(isc_buffer_t *b) {
/*
* Initialize a new buffer which has no backing store. This can
* later be grown as needed and swapped in place.
*/
ISC__BUFFER_INIT(b, NULL, 0);
}
void
isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length) {
/*
* Re-initialize the buffer enough to reconfigure the base of the
* buffer. We will swap in the new buffer, after copying any
* data we contain into the new buffer and adjusting all of our
* internal pointers.
*
* The buffer must not be smaller than the length of the original
* buffer.
*/
REQUIRE(b->length <= length);
REQUIRE(base != NULL);
REQUIRE(!b->autore);
if (b->length > 0U) {
(void)memmove(base, b->base, b->length);
}
b->base = base;
b->length = length;
}
void
isc__buffer_invalidate(isc_buffer_t *b) {
/*
* Make 'b' an invalid buffer.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(!ISC_LINK_LINKED(b, link));
REQUIRE(b->mctx == NULL);
ISC__BUFFER_INVALIDATE(b);
}
void
isc_buffer_setautorealloc(isc_buffer_t *b, bool enable) {
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(b->mctx != NULL);
b->autore = enable;
}
void
isc__buffer_region(isc_buffer_t *b, isc_region_t *r) {
/*
* Make 'r' refer to the region of 'b'.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(r != NULL);
ISC__BUFFER_REGION(b, r);
}
void
isc__buffer_usedregion(const isc_buffer_t *b, isc_region_t *r) {
/*
* Make 'r' refer to the used region of 'b'.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(r != NULL);
ISC__BUFFER_USEDREGION(b, r);
}
void
isc__buffer_availableregion(isc_buffer_t *b, isc_region_t *r) {
/*
* Make 'r' refer to the available region of 'b'.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(r != NULL);
ISC__BUFFER_AVAILABLEREGION(b, r);
}
void
isc__buffer_add(isc_buffer_t *b, unsigned int n) {
/*
* Increase the 'used' region of 'b' by 'n' bytes.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(b->used + n <= b->length);
ISC__BUFFER_ADD(b, n);
}
void
isc__buffer_subtract(isc_buffer_t *b, unsigned int n) {
/*
* Decrease the 'used' region of 'b' by 'n' bytes.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(b->used >= n);
ISC__BUFFER_SUBTRACT(b, n);
}
void
isc__buffer_clear(isc_buffer_t *b) {
/*
* Make the used region empty.
*/
REQUIRE(ISC_BUFFER_VALID(b));
ISC__BUFFER_CLEAR(b);
}
void
isc__buffer_consumedregion(isc_buffer_t *b, isc_region_t *r) {
/*
* Make 'r' refer to the consumed region of 'b'.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(r != NULL);
ISC__BUFFER_CONSUMEDREGION(b, r);
}
void
isc__buffer_remainingregion(isc_buffer_t *b, isc_region_t *r) {
/*
* Make 'r' refer to the remaining region of 'b'.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(r != NULL);
ISC__BUFFER_REMAININGREGION(b, r);
}
void
isc__buffer_activeregion(isc_buffer_t *b, isc_region_t *r) {
/*
* Make 'r' refer to the active region of 'b'.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(r != NULL);
ISC__BUFFER_ACTIVEREGION(b, r);
}
void
isc__buffer_setactive(isc_buffer_t *b, unsigned int n) {
/*
* Sets the end of the active region 'n' bytes after current.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(b->current + n <= b->used);
ISC__BUFFER_SETACTIVE(b, n);
}
void
isc__buffer_first(isc_buffer_t *b) {
/*
* Make the consumed region empty.
*/
REQUIRE(ISC_BUFFER_VALID(b));
ISC__BUFFER_FIRST(b);
}
void
isc__buffer_forward(isc_buffer_t *b, unsigned int n) {
/*
* Increase the 'consumed' region of 'b' by 'n' bytes.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(b->current + n <= b->used);
ISC__BUFFER_FORWARD(b, n);
}
void
isc__buffer_back(isc_buffer_t *b, unsigned int n) {
/*
* Decrease the 'consumed' region of 'b' by 'n' bytes.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(n <= b->current);
ISC__BUFFER_BACK(b, n);
}
void
isc_buffer_compact(isc_buffer_t *b) {
unsigned int length;
void *src;
/*
* Compact the used region by moving the remaining region so it occurs
* at the start of the buffer. The used region is shrunk by the size
* of the consumed region, and the consumed region is then made empty.
*/
REQUIRE(ISC_BUFFER_VALID(b));
src = isc_buffer_current(b);
length = isc_buffer_remaininglength(b);
if (length > 0U) {
(void)memmove(b->base, src, (size_t)length);
}
if (b->active > b->current)
b->active -= b->current;
else
b->active = 0;
b->current = 0;
b->used = length;
}
uint8_t
isc_buffer_getuint8(isc_buffer_t *b) {
unsigned char *cp;
uint8_t result;
/*
* Read an unsigned 8-bit integer from 'b' and return it.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(b->used - b->current >= 1);
cp = isc_buffer_current(b);
b->current += 1;
result = ((uint8_t)(cp[0]));
return (result);
}
void
isc__buffer_putuint8(isc_buffer_t *b, uint8_t val) {
isc_result_t result;
REQUIRE(ISC_BUFFER_VALID(b));
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, 1);
REQUIRE(result == ISC_R_SUCCESS);
}
REQUIRE(isc_buffer_availablelength(b) >= 1);
ISC__BUFFER_PUTUINT8(b, val);
}
uint16_t
isc_buffer_getuint16(isc_buffer_t *b) {
unsigned char *cp;
uint16_t result;
/*
* Read an unsigned 16-bit integer in network byte order from 'b',
* convert it to host byte order, and return it.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(b->used - b->current >= 2);
cp = isc_buffer_current(b);
b->current += 2;
result = ((unsigned int)(cp[0])) << 8;
result |= ((unsigned int)(cp[1]));
return (result);
}
void
isc__buffer_putuint16(isc_buffer_t *b, uint16_t val) {
isc_result_t result;
REQUIRE(ISC_BUFFER_VALID(b));
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, 2);
REQUIRE(result == ISC_R_SUCCESS);
}
REQUIRE(isc_buffer_availablelength(b) >= 2);
ISC__BUFFER_PUTUINT16(b, val);
}
void
isc__buffer_putuint24(isc_buffer_t *b, uint32_t val) {
isc_result_t result;
REQUIRE(ISC_BUFFER_VALID(b));
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, 3);
REQUIRE(result == ISC_R_SUCCESS);
}
REQUIRE(isc_buffer_availablelength(b) >= 3);
ISC__BUFFER_PUTUINT24(b, val);
}
uint32_t
isc_buffer_getuint32(isc_buffer_t *b) {
unsigned char *cp;
uint32_t result;
/*
* Read an unsigned 32-bit integer in network byte order from 'b',
* convert it to host byte order, and return it.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(b->used - b->current >= 4);
cp = isc_buffer_current(b);
b->current += 4;
result = ((unsigned int)(cp[0])) << 24;
result |= ((unsigned int)(cp[1])) << 16;
result |= ((unsigned int)(cp[2])) << 8;
result |= ((unsigned int)(cp[3]));
return (result);
}
void
isc__buffer_putuint32(isc_buffer_t *b, uint32_t val) {
isc_result_t result;
REQUIRE(ISC_BUFFER_VALID(b));
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, 4);
REQUIRE(result == ISC_R_SUCCESS);
}
REQUIRE(isc_buffer_availablelength(b) >= 4);
ISC__BUFFER_PUTUINT32(b, val);
}
uint64_t
isc_buffer_getuint48(isc_buffer_t *b) {
unsigned char *cp;
uint64_t result;
/*
* Read an unsigned 48-bit integer in network byte order from 'b',
* convert it to host byte order, and return it.
*/
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(b->used - b->current >= 6);
cp = isc_buffer_current(b);
b->current += 6;
result = ((int64_t)(cp[0])) << 40;
result |= ((int64_t)(cp[1])) << 32;
result |= ((int64_t)(cp[2])) << 24;
result |= ((int64_t)(cp[3])) << 16;
result |= ((int64_t)(cp[4])) << 8;
result |= ((int64_t)(cp[5]));
return (result);
}
void
isc__buffer_putuint48(isc_buffer_t *b, uint64_t val) {
isc_result_t result;
uint16_t valhi;
uint32_t vallo;
REQUIRE(ISC_BUFFER_VALID(b));
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, 6);
REQUIRE(result == ISC_R_SUCCESS);
}
REQUIRE(isc_buffer_availablelength(b) >= 6);
valhi = (uint16_t)(val >> 32);
vallo = (uint32_t)(val & 0xFFFFFFFF);
ISC__BUFFER_PUTUINT16(b, valhi);
ISC__BUFFER_PUTUINT32(b, vallo);
}
void
isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base,
unsigned int length)
{
isc_result_t result;
REQUIRE(ISC_BUFFER_VALID(b));
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, length);
REQUIRE(result == ISC_R_SUCCESS);
}
REQUIRE(isc_buffer_availablelength(b) >= length);
ISC__BUFFER_PUTMEM(b, base, length);
}
void
isc__buffer_putstr(isc_buffer_t *b, const char *source) {
unsigned int l;
unsigned char *cp;
isc_result_t result;
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(source != NULL);
/*
* Do not use ISC__BUFFER_PUTSTR(), so strlen is only done once.
*/
l = strlen(source);
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, l);
REQUIRE(result == ISC_R_SUCCESS);
}
REQUIRE(isc_buffer_availablelength(b) >= l);
cp = isc_buffer_used(b);
memmove(cp, source, l);
b->used += l;
}
void
isc_buffer_putdecint(isc_buffer_t *b, int64_t v) {
unsigned int l=0;
unsigned char *cp;
char buf[21];
isc_result_t result;
REQUIRE(ISC_BUFFER_VALID(b));
/* xxxwpk do it more low-level way ? */
l = snprintf(buf, 21, "%" PRId64, v);
RUNTIME_CHECK(l <= 21);
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, l);
REQUIRE(result == ISC_R_SUCCESS);
}
REQUIRE(isc_buffer_availablelength(b) >= l);
cp = isc_buffer_used(b);
memmove(cp, buf, l);
b->used += l;
}
isc_result_t
isc_buffer_dup(isc_mem_t *mctx, isc_buffer_t **dstp, const isc_buffer_t *src) {
isc_buffer_t *dst = NULL;
isc_region_t region;
isc_result_t result;
REQUIRE(dstp != NULL && *dstp == NULL);
REQUIRE(ISC_BUFFER_VALID(src));
isc_buffer_usedregion(src, &region);
isc_buffer_allocate(mctx, &dst, region.length);
result = isc_buffer_copyregion(dst, &region);
RUNTIME_CHECK(result == ISC_R_SUCCESS); /* NOSPACE is impossible */
*dstp = dst;
return (ISC_R_SUCCESS);
}
isc_result_t
isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r) {
isc_result_t result;
REQUIRE(ISC_BUFFER_VALID(b));
REQUIRE(r != NULL);
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, r->length);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
if (r->length > isc_buffer_availablelength(b)) {
return (ISC_R_NOSPACE);
}
if (r->length > 0U) {
memmove(isc_buffer_used(b), r->base, r->length);
b->used += r->length;
}
return (ISC_R_SUCCESS);
}
void
isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer,
unsigned int length)
{
REQUIRE(dynbuffer != NULL && *dynbuffer == NULL);
isc_buffer_t *dbuf = isc_mem_get(mctx, sizeof(isc_buffer_t));
unsigned char *bdata = isc_mem_get(mctx, length);
isc_buffer_init(dbuf, bdata, length);
ENSURE(ISC_BUFFER_VALID(dbuf));
dbuf->mctx = mctx;
*dynbuffer = dbuf;
}
isc_result_t
isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size) {
unsigned char *bdata;
uint64_t len;
REQUIRE(dynbuffer != NULL);
REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
len = (*dynbuffer)->length;
if ((len - (*dynbuffer)->used) >= size) {
return (ISC_R_SUCCESS);
}
if ((*dynbuffer)->mctx == NULL) {
return (ISC_R_NOSPACE);
}
/* Round to nearest buffer size increment */
len = size + (*dynbuffer)->used;
len = (len + ISC_BUFFER_INCR - 1 - ((len - 1) % ISC_BUFFER_INCR));
/* Cap at UINT_MAX */
if (len > UINT_MAX) {
len = UINT_MAX;
}
if ((len - (*dynbuffer)->used) < size) {
return (ISC_R_NOMEMORY);
}
/*
* XXXMUKS: This is far more expensive than plain realloc() as
* it doesn't remap pages, but does ordinary copy. So is
* isc_mem_reallocate(), which has additional issues.
*/
bdata = isc_mem_get((*dynbuffer)->mctx, (unsigned int)len);
memmove(bdata, (*dynbuffer)->base, (*dynbuffer)->length);
isc_mem_put((*dynbuffer)->mctx, (*dynbuffer)->base,
(*dynbuffer)->length);
(*dynbuffer)->base = bdata;
(*dynbuffer)->length = (unsigned int) len;
return (ISC_R_SUCCESS);
}
void
isc_buffer_free(isc_buffer_t **dynbuffer) {
isc_buffer_t *dbuf;
isc_mem_t *mctx;
REQUIRE(dynbuffer != NULL);
REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
REQUIRE((*dynbuffer)->mctx != NULL);
dbuf = *dynbuffer;
*dynbuffer = NULL;/* destroy external reference */
mctx = dbuf->mctx;
dbuf->mctx = NULL;
isc_mem_put(mctx, dbuf->base, dbuf->length);
isc_buffer_invalidate(dbuf);
isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t));
}
isc_result_t
isc_buffer_printf(isc_buffer_t *b, const char *format, ...) {
va_list ap;
int n;
isc_result_t result;
REQUIRE(ISC_BUFFER_VALID(b));
va_start(ap, format);
n = vsnprintf(NULL, 0, format, ap);
va_end(ap);
if (n < 0) {
return (ISC_R_FAILURE);
}
if (ISC_UNLIKELY(b->autore)) {
result = isc_buffer_reserve(&b, n + 1);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
if (isc_buffer_availablelength(b) < (unsigned int)n + 1) {
return (ISC_R_NOSPACE);
}
va_start(ap, format);
n = vsnprintf(isc_buffer_used(b), n + 1, format, ap);
va_end(ap);
if (n < 0) {
return (ISC_R_FAILURE);
}
b->used += n;
return (ISC_R_SUCCESS);
}