diff --git a/.gitmodules b/.gitmodules index 25b3b54..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +0,0 @@ -[submodule "chck"] - path = lib/chck - url = git://github.com/Cloudef/chck.git - branch = master diff --git a/CMakeLists.txt b/CMakeLists.txt index 0845729..3f75814 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,6 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMake") # Subprojects include(subproject) -add_subdirectory(lib) # CPack set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") @@ -74,7 +73,7 @@ set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_definitions(-D_DEFAULT_SOURCE) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src ${CHCK_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) add_subdirectory(src) if (PI9_BUILD_BINARIES) @@ -82,6 +81,8 @@ if (PI9_BUILD_BINARIES) endif () file(COPY src/pi9.h src/pi9_string.h DESTINATION include/pi9) +file(COPY src/chck/buffer/buffer.h src/chck/buffer/endianess.h DESTINATION include/chck/buffer) +file(COPY src/chck/overflow/overflow.h DESTINATION include/chck/overflow) if ("${CMAKE_PROJECT_NAME}" STREQUAL "${PROJECT_NAME}") feature_summary(WHAT ALL) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt deleted file mode 100644 index 9c0b336..0000000 --- a/lib/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -set(CHCK_BUILD_STATIC ON CACHE STRING "Build chck statically if not found systemwide" FORCE) -set(CHCK_BUILD_TESTS OFF CACHE STRING "Do not build chck tests" FORCE) -add_subproject(chck Chck) diff --git a/lib/chck b/lib/chck deleted file mode 160000 index 7fd2a73..0000000 --- a/lib/chck +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7fd2a7375492c0704286f7073fa22dbfe2218653 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ebc3116..27bb0f1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(pi9 pi9.c pi9_string.c) +add_library(pi9 pi9.c pi9_string.c chck/buffer/buffer.c chck/buffer/buffer.h chck/buffer/endianess.h chck/overflow.h) target_link_libraries(pi9 PRIVATE ${CHCK_LIBRARIES}) # Parse soversion version diff --git a/src/chck/buffer/buffer.c b/src/chck/buffer/buffer.c new file mode 100644 index 0000000..3e31996 --- /dev/null +++ b/src/chck/buffer/buffer.c @@ -0,0 +1,568 @@ +#include "buffer.h" +#include +#include +#include +#include + +#if HAS_ZLIB +# include +#elif !defined(HAS_ZLIB) +# define HAS_ZLIB 0 +#endif + +struct chck_variant { + union { + uint64_t u64; + uint32_t u32; + uint16_t u16; + uint8_t u8; + uint8_t b[sizeof(uint64_t)]; + }; + enum chck_bits bits; +}; + +static inline bool +valid_bits(enum chck_bits bits) +{ + return (bits == CHCK_BUFFER_B8 || + bits == CHCK_BUFFER_B16 || + bits == CHCK_BUFFER_B32 || + bits == CHCK_BUFFER_B64); +} + +static inline enum chck_bits +smallest_bits_for_value(uintmax_t v) +{ + static const struct { + uintmax_t off; + enum chck_bits bits; + } map[3] = { + { ~(uint32_t)0, CHCK_BUFFER_B64 }, + { ~(uint16_t)0, CHCK_BUFFER_B32 }, + { ~(uint8_t)0, CHCK_BUFFER_B16 }, + }; + + for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); ++i) { + if (v <= map[i].off) + continue; + + return map[i].bits; + } + + return CHCK_BUFFER_B8; +} + +static inline uintmax_t +variant_get_value(struct chck_variant v) +{ + switch (v.bits) { + case CHCK_BUFFER_B8: + return v.u8; + case CHCK_BUFFER_B16: + return v.u16; + case CHCK_BUFFER_B32: + return v.u32; + case CHCK_BUFFER_B64: + return v.u64; + } + + assert(0 && "should not happen"); + return 0; +} + +void +chck_buffer_flush(struct chck_buffer *buf) +{ + assert(buf); + + if (buf->copied) + free(buf->buffer); + + buf->copied = false; + buf->curpos = buf->buffer = NULL; +} + +void +chck_buffer_release(struct chck_buffer *buf) +{ + if (!buf) + return; + + chck_buffer_flush(buf); + *buf = (struct chck_buffer){0}; +} + +bool +chck_buffer_from_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess) +{ + assert(buf); + *buf = (struct chck_buffer){ .step = 32 }; + chck_buffer_set_pointer(buf, ptr, size, endianess); + return true; +} + +bool +chck_buffer(struct chck_buffer *buf, size_t size, enum chck_endianess endianess) +{ + assert(buf); + + void *data = NULL; + if (size > 0 && !(data = malloc(size))) + return false; + + if (unlikely(!chck_buffer_from_pointer(buf, data, size, endianess))) + goto fail; + + buf->copied = true; + return true; + +fail: + free(data); + return false; +} + +void +chck_buffer_set_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess) +{ + assert(buf); + + if (buf->copied) { + free(buf->buffer); + buf->buffer = NULL; + } + + if (endianess == CHCK_ENDIANESS_NATIVE) { + buf->endianess = chck_endianess(); + } else { + buf->endianess = endianess; + } + + buf->size = size; + buf->buffer = buf->curpos = ptr; + buf->copied = false; +} + +bool +chck_buffer_resize(struct chck_buffer *buf, size_t size) +{ + assert(buf); + + if (unlikely(size == buf->size)) + return true; + + if (unlikely(size == 0)) { + chck_buffer_flush(buf); + return true; + } + + uint8_t *tmp; + if (!(tmp = realloc((buf->copied ? buf->buffer : NULL), size))) + return false; + + /* set new buffer position */ + if (buf->curpos - buf->buffer > (ptrdiff_t)size) { + buf->curpos = tmp + (buf->size - size); + } else { + buf->curpos = tmp + (buf->curpos - buf->buffer); + } + + buf->size = size; + buf->buffer = tmp; + buf->copied = true; + return true; +} + +ptrdiff_t +chck_buffer_seek(struct chck_buffer *buf, long offset, int whence) +{ + assert(buf); + assert(whence == SEEK_SET || whence == SEEK_END || whence == SEEK_CUR); + + switch (whence) { + case SEEK_SET: + if (buf->buffer + offset > buf->buffer + buf->size) { + buf->curpos = buf->buffer + buf->size; + } else if (offset >= 0) { + buf->curpos = buf->buffer + offset; + } + break; + case SEEK_CUR: + if (buf->curpos + offset > buf->buffer + buf->size) { + buf->curpos = buf->curpos + buf->size; + } else if (buf->curpos + offset < buf->buffer) { + buf->curpos = buf->buffer; + } else { + buf->curpos = buf->curpos + offset; + } + break; + case SEEK_END: + buf->curpos = buf->buffer + buf->size; + break; + default:break; + } + + return buf->curpos - buf->buffer; +} + +static bool +bounds_check(struct chck_buffer *buf, size_t size, size_t memb) +{ + size_t nsz; + if (unlikely(chck_mul_ofsz(size, memb, &nsz))) + return false; + + const size_t sz = buf->size - (buf->curpos - buf->buffer); + if (nsz > sz) { + /* buf->size + size * memb + buf->step */ + if (unlikely(chck_add_ofsz(buf->size, nsz, &nsz)) || unlikely(chck_add_ofsz(buf->step, nsz, &nsz))) + return false; + + if (!chck_buffer_resize(buf, nsz)) + return false; + } + + return true; +} + +size_t +chck_buffer_fill(const void *src, size_t size, size_t memb, struct chck_buffer *buf) +{ + assert(src && buf); + + if (!bounds_check(buf, size, memb) || !buf->curpos || !src) + return 0; + + memcpy(buf->curpos, src, size * memb); + return memb; +} + +size_t +chck_buffer_fill_from_file(FILE *src, size_t size, size_t memb, struct chck_buffer *buf) +{ + assert(src && buf); + + if (!bounds_check(buf, size, memb) || !buf->curpos || !src) + return 0; + + return fread(buf->curpos, size, memb, src); +} + +size_t +chck_buffer_fill_from_fd(int fd, size_t size, size_t memb, struct chck_buffer *buf) +{ + assert(fd && buf); + + if (!bounds_check(buf, size, memb) || !buf->curpos) + return 0; + + const ssize_t ret = read(fd, buf->curpos, size * memb); + if (unlikely(ret == -1 || ret == 0)) + return 0; + + return (size_t)ret / size; +} + +size_t +chck_buffer_read(void *dst, size_t size, size_t memb, struct chck_buffer *buf) +{ + assert(dst && buf); + + size_t nsz; + if (unlikely(chck_mul_ofsz(size, memb, &nsz))) + return 0; + + if (unlikely(nsz > buf->size - (buf->curpos - buf->buffer))) { + assert(size != 0); // should never happen + // read as much as we can + memb = (buf->size - (buf->curpos - buf->buffer)) / size; + } + + memcpy(dst, buf->curpos, size * memb); + buf->curpos += size * memb; + return memb; +} + +bool +chck_buffer_read_int(void *i, enum chck_bits bits, struct chck_buffer *buf) +{ + assert(i && buf); + + if (!valid_bits(bits)) + return false; + + if (unlikely(chck_buffer_read(i, bits, 1, buf) != 1)) + return false; + + if (!chck_buffer_native_endianess(buf)) + chck_bswap_single(i, bits); + + return true; +} + +bool +chck_buffer_read_string_of_type(char **str, size_t *out_len, enum chck_bits bits, struct chck_buffer *buf) +{ + assert(buf && str); + *str = NULL; + + if (out_len) + *out_len = 0; + + struct chck_variant v = { .bits = bits }; + if (unlikely(!chck_buffer_read_int(v.b, bits, buf))) + return false; + + const size_t len = variant_get_value(v); + + if (out_len) + *out_len = len; + + if (len <= 0) + return true; + + if (!(*str = chck_calloc_add_of(len, 1))) + return false; + + if (unlikely(chck_buffer_read(*str, 1, len, buf) != len)) { + free(*str); + return false; + } + + return true; +} + +bool +chck_buffer_read_string(char **str, size_t *len, struct chck_buffer *buf) +{ + assert(str && buf); + *str = NULL; + + if (len) + *len = 0; + + uint8_t bits; + if (unlikely(!chck_buffer_read_int(&bits, sizeof(bits), buf))) + return false; + + return likely(chck_buffer_read_string_of_type(str, len, bits, buf)); +} + +size_t +chck_buffer_write(const void *src, size_t size, size_t memb, struct chck_buffer *buf) +{ + memb = chck_buffer_fill(src, size, memb, buf); + buf->curpos += size * memb; + return memb; +} + +size_t +chck_buffer_write_from_file(FILE *src, size_t size, size_t memb, struct chck_buffer *buf) +{ + memb = chck_buffer_fill_from_file(src, size, memb, buf); + buf->curpos += size * memb; + return memb; +} + +size_t +chck_buffer_write_from_fd(int fd, size_t size, size_t memb, struct chck_buffer *buf) +{ + memb = chck_buffer_fill_from_fd(fd, size, memb, buf); + buf->curpos += size * memb; + return memb; +} + +bool +chck_buffer_write_int(const void *i, enum chck_bits bits, struct chck_buffer *buf) +{ + assert(buf); + + if (!valid_bits(bits)) + return false; + + bool ret; + if (!chck_buffer_native_endianess(buf)) { + uint8_t b[sizeof(uint64_t)]; + memcpy(b, i, bits); + chck_bswap_single(b, bits); + ret = (chck_buffer_write(b, bits, 1, buf) == 1); + } else { + ret = (chck_buffer_write(i, bits, 1, buf) == 1); + } + + return ret; +} + +bool +chck_buffer_write_string_of_type(const char *str, size_t len, enum chck_bits bits, struct chck_buffer *buf) +{ + assert(buf); + + bool ret = false; + switch (bits) { + case CHCK_BUFFER_B8: + ret = chck_buffer_write_int((uint8_t[]){len}, bits, buf); + break; + case CHCK_BUFFER_B16: + ret = chck_buffer_write_int((uint16_t[]){len}, bits, buf); + break; + case CHCK_BUFFER_B32: + ret = chck_buffer_write_int((uint32_t[]){len}, bits, buf); + break; + case CHCK_BUFFER_B64: + ret = chck_buffer_write_int((uint64_t[]){len}, bits, buf); + break; + } + + if (unlikely(!ret)) + return false; + + return likely(chck_buffer_write(str, 1, len, buf) == len); +} + +bool +chck_buffer_write_string(const char *str, size_t len, struct chck_buffer *buf) +{ + assert(buf); + + const uint8_t bits = smallest_bits_for_value(len); + if (unlikely(!chck_buffer_write_int(&bits, sizeof(bits), buf))) + return false; + + return likely(chck_buffer_write_string_of_type(str, len, bits, buf)); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + +size_t +chck_buffer_write_format(struct chck_buffer *buf, const char *fmt, ...) +{ + va_list argp; + va_start(argp, fmt); + const size_t wrote = chck_buffer_write_varg(buf, fmt, argp); + va_end(argp); + return wrote; +} + +size_t +chck_buffer_write_varg(struct chck_buffer *buf, const char *fmt, va_list args) +{ + va_list cpy; + va_copy(cpy, args); + + char *str = NULL; + const size_t len = vsnprintf(NULL, 0, fmt, args); + if (len > 0 && !(str = chck_malloc_add_of(len, 1))) { + va_end(cpy); + return false; + } + + vsnprintf(str, len + 1, fmt, cpy); + va_end(cpy); + + const size_t wrote = chck_buffer_write(str, 1, len, buf); + free(str); + return wrote; +} + +#pragma GCC diagnostic pop + +bool +chck_buffer_has_zlib(void) +{ + return HAS_ZLIB; +} + +#pragma GCC diagnostic ignored "-Wsuggest-attribute=const" + +bool +chck_buffer_compress_zlib(struct chck_buffer *buf) +{ +#if HAS_ZLIB + uLongf dsize, bsize; + dsize = bsize = compressBound(buf->size); + + void *compressed; + if (!(compressed = malloc(dsize))) + return false; + + int ret; + while ((ret = compress(compressed, &dsize, buf->buffer, buf->size)) == Z_BUF_ERROR) { + void *tmp; + if (!(tmp = chck_realloc_mul_of(compressed, bsize, 2))) + goto fail; + + compressed = tmp; + dsize = (bsize *= 2); + } + + if (unlikely(ret != Z_OK)) + goto fail; + + chck_buffer_set_pointer(buf, compressed, bsize, buf->endianess); + buf->copied = true; + + if (buf->size > dsize) + chck_buffer_resize(buf, dsize); + + return true; + +fail: + free(compressed); + return false; +#else + (void)buf; + return false; +#endif +} + +bool +chck_buffer_decompress_zlib(struct chck_buffer *buf) +{ +#if HAS_ZLIB + uLongf dsize, bsize; + + { + size_t sz; + if (unlikely(chck_mul_ofsz(buf->size, 2, &sz)) || (uLongf)sz < sz) + return false; + + dsize = bsize = sz; + } + + if (!dsize) + return false; + + void *decompressed; + if (!(decompressed = malloc(dsize))) + return false; + + int ret; + while ((ret = uncompress(decompressed, &dsize, buf->buffer, buf->size)) == Z_BUF_ERROR) { + void *tmp; + if (!(tmp = chck_realloc_mul_of(decompressed, bsize, 2))) + goto fail; + + decompressed = tmp; + dsize = (bsize *= 2); + } + + if (unlikely(ret != Z_OK)) + goto fail; + + chck_buffer_set_pointer(buf, decompressed, bsize, buf->endianess); + buf->copied = true; + + if (bsize > dsize) + chck_buffer_resize(buf, dsize); + + return true; + +fail: + free(decompressed); + return false; +#else + (void)buf; + return false; +#endif +} diff --git a/src/chck/buffer/buffer.h b/src/chck/buffer/buffer.h new file mode 100644 index 0000000..2494efe --- /dev/null +++ b/src/chck/buffer/buffer.h @@ -0,0 +1,75 @@ +#ifndef __chck_buffer__ +#define __chck_buffer__ + +#include +#include +#include +#include + +#include "endianess.h" + +enum chck_bits { + CHCK_BUFFER_B8 = sizeof(int8_t), + CHCK_BUFFER_B16 = sizeof(int16_t), + CHCK_BUFFER_B32 = sizeof(int32_t), + CHCK_BUFFER_B64 = sizeof(int64_t), +}; + +struct chck_buffer { + // pointer to current buffer and the current position + uint8_t *buffer, *curpos; + + // size of the buffer + size_t size; + + // growth step for the buffer incase writing to full buffer + size_t step; + + // endianess true == big, false == little + bool endianess; + + // copied == true, means that buffer is owned by this struct and will be freed on chck_buffer_release + bool copied; +}; + +static inline bool +chck_buffer_native_endianess(const struct chck_buffer *buf) +{ + return (chck_endianess() == buf->endianess); +} + +void chck_buffer_release(struct chck_buffer *buf); +void chck_buffer_flush(struct chck_buffer *buf); +bool chck_buffer_from_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess); +bool chck_buffer(struct chck_buffer *buf, size_t size, enum chck_endianess endianess); +void chck_buffer_set_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess); + +size_t chck_buffer_fill(const void *src, size_t size, size_t memb, struct chck_buffer *buf); +size_t chck_buffer_fill_from_file(FILE *src, size_t size, size_t memb, struct chck_buffer *buf); +size_t chck_buffer_fill_from_fd(int fd, size_t size, size_t memb, struct chck_buffer *buf); + +size_t chck_buffer_write(const void *src, size_t size, size_t nmemb, struct chck_buffer *buf); +size_t chck_buffer_write_from_file(FILE *src, size_t size, size_t nmemb, struct chck_buffer *buf); +size_t chck_buffer_write_from_fd(int fd, size_t size, size_t nmemb, struct chck_buffer *buf); + +size_t chck_buffer_read(void *dst, size_t size, size_t memb, struct chck_buffer *buf); +bool chck_buffer_read_int(void *i, enum chck_bits bits, struct chck_buffer *buf); +bool chck_buffer_read_string(char **str, size_t *len, struct chck_buffer *buf); +bool chck_buffer_read_string_of_type(char **str, size_t *len, enum chck_bits bits, struct chck_buffer *buf); + +bool chck_buffer_write_int(const void *i, enum chck_bits bits, struct chck_buffer *buf); +bool chck_buffer_write_string(const char *str, size_t len, struct chck_buffer *buf); +bool chck_buffer_write_string_of_type(const char *str, size_t len, enum chck_bits bits, struct chck_buffer *buf); + +CHCK_FORMAT(printf, 2, 3) size_t chck_buffer_write_format(struct chck_buffer *buf, const char *fmt, ...); +size_t chck_buffer_write_varg(struct chck_buffer *buf, const char *fmt, va_list args); + +ptrdiff_t chck_buffer_seek(struct chck_buffer *buf, long offset, int whence); +bool chck_buffer_resize(struct chck_buffer *buf, size_t size); + +/* -DHAS_ZLIB=1 -lz */ +bool chck_buffer_has_zlib(void); +bool chck_buffer_compress_zlib(struct chck_buffer *buf); +bool chck_buffer_decompress_zlib(struct chck_buffer *buf); + +#endif /* __chck_buffer__ */ diff --git a/src/chck/buffer/endianess.h b/src/chck/buffer/endianess.h new file mode 100644 index 0000000..54109e7 --- /dev/null +++ b/src/chck/buffer/endianess.h @@ -0,0 +1,133 @@ +#ifndef __chck_endianess__ +#define __chck_endianess__ + +#include +#include +#include +#include + +enum chck_endianess { + CHCK_ENDIANESS_LITTLE = 0, + CHCK_ENDIANESS_BIG = 1, + CHCK_ENDIANESS_NATIVE = 2, +}; + +#if defined(__clang__) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 3 && !defined(__MINGW32__) && !defined(__MINGW64__)) +# define bswap16 __builtin_bswap16 +# define bswap32 __builtin_bswap32 +# define bswap64 __builtin_bswap64 +# define HAS_BYTESWAP 1 +#elif defined(__GLIBC__) +# include +# define bswap16 __bswap_16 +# define bswap32 __bswap_32 +# define bswap64 __bswap_64 +# define HAS_BYTESWAP 1 +#elif defined(__NetBSD__) +# include +# include /* already named bswap16/32/64 */ +# define HAS_BYTESWAP 1 +#elif defined(_MSC_VER) +# define bswap16 _byteswap_ushort +# define bswap32 _byteswap_ulong +# define bswap64 _byteswap_uint64 +# define HAS_BYTESWAP 1 +#else +# define HAS_BYTESWAP 0 +#endif + +#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ + (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) || \ + defined(__BIG_ENDIAN__) || \ + defined(__ARMEB__) || \ + defined(__THUMBEB__) || \ + defined(__AARCH64EB__) || \ + defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) +// compiletime big endian +# define chck_endianess() CHCK_ENDIANESS_BIG +#elif (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + defined(__LITTLE_ENDIAN__) || \ + defined(__ARMEL__) || \ + defined(__THUMBEL__) || \ + defined(__AARCH64EL__) || \ + defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) +// compiletime little endian +# define chck_endianess() CHCK_ENDIANESS_LITTLE +#else +// runtime endianess check +static inline enum +chck_endianess chck_endianess(void) +{ + union { + uint32_t i; + char c[4]; + } bint = { 0x01020304 }; + return (bint.c[0] == 1 ? CHCK_ENDIANESS_BIG : CHCK_ENDIANESS_LITTLE); +}; +#endif + +static inline void +chck_bswap_generic(void *p, size_t size) +{ + if (size <= sizeof(uint8_t)) + return; + + assert(size <= sizeof(intmax_t)); + uint8_t b[sizeof(intmax_t)]; + memcpy(b, p, size); + + for (size_t s = 0; s < size; ++s) + memset((uint8_t*)p + s, b[size - s - 1], 1); +} + +static inline void +chck_bswap_single(void *p, size_t size) +{ +#if HAS_BYTESWAP + assert(p); + switch (size) { + case sizeof(uint32_t): + *((uint32_t*)p) = bswap32(*((uint32_t*)p)); + break; + case sizeof(uint16_t): + *((uint16_t*)p) = bswap16(*((uint16_t*)p)); + break; + case sizeof(uint64_t): + *((uint64_t*)p) = bswap64(*((uint64_t*)p)); + break; + default: + chck_bswap_generic(p, size); + break; + } +#else + chck_bswap_generic(p, size); +#endif +} + +static inline void +chck_bswap(void *v, size_t size, size_t memb) +{ + assert(v); + + for (uint8_t *p = v; size > sizeof(uint8_t) && p < (uint8_t*)v + (memb * size); p += size) + chck_bswap_single(p, size); +} + +/** define chck_bswap{16,32,64} for use **/ + +#if HAS_BYTESWAP +# define generic_swap(T, n) \ + static inline T chck_bswap##n(T v) { return bswap##n(v); } +#else +# define generic_swap(T, n) \ + static inline T chck_bswap##n(T v) { chck_bswap_generic(&v, sizeof(v)); return v; } +#endif + +generic_swap(uint16_t, 16) +generic_swap(uint32_t, 32) +generic_swap(uint64_t, 64) + +#undef generic_swap + +#endif /* __chck_endianess__ */ diff --git a/src/chck/overflow/overflow.h b/src/chck/overflow/overflow.h new file mode 100644 index 0000000..ffa489e --- /dev/null +++ b/src/chck/overflow/overflow.h @@ -0,0 +1,159 @@ +#ifndef __chck_overflow_h__ +#define __chck_overflow_h__ + +#include +#include +#include +#include +#include +#include + +/** clang feature detection. */ +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#if __GNUC__ >= 5 || __has_builtin(__builtin_add_overflow) +/** assume clang and gcc (>=5) to only have builtins for now. */ +# define add_of(a, b, r) __builtin_add_overflow(a, b, r) +# define sub_of(a, b, r) __builtin_sub_overflow(a, b, r) +# define mul_of(a, b, r) __builtin_mul_overflow(a, b, r) +# define of_attr +#else +/** else use these generics, note behaviour of these is not strictly defined. */ +# define add_of(a, b, r) ((*(r) = ((a) + (b))) < (a)) +# define sub_of(a, b, r) ((*(r) = ((a) - (b))) > (a)) +# define mul_of(a, b, r) (((*(r) = ((a) * (b))) || *(r) == 0) && ((a) != 0 && (b) > *(r) / (a))) +# if __clang__ +# define of_attr __attribute__((optnone)) // Do not optimize above checks, in most systems this will work, but not defined. +# warning "Using non compiler builtins for overflow checks, this will be undefined for signed integers" +# elif __GNUC__ +# define of_attr __attribute__((optimize("wrapv"))) // in older GCC we can make this behavior defined +# else +# warning "Using non compiler builtins for overflow checks, this will be undefined for signed integers" +# endif +#endif + +/** declare overflow functions */ + +// T = type name, n = function suffix, s = is type signed? +#define decl_generics_for_type(T, n, s) \ + of_attr static inline bool chck_add_of##n(T a, T b, T *r) { assert((!s || b >= 0) && r); return add_of(a, b, r); } \ + of_attr static inline bool chck_sub_of##n(T a, T b, T *r) { assert((!s || b >= 0) && r); return sub_of(a, b, r); } \ + of_attr static inline bool chck_mul_of##n(T a, T b, T *r) { assert(r); return mul_of(a, b, r); } + +// UT = unsigned type, un = unsigned function suffix +// T = signed type, n = signed function suffix +#define decl_generics(UT, un, T, n) \ + decl_generics_for_type(UT, un, false) \ + decl_generics_for_type(T, n, true) + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" + +decl_generics_for_type(size_t, sz, false) +decl_generics(uint64_t, u64, int64_t, 64) +decl_generics(uint32_t, u32, int32_t, 32) +decl_generics(uint16_t, u16, int16_t, 16) +decl_generics(uint8_t, u8, int8_t, 8) + +#pragma GCC diagnostic pop + +#undef decl_generics +#undef decl_generics_for_type +#undef add_of +#undef sub_of +#undef mul_of +#undef of_attr + +CHCK_MALLOC static inline void* +chck_malloc_add_of(size_t size, size_t add) +{ + size_t r; + if (unlikely(chck_add_ofsz(size, add, &r)) || !r) + return NULL; + + return malloc(r); +} + +CHCK_MALLOC static inline void* +chck_malloc_sub_of(size_t size, size_t sub) +{ + size_t r; + if (unlikely(chck_sub_ofsz(size, sub, &r)) || !r) + return NULL; + + return malloc(r); +} + +CHCK_MALLOC static inline void* +chck_malloc_mul_of(size_t size, size_t mul) +{ + size_t r; + if (unlikely(chck_mul_ofsz(size, mul, &r)) || !r) + return NULL; + + return malloc(r); +} + +CHCK_MALLOC static inline void* +chck_calloc_of(size_t nmemb, size_t size) +{ + size_t r; + if (unlikely(chck_mul_ofsz(nmemb, size, &r)) || !r) + return NULL; + + return calloc(nmemb, size); +} + +CHCK_MALLOC static inline void* +chck_calloc_add_of(size_t size, size_t add) +{ + size_t r; + if (unlikely(chck_add_ofsz(size, add, &r)) || !r) + return NULL; + + return calloc(1, r); +} + +CHCK_MALLOC static inline void* +chck_calloc_sub_of(size_t size, size_t sub) +{ + size_t r; + if (unlikely(chck_sub_ofsz(size, sub, &r)) || !r) + return NULL; + + return calloc(1, r); +} + +static inline void* +chck_realloc_add_of(void *ptr, size_t size, size_t add) +{ + size_t r; + if (unlikely(chck_add_ofsz(size, add, &r)) || !r) + return NULL; + + return realloc(ptr, r); +} + +static inline void* +chck_realloc_sub_of(void *ptr, size_t size, size_t sub) +{ + size_t r; + if (unlikely(chck_sub_ofsz(size, sub, &r)) || !r) + return NULL; + + return realloc(ptr, r); +} + +static inline void* +chck_realloc_mul_of(void *ptr, size_t size, size_t mul) +{ + size_t r; + if (unlikely(chck_mul_ofsz(size, mul, &r)) || !r) + return NULL; + + return realloc(ptr, r); +} + +#endif /* __chck_overflow_h__ */ diff --git a/src/pi9.pc.in b/src/pi9.pc.in index 9928702..3bb6292 100644 --- a/src/pi9.pc.in +++ b/src/pi9.pc.in @@ -6,6 +6,5 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Name: @PROJECT_NAME@ Description: 9p server abstraction library Version: @PROJECT_VERSION@ -Requires.private: chck Libs: -L${libdir} -lpi9 Cflags: -I${includedir}