/*------------------------------------------------------------------ * safeclib_private.h - Internal library references * * 2012, Jonathan Toppins * 2017 Reini Urban * * Copyright (c) 2012, 2013 by Cisco Systems, Inc * Copyright (c) 2017 Reini Urban * All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. *------------------------------------------------------------------ */ #ifndef __SAFECLIB_PRIVATE_H__ #define __SAFECLIB_PRIVATE_H__ #ifndef DEBUG #define NDEBUG #endif #include "config.h" #ifdef __KERNEL__ /* linux kernel environment */ #include #include #include /* Needed since the switch to time64_t */ #if defined CONFIG_COMPAT_32BIT_TIME && defined _LINUX_TIME64_H && defined __VDSO_TIME32_H #define time_t old_time32_t #endif #define RCNEGATE(x) (-(x)) #ifndef SAFECLIB_HAVE_C99 #define slprintf #define sldebug_printf #else #define slprintf(...) printk(KERN_EMERG __VA_ARGS__) #define slabort() #ifdef DEBUG #define sldebug_printf(...) printk(KERN_DEBUG __VA_ARGS__) #endif #endif #else /* !__KERNEL__ */ #if defined(__CYGWIN__) && defined(__x86_64) #define HAVE_CYGWIN64 #endif #if defined(HAVE_SECURE_GETENV) #ifdef _GNU_SOURCE #define _GNU_SOURCE_WAS_DEFINED #else #define _GNU_SOURCE #endif #endif #include #ifdef STDC_HEADERS #include #include #if defined(HAVE_SECURE_GETENV) #ifndef _GNU_SOURCE_WAS_DEFINED #undef _GNU_SOURCE #else #undef _GNU_SOURCE_WAS_DEFINED #endif #endif #include #include #else /* newlib, cygwin64 has no STDC_HEADERS */ #ifdef HAVE_STDLIB_H #ifdef HAVE_SECURE_GETENV #define _GNU_SOURCE #endif #include #else /* cygwin64 */ #ifdef __GNUC__ void abort(void) __attribute__((noreturn)); #endif #endif #ifdef HAVE_CTYPE_H #include #endif #ifdef HAVE_MALLOC_H #include #endif #endif #ifdef HAVE_STRING_H #if !defined STDC_HEADERS && defined HAVE_MEMORY_H #include #endif #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #if defined(__STDC_WANT_LIB_EXT1__) && (__STDC_WANT_LIB_EXT1__ >= 1) #define WANT_C11 #endif #ifndef HAVE_UINTPTR_T typedef unsigned long uintptr_t; #endif /* mingw 3.4 */ #ifndef EOVERFLOW #ifdef _WIN32 #define EOVERFLOW 132 #else #define EOVERFLOW 75 #endif #endif #ifndef HAVE_STRNSTR #define strnstr(a, b, c) strstr(a, b) /* broken in cygwin/newlib until Aug 2017 */ #elif !defined HAVE_STRNSTR_OK #define strnstr(a, b, c) strstr(a, b) #endif /* Only needed for the linux kernel */ #define EXPORT_SYMBOL(sym) #define RCNEGATE(x) (x) #define slabort() abort() #ifndef SAFECLIB_HAVE_C99 #define slprintf printf #define sldebug_printf printf #else #define slprintf(...) fprintf(stderr, __VA_ARGS__) #ifdef DEBUG #define sldebug_printf(...) printf(__VA_ARGS__) #endif #endif #if __GNUC__ >= 3 #define _expect(expr, value) __builtin_expect((expr), (value)) #define INLINE static inline #else #define _expect(expr, value) (expr) #define INLINE static #endif #ifndef likely #define likely(expr) _expect((long)((expr) != 0), 1) #define unlikely(expr) _expect((long)((expr) != 0), 0) #endif #endif /* __KERNEL__ */ /* for a proper asctime string with 26 digits, i.e. max 4 digit year. 01.01.10000 00:00 */ #ifndef MAX_TIME_T_STR #if SIZEOF_TIME_T < 8 #define MAX_TIME_T_STR (0x7fffffff) #else #define MAX_TIME_T_STR (313360441200L) #endif #endif #define _UNICODE_MAX 0x10ffff #ifndef sldebug_printf #ifdef SAFECLIB_HAVE_C99 #define sldebug_printf(...) #else #define sldebug_printf printf #endif #endif #ifdef _WIN32 #define EXPORT __declspec(dllexport) #else #define EXPORT #endif #include "safe_config.h" #include "safe_lib_errno.h" #include "safe_types.h" #include "safe_str_lib.h" #include "str/safe_str_constraint.h" #include "safe_mem_lib.h" #include "mem/safe_mem_constraint.h" #include "safe_lib.h" /* needed for -Wcast-align */ #if defined(__clang__) || defined(__clang) || \ (defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) #define GCC_DIAG_PRAGMA(x) _Pragma(#x) /* clang has "clang diagnostic" pragmas, but also understands gcc. */ #define GCC_DIAG_IGNORE(x) \ _Pragma("GCC diagnostic push") GCC_DIAG_PRAGMA(GCC diagnostic ignored #x) #define GCC_DIAG_RESTORE _Pragma("GCC diagnostic pop") #else #define GCC_DIAG_IGNORE(w) #define GCC_DIAG_RESTORE #endif #if defined(DEBUG) && defined(SAFECLIB_HAVE_C99) && defined(__KERNEL__) #define debug_printf(...) printk(KERN_DEBUG __VA_ARGS__) #elif defined(SAFECLIB_HAVE_C99) #define debug_printf(...) fprintf(STDERR, __VA_ARGS__) #else #define debug_printf printf #endif /* TODO: do we need the builtin's? rather just use __bnd... which is defined on CHKP and MPX. requires -fcheck-pointer-bounds -mmpx */ #if defined(HAVE___BUILTIN___BND_CHK_PTR_BOUNDS) && defined(__CHKP__) && \ defined(__MPX__) #define BND_CHK_PTR_BOUNDS(dest, count) \ __builtin___bnd_chk_ptr_bounds(dest, count) #elif defined(HAVE___BND_CHK_PTR_BOUNDS) && defined(__CHKP__) && \ defined(__MPX__) #define BND_CHK_PTR_BOUNDS(dest, count) __bnd_chk_ptr_bounds(dest, count) #else #define BND_CHK_PTR_BOUNDS(dest, count) #endif #ifdef HAVE_ERROR_DMAX #define RETURN_ESLEWRNG return (RCNEGATE(ESLEWRNG)) #else #define RETURN_ESLEWRNG #endif #ifdef HAVE_ERROR_DMAX #define RETURN_ESLEWRNG return (RCNEGATE(ESLEWRNG)) #else #define RETURN_ESLEWRNG #endif #define CHK_DEST_NULL(func) \ if (unlikely(dest == NULL)) { \ invoke_safe_str_constraint_handler(func ": dest is null", \ (void *)dest, ESNULLP); \ return RCNEGATE(ESNULLP); \ } #define CHK_DMAX_ZERO(func) \ if (unlikely(dmax == 0)) { \ invoke_safe_str_constraint_handler(func ": dmax is 0", (void *)dest, \ ESZEROL); \ return RCNEGATE(ESZEROL); \ } #define CHK_DEST_MEM_NULL(func) \ if (unlikely(dest == NULL)) { \ invoke_safe_mem_constraint_handler(func ": dest is null", \ (void *)dest, ESNULLP); \ return RCNEGATE(ESNULLP); \ } #define CHK_DMAX_MEM_ZERO(func) \ if (unlikely(dmax == 0)) { \ invoke_safe_mem_constraint_handler(func ": dmax is 0", (void *)dest, \ ESZEROL); \ return RCNEGATE(ESZEROL); \ } #define CHK_DMAX_MAX(func, max) \ if (unlikely(dmax > (max))) { \ invoke_safe_str_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } #define CHK_DMAX_MEM_MAX(func, max) \ if (unlikely(dmax > (max))) { \ invoke_safe_mem_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } /* for known dest size, we should have already errored at compile-time before. anyway, for known dest size check overflows in detail. does CLEAR */ #if defined(HAVE_WARN_DMAX) #define CHK_DEST_OVR_CLEAR(func, destbos) \ if (unlikely(dmax != destbos)) { \ if (unlikely(dmax > destbos)) { \ if (dmax > RSIZE_MAX_STR) { \ handle_error(dest, destbos, func ": dmax exceeds max", \ ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ return handle_str_bos_overload(func ": dmax exceeds dest", \ (char *)dest, destbos); \ } \ } \ handle_str_bos_chk_warn(func, (char *)dest, dmax, destbos); \ RETURN_ESLEWRNG; \ } #define CHK_DEST_OVR(func, destbos) \ if (unlikely(dmax != destbos)) { \ if (unlikely(dmax > destbos)) { \ if (dmax > RSIZE_MAX_STR) { \ invoke_safe_str_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ invoke_safe_str_constraint_handler(func ": dmax exceeds dest", \ (void *)dest, EOVERFLOW); \ return RCNEGATE(EOVERFLOW); \ } \ } \ handle_str_bos_chk_warn(func, (char *)dest, dmax, destbos); \ RETURN_ESLEWRNG; \ } #define CHK_DESTW_OVR(func, destsz, destbos) \ if (unlikely(destsz != destbos)) { \ if (unlikely(destsz > destbos)) { \ if (dmax > RSIZE_MAX_WSTR) { \ invoke_safe_str_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ invoke_safe_str_constraint_handler(func ": dmax exceeds dest", \ (void *)dest, EOVERFLOW); \ return RCNEGATE(EOVERFLOW); \ } \ } \ handle_str_bos_chk_warn(func, (char *)dest, dmax, \ destbos / sizeof(wchar_t)); \ RETURN_ESLEWRNG; \ } #define CHK_DESTW_OVR_CLEAR(func, destsz, destbos) \ if (unlikely(destsz != destbos)) { \ if (unlikely(destsz > destbos)) { \ if (dmax > RSIZE_MAX_WSTR) { \ handle_werror(dest, destbos / sizeof(wchar_t), \ func ": dmax exceeds max", ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ handle_werror(dest, destbos / sizeof(wchar_t), \ func ": dmax exceeds dest", EOVERFLOW); \ return RCNEGATE(EOVERFLOW); \ } \ } \ handle_str_bos_chk_warn(func, (char *)dest, dmax, \ destbos / sizeof(wchar_t)); \ RETURN_ESLEWRNG; \ } #define CHK_DEST_OVR_BOOL(func, destbos) \ if (unlikely(dmax != destbos)) { \ if (unlikely(dmax > destbos)) { \ if (dmax > RSIZE_MAX_STR) { \ invoke_safe_str_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ } else { \ invoke_safe_str_constraint_handler(func ": dmax exceeds dest", \ (void *)dest, EOVERFLOW); \ } \ return false; \ } \ handle_str_bos_chk_warn(func, (char *)dest, dmax, destbos); \ } /* does no clear */ #define CHK_DEST_MEM_OVR(func, destbos) \ if (unlikely(dmax != destbos)) { \ if (unlikely(dmax > destbos)) { \ if (dmax > RSIZE_MAX_MEM) { \ invoke_safe_mem_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ invoke_safe_mem_constraint_handler(func ": dmax exceeds dest", \ (void *)dest, EOVERFLOW); \ return RCNEGATE(EOVERFLOW); \ } \ } \ handle_mem_bos_chk_warn(func, (void *)dest, dmax, destbos); \ RETURN_ESLEWRNG; \ } #else /* WARN_DMAX */ #define CHK_DEST_OVR_CLEAR(func, destbos) \ if (unlikely(dmax > destbos)) { \ if (dmax > RSIZE_MAX_STR) { \ handle_error(dest, destbos, func ": dmax exceeds max", ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ return handle_str_bos_overload(func ": dmax exceeds dest", \ (char *)dest, destbos); \ } \ } #define CHK_DEST_OVR(func, destbos) \ if (unlikely(dmax > destbos)) { \ if (dmax > RSIZE_MAX_STR) { \ invoke_safe_str_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ invoke_safe_str_constraint_handler(func ": dmax exceeds dest", \ (void *)dest, EOVERFLOW); \ return RCNEGATE(EOVERFLOW); \ } \ } #define CHK_DESTW_OVR(func, destsz, destbos) \ if (unlikely(destsz > destbos)) { \ if (dmax > RSIZE_MAX_WSTR) { \ invoke_safe_str_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ invoke_safe_str_constraint_handler(func ": dmax exceeds dest", \ (void *)dest, EOVERFLOW); \ return RCNEGATE(EOVERFLOW); \ } \ } #define CHK_DESTW_OVR_CLEAR(func, destsz, destbos) \ if (unlikely(destsz > destbos)) { \ if (dmax > RSIZE_MAX_WSTR) { \ handle_werror(dest, destbos / sizeof(wchar_t), \ func ": dmax exceeds max", ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ handle_werror(dest, destbos / sizeof(wchar_t), \ func ": dmax exceeds dest", EOVERFLOW); \ return RCNEGATE(EOVERFLOW); \ } \ } #define CHK_DEST_OVR_BOOL(func, destbos) \ if (unlikely(dmax > destbos)) { \ if (dmax > RSIZE_MAX_STR) { \ invoke_safe_str_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ } else { \ invoke_safe_str_constraint_handler(func ": dmax exceeds dest", \ (void *)dest, EOVERFLOW); \ } \ return false; \ } #define CHK_DEST_MEM_OVR(func, destbos) \ if (unlikely(dmax > destbos)) { \ if (dmax > RSIZE_MAX_MEM) { \ invoke_safe_mem_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } else { \ invoke_safe_mem_constraint_handler(func ": dmax exceeds dest", \ (void *)dest, EOVERFLOW); \ return RCNEGATE(EOVERFLOW); \ } \ } #endif #define CHK_SRC_NULL(func, src) \ if (unlikely(src == NULL)) { \ invoke_safe_str_constraint_handler(func ": " _XSTR(src) " is null", \ (void *)src, ESNULLP); \ return RCNEGATE(ESNULLP); \ } #define CHK_SRC_NULL_CLEAR(func, src) \ if (unlikely(src == NULL)) { \ handle_error(dest, _BOS_KNOWN(dest) ? BOS(dest) : dmax, \ func ": " _XSTR(src) " is null", ESNULLP); \ return RCNEGATE(ESNULLP); \ } #define CHK_SRCW_NULL_CLEAR(func, src) \ if (unlikely(src == NULL)) { \ handle_werror(dest, \ _BOS_KNOWN(dest) ? BOS(dest) / sizeof(wchar_t) : dmax, \ func ": " _XSTR(src) " is null", ESNULLP); \ return RCNEGATE(ESNULLP); \ } #if defined(HAVE_WARN_DMAX) && defined(HAVE_ERROR_DMAX) #define CHK_SRC_OVR_CLEAR(func, src, slen, MAX) \ if (_BOS_KNOWN(src)) { \ if (unlikely(_BOS_OVR_N(src, slen))) { \ return handle_str_bos_overload( \ func, (char *)dest, _BOS_KNOWN(dest) ? BOS(dest) : dmax); \ } else if (_BOS_CHK_N(src, slen)) { \ handle_str_src_bos_chk_warn(func, (char *)dest, slen, BOS(src), \ _XSTR(src), _XSTR(slen)); \ return RCNEGATE(ESLEWRNG); \ } \ BND_CHK_PTR_BOUNDS(src, slen); \ } #elif defined(HAVE_WARN_DMAX) #define CHK_SRC_OVR_CLEAR(func, src, slen, MAX) \ if (_BOS_KNOWN(src)) { \ if (unlikely(_BOS_OVR_N(src, slen))) { \ return handle_str_bos_overload( \ func, (char *)dest, _BOS_KNOWN(dest) ? BOS(dest) : dmax); \ } else if (_BOS_CHK_N(src, slen)) { \ handle_str_src_bos_chk_warn(func, (char *)dest, slen, BOS(src), \ _XSTR(src), _XSTR(slen)); \ } \ BND_CHK_PTR_BOUNDS(src, slen); \ } #else #define CHK_SRC_OVR_CLEAR(func, src, slen, MAX) \ if (_BOS_KNOWN(src)) { \ if (unlikely(_BOS_OVR_N(src, slen))) { \ return handle_str_bos_overload( \ func, (char *)dest, _BOS_KNOWN(dest) ? BOS(dest) : dmax); \ } \ BND_CHK_PTR_BOUNDS(src, slen); \ } #endif #define CHK_SLEN_MAX_CLEAR(func, slen, max) \ if (unlikely(slen > (max))) { \ handle_error(dest, \ _BOS_KNOWN(dest) ? BOS(dest) : strnlen_s(dest, dmax), \ func ": " _XSTR(slen) " exceeds max", ESLEMAX); \ return RCNEGATE(ESLEMAX); \ } #define CHK_SLEN_MAX_NOSPC_CLEAR(func, slen, max) \ if (unlikely(slen > dmax)) { \ errno_t error = slen > max ? ESLEMAX : ESNOSPC; \ handle_error(dest, \ _BOS_KNOWN(dest) ? BOS(dest) : strnlen_s(dest, dmax), \ func ": " _XSTR(slen) " exceeds max", error); \ return RCNEGATE(error); \ } #define CHK_SRC_MEM_NULL_CLEAR(func, src) \ if (unlikely(src == NULL)) { \ handle_mem_error(dest, _BOS_KNOWN(dest) ? BOS(dest) : dmax, \ func ": " _XSTR(src) " is null", ESNULLP); \ return RCNEGATE(ESNULLP); \ } #define CHK_SLEN_MEM_MAX_NOSPC_CLEAR(func, slen, max) \ if (unlikely(slen > dmax)) { \ errno_t error = slen > max ? ESLEMAX : ESNOSPC; \ handle_mem_error((void *)dest, _BOS_KNOWN(dest) ? BOS(dest) : dmax, \ func ": " _XSTR(slen) " exceeds max", error); \ return RCNEGATE(error); \ } #define CHK_DEST_DMAX_BOOL(func, max) \ if (unlikely(dest == NULL)) { \ invoke_safe_str_constraint_handler(func ": dest is null", NULL, \ ESNULLP); \ return false; \ } \ if (unlikely(dmax == 0)) { \ invoke_safe_str_constraint_handler(func ": dmax is 0", (void *)dest, \ ESZEROL); \ return false; \ } \ if (destbos == BOS_UNKNOWN) { \ if (unlikely(dmax > (max))) { \ invoke_safe_str_constraint_handler(func ": dmax exceeds max", \ (void *)dest, ESLEMAX); \ return false; \ } \ BND_CHK_PTR_BOUNDS(dest, dmax); \ } else { \ CHK_DEST_OVR_BOOL(func, destbos) \ } /* Comparing pointers from two separately allocated objects is forbidden as per 6.5.8 C11 except when using (in)equality. GH #51 */ #define CHK_OVRLP(dp, dlen, sp, slen) \ (((uintptr_t)dp >= (uintptr_t)sp) && \ ((uintptr_t)dp < (uintptr_t)(sp + slen))) || \ (((uintptr_t)dp < (uintptr_t)sp) && \ ((uintptr_t)sp < (uintptr_t)(dp + dlen))) /* but allow dp==sp */ #define CHK_OVRLP_BUTSAME(dp, dlen, sp, slen) \ (((uintptr_t)dp > (uintptr_t)sp) && \ ((uintptr_t)dp < (uintptr_t)(sp + slen))) || \ (((uintptr_t)dp < (uintptr_t)sp) && \ ((uintptr_t)sp < (uintptr_t)(dp + dlen))) /* platform quirks */ #ifndef SAFECLIB_DISABLE_WCHAR /* mingw32 3.15.2 */ #if defined(_WIN32) && defined(__MINGW32_MAJOR_VERSION) && \ !defined(__STRICT_ANSI__) && !defined(__MINGW64_VERSION_MAJOR) #define vswprintf(dest, dmax, fmt, ap) vswprintf(dest, fmt, ap) #endif /* mingw64 3.0.1 has strtok_s, wcstok_s, and vsnprintf_s, which we patch in the tests. */ #if SIZEOF_WCHAR_T > 2 #define _dec_w16(src) *(src) #define _ENC_W16(dest, dmax, cp) \ *(dest)++ = (cp); \ (dmax)-- #else /* windows, cygwin + 32bit aix, solaris */ /* convert surrogate pair to unicode codepoint */ EXTERN uint32_t _dec_w16(wchar_t *src); /* convert unicode codepoint to surrogate pair, advancing dest */ #define _ENC_W16(dest, dmax, cp) \ if (unlikely((cp) < 0x10000)) { \ *(dest)++ = (cp); \ (dmax)--; \ } else { \ *dest++ = ((cp) >> 10) + 0xd7c0; \ *dest++ = ((cp)&0x3ff) + 0xdc00; \ (dmax)--; \ (dmax)--; \ } #endif /* is start of a surrogate pair? */ #define _IS_W16(cp) ((cp) >= 0xd800 && (cp) < 0xdc00) EXTERN errno_t _towfc_single(wchar_t *restrict dest, const uint32_t src); EXPORT uint32_t _towcase(uint32_t wc, int lower); EXPORT uint32_t _towupper(uint32_t wc); #ifndef HAVE_TOWLOWER EXTERN wint_t towlower(wint_t wc); #endif /* from wcsnorm_s.c */ EXTERN int _decomp_s(wchar_t *restrict dest, rsize_t dmax, const uint32_t cp, const bool iscompat); #endif /* SAFECLIB_DISABLE_WCHAR */ #endif /* __SAFECLIB_PRIVATE_H__ */