diff --git a/include/os/windows/spl/sys/kmem.h b/include/os/windows/spl/sys/kmem.h index f457df156439..9993b8419b94 100644 --- a/include/os/windows/spl/sys/kmem.h +++ b/include/os/windows/spl/sys/kmem.h @@ -61,10 +61,30 @@ extern uint64_t physmem; */ #define MALLOC(A, C, S, T, F) \ - (A) = (C)ExAllocatePoolWithTag(NonPagedPoolNx, (S), '!SFZ') + (A) = (C)ExAllocatePoolUninitialized(NonPagedPoolNx, (S), '!SFZ') #define FREE(A, T) \ ExFreePoolWithTag((A), '!SFZ') +/* + * Centralizes the "allocate uninitialized, then zero on success" + * pattern used throughout the Windows port, in one place, so the + * allocation size can never drift between the alloc call and the + * zero call (two historical call sites had exactly that bug - see + * zfs_windows_zvol.c's zvol_start() and zfs_vnops_windows.c's + * pnp_query_id(), before this was centralized). A real function, + * not a macro: a macro that referenced its Size argument twice would + * silently reintroduce the same double-evaluation bug for any future + * caller passing a computed expression. + */ +static __inline PVOID +spl_ExAllocatePoolZero(POOL_TYPE PoolType, SIZE_T Size, ULONG Tag) +{ + PVOID ptr = ExAllocatePoolUninitialized(PoolType, Size, Tag); + if (ptr != NULL) + RtlZeroMemory(ptr, Size); + return (ptr); +} + // Work around symbol collisions in XNU #define kmem_alloc(size, kmflags) zfs_kmem_alloc((size), (kmflags)) #define kmem_zalloc(size, kmflags) zfs_kmem_zalloc((size), (kmflags)) diff --git a/include/os/windows/spl/sys/types.h b/include/os/windows/spl/sys/types.h index 40cb9b22a3a4..db7b1fb545e8 100644 --- a/include/os/windows/spl/sys/types.h +++ b/include/os/windows/spl/sys/types.h @@ -94,12 +94,106 @@ typedef uintptr_t pc_t; #include #include #include +#include - -#define snprintf _snprintf +/* + * Kernel-mode _snprintf() returns -1 on truncation (not the would-be + * length) and does not NUL-terminate the buffer on truncation, unlike + * standard snprintf(). Portable ZFS/SPL code assumes real snprintf() + * semantics, so give it those semantics here rather than the raw + * deprecated function. + * + * There is no _vscprintf() in ntoskrnl.lib, and ntstrsafe.h's + * String RtlStringCchPrintfEx family cannot measure a formatted + * string's length without a real, non-zero destination buffer (a + * cchDest of 0 short-circuits before formatting even happens) - so + * "how long would this be" can only be discovered by actually + * formatting into a real, possibly-grown, scratch buffer. + * + * spl_vsnprintf() is implemented out-of-line in + * module/os/windows/spl/spl-kmem.c, NOT as a static inline here, + * because that implementation needs kmem_alloc()/kmem_free() - + * sys/kmem.h itself #includes sys/types.h, so an inline definition + * here could never see kmem_alloc()'s declaration without an + * unsupportable circular include. + */ +extern int spl_vsnprintf(char *buf, size_t size, const char *fmt, + va_list args); + +static __inline int +spl_snprintf(char *buf, size_t size, const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = spl_vsnprintf(buf, size, fmt, args); + va_end(args); + return (ret); +} + +#define snprintf spl_snprintf #define vprintf(...) vKdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, \ __VA_ARGS__)) -#define vsnprintf _vsnprintf +/* + * No #define vsnprintf here (unlike snprintf above): CodeQL's + * extended-deprecated-apis check flags macro invocations by the + * macro's own name against Microsoft's banned-API list, regardless of + * what the macro expands to - "vsnprintf" (no underscore) is on that + * list, "snprintf" is not. A macro named vsnprintf can never pass the + * check no matter its target, so every caller below calls + * spl_vsnprintf directly instead of going through a same-named macro. + */ + +/* + * Kernel-mode strncpy() does not NUL-terminate if strlen(src) >= n, + * and zero-fills the whole remainder of the buffer if strlen(src) < n + * - neither behavior is depended on by any of this codebase's callers. + * strlcpy() is the semantically-closest safe replacement (always + * terminates, never overflows) but has no kernel-linkable + * implementation here, so provide one - mirrors lib/libspl/strlcpy.c's + * existing user-mode algorithm exactly. Callers pass n == the size of + * the destination buffer (or the intended-substring-length + 1), + * unlike strncpy's n == copy-length - not a drop-in same-args swap. + */ +static __inline size_t +spl_strlcpy(char *dst, const char *src, size_t dstsize) +{ + size_t srclen = strlen(src); + size_t copied = (srclen < dstsize) ? srclen : dstsize - 1; + + if (dstsize != 0) { + memcpy(dst, src, copied); + dst[copied] = '\0'; + } + return (srclen); +} + +/* + * strcat() has no size parameter at all - unbounded by construction. + * strlcat() is the closest safe replacement (always terminates, never + * overflows, return value is the total length it tried to create) but + * - same as strlcpy() - has no kernel-linkable implementation here. + * Mirrors lib/libspl/strlcat.c's existing user-mode algorithm exactly. + */ +static __inline size_t +spl_strlcat(char *dst, const char *src, size_t dstsize) +{ + char *df = dst; + size_t left = dstsize; + size_t l1, l2 = strlen(src), copied; + + while (left-- != 0 && *df != '\0') + df++; + l1 = df - dst; + if (dstsize == l1) + return (l1 + l2); + + copied = (l1 + l2 >= dstsize) ? dstsize - l1 - 1 : l2; + memcpy(dst + l1, src, copied); + dst[l1 + copied] = '\0'; + return (l1 + l2); +} #ifndef ULLONG_MAX #define ULLONG_MAX (~0ULL) diff --git a/lib/libspl/include/os/windows/sys/types.h b/lib/libspl/include/os/windows/sys/types.h index 10090c77652a..fb0e153febff 100644 --- a/lib/libspl/include/os/windows/sys/types.h +++ b/lib/libspl/include/os/windows/sys/types.h @@ -228,4 +228,47 @@ typedef uint64_t zoff_t; #include #endif +#include +#include + +/* + * Mirrors include/os/windows/spl/sys/types.h's kernel-mode shims of the + * same name. Several shared module/zfs, module/icp, and module/lua + * source files (built both into the ZFSin kernel driver and into + * user-mode libzpool/libicp/zlib here) call these directly by name, not + * through a portable macro. In user mode, real strlcpy/strlcat + * (lib/libspl) and real, C99-conformant UCRT vsnprintf are already + * available, so these are simple passthroughs - no downlevel- + * unavailability workaround is needed here, unlike the kernel version. + */ +static __inline size_t +spl_strlcpy(char *dst, const char *src, size_t dstsize) +{ + return (strlcpy(dst, src, dstsize)); +} + +static __inline size_t +spl_strlcat(char *dst, const char *src, size_t dstsize) +{ + return (strlcat(dst, src, dstsize)); +} + +static __inline int +spl_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + return (vsnprintf(buf, size, fmt, args)); +} + +static __inline int +spl_snprintf(char *buf, size_t size, const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = spl_vsnprintf(buf, size, fmt, args); + va_end(args); + return (ret); +} + #endif diff --git a/lib/libzfs/CMakeLists.txt b/lib/libzfs/CMakeLists.txt index 488bc9826742..5fc68999915e 100644 --- a/lib/libzfs/CMakeLists.txt +++ b/lib/libzfs/CMakeLists.txt @@ -20,12 +20,24 @@ add_library(libzfs os/windows/libzfs_util_os.c ) -#variable_watch(CRYPTO_STATIC) -# set(CRYPTO_STATIC "notset") -set(CMAKE_FIND_DEBUG_MODE TRUE) -find_library(CRYPTO_STATIC_TEST +# find_package(OpenSSL)'s own LIB_EAY_DEBUG/LIB_EAY_RELEASE search (see +# contrib/windows/cmake/FindOpenSSL.cmake) correctly locates the CRT- +# matched static libs, but nothing in this tree actually links against +# OPENSSL_CRYPTO_LIBRARY - hence this direct find_library, picking the +# variant that matches CMAKE_BUILD_TYPE (a fresh search hardcoded to the +# MTd/debug name here would silently link the debug-CRT crypto lib into +# a Release build too). Cache variable name changed from the old +# CRYPTO_STATIC_TEST so this re-searches on the next configure rather +# than reusing a stale cached path from before this logic existed. +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(_libzfs_openssl_crypto_name libcrypto64MTd) +else() + set(_libzfs_openssl_crypto_name libcrypto64MT) +endif() + +find_library(LIBZFS_OPENSSL_CRYPTO NAMES - libcrypto64MTd + ${_libzfs_openssl_crypto_name} NAMES_PER_DIR HINTS "C:/Program Files/OpenSSL-Win64/lib/VC/static" @@ -33,7 +45,8 @@ find_library(CRYPTO_STATIC_TEST lib REQUIRED ) +unset(_libzfs_openssl_crypto_name) target_include_directories(libzfs PRIVATE "${CMAKE_SOURCE_DIR}/lib/libzfs") target_link_libraries(libzfs PUBLIC libpthread zlib libzutil libshare libzfs_core libnvpair libuutil) -target_link_libraries(libzfs PRIVATE Crypt32.lib ${CRYPTO_STATIC_TEST}) +target_link_libraries(libzfs PRIVATE Crypt32.lib ${LIBZFS_OPENSSL_CRYPTO}) diff --git a/lib/os/windows/zlib-1.2.3/gzio.c b/lib/os/windows/zlib-1.2.3/gzio.c index 8a8181c80b31..5c7ed4aaf16b 100644 --- a/lib/os/windows/zlib-1.2.3/gzio.c +++ b/lib/os/windows/zlib-1.2.3/gzio.c @@ -132,7 +132,7 @@ gz_open( if (s->path == NULL) { return (destroy(s), (gzFile)Z_NULL); } - strcpy(s->path, path); /* do this early for debugging */ + spl_strlcpy(s->path, path, strlen(path) + 1); /* do this early for debugging */ s->mode = '\0'; do { @@ -234,7 +234,7 @@ gzdopen( if (fd < 0) return ((gzFile)Z_NULL); - sprintf(name, "", fd); /* for debugging */ + spl_snprintf(name, sizeof (name), "", fd); /* for debugging */ return (gz_open(name, mode, fd)); } @@ -666,7 +666,7 @@ gzprintf(gzFile file, const char *format, /* args */ ...) va_end(va); len = strlen(buf); #else - len = vsnprintf(buf, sizeof (buf), format, va); + len = spl_vsnprintf(buf, sizeof (buf), format, va); va_end(va); #endif #endif @@ -1094,9 +1094,9 @@ gzerror( s->msg = (char *)ALLOC(strlen(s->path) + strlen(m) + 3); if (s->msg == Z_NULL) return ((const char *)ERR_MSG(Z_MEM_ERROR)); - strcpy(s->msg, s->path); - strcat(s->msg, ": "); - strcat(s->msg, m); + spl_strlcpy(s->msg, s->path, strlen(s->path) + strlen(m) + 3); + spl_strlcat(s->msg, ": ", strlen(s->path) + strlen(m) + 3); + spl_strlcat(s->msg, m, strlen(s->path) + strlen(m) + 3); return ((const char *)s->msg); } diff --git a/module/icp/core/kcf_mech_tabs.c b/module/icp/core/kcf_mech_tabs.c index 2642b317d698..d9f185eea8b0 100644 --- a/module/icp/core/kcf_mech_tabs.c +++ b/module/icp/core/kcf_mech_tabs.c @@ -177,72 +177,72 @@ kcf_init_mech_tabs(void) /* Then the pre-defined mechanism entries */ /* Two digests */ - (void) strncpy(kcf_digest_mechs_tab[0].me_name, SUN_CKM_MD5, + (void) spl_strlcpy(kcf_digest_mechs_tab[0].me_name, SUN_CKM_MD5, CRYPTO_MAX_MECH_NAME); kcf_digest_mechs_tab[0].me_threshold = kcf_md5_threshold; - (void) strncpy(kcf_digest_mechs_tab[1].me_name, SUN_CKM_SHA1, + (void) spl_strlcpy(kcf_digest_mechs_tab[1].me_name, SUN_CKM_SHA1, CRYPTO_MAX_MECH_NAME); kcf_digest_mechs_tab[1].me_threshold = kcf_sha1_threshold; /* The symmetric ciphers in various modes */ - (void) strncpy(kcf_cipher_mechs_tab[0].me_name, SUN_CKM_DES_CBC, + (void) spl_strlcpy(kcf_cipher_mechs_tab[0].me_name, SUN_CKM_DES_CBC, CRYPTO_MAX_MECH_NAME); kcf_cipher_mechs_tab[0].me_threshold = kcf_des_threshold; - (void) strncpy(kcf_cipher_mechs_tab[1].me_name, SUN_CKM_DES3_CBC, + (void) spl_strlcpy(kcf_cipher_mechs_tab[1].me_name, SUN_CKM_DES3_CBC, CRYPTO_MAX_MECH_NAME); kcf_cipher_mechs_tab[1].me_threshold = kcf_des3_threshold; - (void) strncpy(kcf_cipher_mechs_tab[2].me_name, SUN_CKM_DES_ECB, + (void) spl_strlcpy(kcf_cipher_mechs_tab[2].me_name, SUN_CKM_DES_ECB, CRYPTO_MAX_MECH_NAME); kcf_cipher_mechs_tab[2].me_threshold = kcf_des_threshold; - (void) strncpy(kcf_cipher_mechs_tab[3].me_name, SUN_CKM_DES3_ECB, + (void) spl_strlcpy(kcf_cipher_mechs_tab[3].me_name, SUN_CKM_DES3_ECB, CRYPTO_MAX_MECH_NAME); kcf_cipher_mechs_tab[3].me_threshold = kcf_des3_threshold; - (void) strncpy(kcf_cipher_mechs_tab[4].me_name, SUN_CKM_BLOWFISH_CBC, + (void) spl_strlcpy(kcf_cipher_mechs_tab[4].me_name, SUN_CKM_BLOWFISH_CBC, CRYPTO_MAX_MECH_NAME); kcf_cipher_mechs_tab[4].me_threshold = kcf_bf_threshold; - (void) strncpy(kcf_cipher_mechs_tab[5].me_name, SUN_CKM_BLOWFISH_ECB, + (void) spl_strlcpy(kcf_cipher_mechs_tab[5].me_name, SUN_CKM_BLOWFISH_ECB, CRYPTO_MAX_MECH_NAME); kcf_cipher_mechs_tab[5].me_threshold = kcf_bf_threshold; - (void) strncpy(kcf_cipher_mechs_tab[6].me_name, SUN_CKM_AES_CBC, + (void) spl_strlcpy(kcf_cipher_mechs_tab[6].me_name, SUN_CKM_AES_CBC, CRYPTO_MAX_MECH_NAME); kcf_cipher_mechs_tab[6].me_threshold = kcf_aes_threshold; - (void) strncpy(kcf_cipher_mechs_tab[7].me_name, SUN_CKM_AES_ECB, + (void) spl_strlcpy(kcf_cipher_mechs_tab[7].me_name, SUN_CKM_AES_ECB, CRYPTO_MAX_MECH_NAME); kcf_cipher_mechs_tab[7].me_threshold = kcf_aes_threshold; - (void) strncpy(kcf_cipher_mechs_tab[8].me_name, SUN_CKM_RC4, + (void) spl_strlcpy(kcf_cipher_mechs_tab[8].me_name, SUN_CKM_RC4, CRYPTO_MAX_MECH_NAME); kcf_cipher_mechs_tab[8].me_threshold = kcf_rc4_threshold; /* 4 HMACs */ - (void) strncpy(kcf_mac_mechs_tab[0].me_name, SUN_CKM_MD5_HMAC, + (void) spl_strlcpy(kcf_mac_mechs_tab[0].me_name, SUN_CKM_MD5_HMAC, CRYPTO_MAX_MECH_NAME); kcf_mac_mechs_tab[0].me_threshold = kcf_md5_threshold; - (void) strncpy(kcf_mac_mechs_tab[1].me_name, SUN_CKM_MD5_HMAC_GENERAL, + (void) spl_strlcpy(kcf_mac_mechs_tab[1].me_name, SUN_CKM_MD5_HMAC_GENERAL, CRYPTO_MAX_MECH_NAME); kcf_mac_mechs_tab[1].me_threshold = kcf_md5_threshold; - (void) strncpy(kcf_mac_mechs_tab[2].me_name, SUN_CKM_SHA1_HMAC, + (void) spl_strlcpy(kcf_mac_mechs_tab[2].me_name, SUN_CKM_SHA1_HMAC, CRYPTO_MAX_MECH_NAME); kcf_mac_mechs_tab[2].me_threshold = kcf_sha1_threshold; - (void) strncpy(kcf_mac_mechs_tab[3].me_name, SUN_CKM_SHA1_HMAC_GENERAL, + (void) spl_strlcpy(kcf_mac_mechs_tab[3].me_name, SUN_CKM_SHA1_HMAC_GENERAL, CRYPTO_MAX_MECH_NAME); kcf_mac_mechs_tab[3].me_threshold = kcf_sha1_threshold; /* 1 random number generation pseudo mechanism */ - (void) strncpy(kcf_misc_mechs_tab[0].me_name, SUN_RANDOM, + (void) spl_strlcpy(kcf_misc_mechs_tab[0].me_name, SUN_RANDOM, CRYPTO_MAX_MECH_NAME); kcf_mech_hash = mod_hash_create_strhash_nodtr("kcf mech2id hash", diff --git a/module/icp/spi/kcf_spi.c b/module/icp/spi/kcf_spi.c index 34b36b81c0ab..edfb9765002c 100644 --- a/module/icp/spi/kcf_spi.c +++ b/module/icp/spi/kcf_spi.c @@ -606,7 +606,7 @@ init_prov_mechs(crypto_provider_info_t *info, kcf_provider_desc_t *desc) rand_mi = &desc->pd_mechanisms[mcount - 1]; bzero(rand_mi, sizeof (crypto_mech_info_t)); - (void) strncpy(rand_mi->cm_mech_name, SUN_RANDOM, + (void) spl_strlcpy(rand_mi->cm_mech_name, SUN_RANDOM, CRYPTO_MAX_MECH_NAME); rand_mi->cm_func_group_mask = CRYPTO_FG_RANDOM; } else { diff --git a/module/lua/lcompat.c b/module/lua/lcompat.c index c0a27182c7d8..a43ca10c331e 100644 --- a/module/lua/lcompat.c +++ b/module/lua/lcompat.c @@ -12,7 +12,7 @@ lcompat_sprintf(char *buf, size_t size, const char *fmt, ...) va_list args; va_start(args, fmt); - res = vsnprintf(buf, size, fmt, args); + res = spl_vsnprintf(buf, size, fmt, args); va_end(args); return (res); diff --git a/module/lua/lstrlib.c b/module/lua/lstrlib.c index 12027757bf53..13e5941d6f5d 100644 --- a/module/lua/lstrlib.c +++ b/module/lua/lstrlib.c @@ -37,7 +37,7 @@ static size_t str_sprintf(char *buf, const char *fmt, ...) { size_t len; va_start(args, fmt); - len = vsnprintf(buf, INT_MAX, fmt, args); + len = spl_vsnprintf(buf, INT_MAX, fmt, args); va_end(args); return len; diff --git a/module/os/windows/debug.c b/module/os/windows/debug.c index d97637e44861..be8c349be086 100644 --- a/module/os/windows/debug.c +++ b/module/os/windows/debug.c @@ -28,6 +28,7 @@ #define _NO_CRT_STDIO_INLINE #include +#include #include #include #include @@ -51,7 +52,7 @@ static unsigned long long startOff = 0; int initDbgCircularBuffer(void) { - cbuf = ExAllocatePoolWithTag(NonPagedPoolNx, cbuf_size, '!GBD'); + cbuf = spl_ExAllocatePoolZero(NonPagedPoolNx, cbuf_size, '!GBD'); ASSERT(cbuf); KeInitializeSpinLock(&cbuf_spin); return (0); @@ -125,12 +126,12 @@ printBuffer(const char *fmt, ...) va_list args; va_start(args, fmt); char buf[max_line_length]; - _snprintf(buf, 18, "%p: ", PsGetCurrentThread()); + _snprintf_s(buf, sizeof (buf), _TRUNCATE, "%p: ", PsGetCurrentThread()); int tmp = _vsnprintf_s(&buf[17], sizeof (buf), max_line_length, fmt, args); if (tmp >= max_line_length) { - _snprintf(&buf[17], 17, "buffer too small"); + _snprintf_s(&buf[17], 17, _TRUNCATE, "buffer too small"); } KeAcquireSpinLock(&cbuf_spin, &level); diff --git a/module/os/windows/driver.c b/module/os/windows/driver.c index 5873fae229ef..995e27a636d2 100644 --- a/module/os/windows/driver.c +++ b/module/os/windows/driver.c @@ -319,7 +319,7 @@ spl_kstat_registry(void *arg, kstat_t *ksp) break; // Something is wrong - or we finished // Allocate space to hold - regBuffer = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePoolWithTag( + regBuffer = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePoolUninitialized( NonPagedPoolNx, length, 'zfsr'); if (regBuffer == NULL) diff --git a/module/os/windows/spl/spl-err.c b/module/os/windows/spl/spl-err.c index f54ff5405e27..d4250bfc51b2 100644 --- a/module/os/windows/spl/spl-err.c +++ b/module/os/windows/spl/spl-err.c @@ -36,7 +36,7 @@ vcmn_err(int ce, const char *fmt, va_list ap) { char msg[MAXMSGLEN]; - _vsnprintf(msg, MAXMSGLEN - 1, fmt, ap); + spl_vsnprintf(msg, MAXMSGLEN - 1, fmt, ap); switch (ce) { case CE_IGNORE: diff --git a/module/os/windows/spl/spl-kmem.c b/module/os/windows/spl/spl-kmem.c index 54cc47adbae4..4fad95d648c1 100644 --- a/module/os/windows/spl/spl-kmem.c +++ b/module/os/windows/spl/spl-kmem.c @@ -1885,7 +1885,7 @@ kmem_dumppr(char **pp, char *e, const char *format, ...) va_list ap; va_start(ap, format); - n = vsnprintf(p, e - p, format, ap); + n = spl_vsnprintf(p, e - p, format, ap); va_end(ap); *pp = p + n; } @@ -3560,7 +3560,7 @@ kmem_cache_create( /* * Set cache properties. */ - (void) strncpy(cp->cache_name, name, KMEM_CACHE_NAMELEN); + (void) spl_strlcpy(cp->cache_name, name, KMEM_CACHE_NAMELEN + 1); strident_canon(cp->cache_name, KMEM_CACHE_NAMELEN + 1); cp->cache_bufsize = bufsize; cp->cache_align = align; @@ -6613,6 +6613,118 @@ kmem_asdprintf(const char *fmt, ...) return (ptr); } +#define SPL_VSNPRINTF_PROBE_MIN 256 +/* + * 1 MiB: roughly 256x the largest single formatted string anywhere + * in this tree today (PAGE_SIZE == 4096, in zfs_fletcher.c). No real + * caller is expected to ever reach this; it exists only to bound a + * pathological/malformed format string's retry loop. + */ +#define SPL_VSNPRINTF_PROBE_MAX (1024 * 1024) + +/* + * True-length-preserving, deprecated-API-free replacement for + * _vsnprintf(). See the comment above its prototype in sys/types.h + * for why this can't be ntstrsafe.h-based and why it lives here + * rather than as a header inline. + * + * Contract (do not change without auditing every caller of + * spl_vsnprintf()/spl_snprintf()/snprintf() in the tree - e.g. + * dmu_redact.c, zcp_iter.c, zfs_fletcher.c, kmem_asprintf(), + * kmem_vasprintf()): + * - If the formatted string (plus NUL) fits in [buf, buf+size), + * it is written in full and the exact number of characters + * written (excluding the NUL) is returned. + * - Otherwise (including buf==NULL/size==0), the return value is + * still the exact number of characters the FULL, untruncated + * result would have needed - real snprintf() semantics, not + * _vsnprintf()'s -1 - even though buf itself may be left + * truncated exactly as _vsnprintf_s(..., _TRUNCATE, ...) leaves + * it (or untouched, if buf==NULL/size==0). + */ +int +spl_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + va_list args_copy; + int ret; + size_t cap; + char stackbuf[SPL_VSNPRINTF_PROBE_MIN]; + + /* + * Tier 1: try the caller's own buffer first. This covers every + * call site that already passes a real, adequately sized + * buffer (the common case) with zero extra allocation - IRQL- + * safe (no allocation), cheaper than the old code, which always + * paid for a wasted measuring call even when the real write + * succeeded. + */ + if (buf != NULL && size > 0) { + args_copy = args; /* x64 MSVC va_list is a plain pointer */ + ret = _vsnprintf_s(buf, size, _TRUNCATE, fmt, args_copy); + if (ret >= 0) + return (ret); /* fit: ret IS the true length */ + } + + /* + * Tier 2: a small on-stack probe. Still IRQL-safe (no + * allocation) - every real caller in this tree writes a buffer + * under a few hundred bytes (the one known exception, + * module/lua/lstrlib.c's Lua channel-program formatting, is + * intentionally unbounded and falls through to Tier 3), so this + * is what makes every measure-only caller (buf==NULL, e.g. + * __dprintf's first call, kmem_asprintf(), kmem_vasprintf()) + * avoid the allocator entirely in the overwhelmingly common + * case. + */ + args_copy = args; + ret = _vsnprintf_s(stackbuf, sizeof (stackbuf), _TRUNCATE, fmt, + args_copy); + if (ret >= 0) + return (ret); + + /* + * Tier 3: only reached when even a 256-byte probe truncates. + * This is the only tier that allocates, so it is the only tier + * that can violate IRQL rules (KM_SLEEP can block) - guard it + * explicitly here, at the one place that actually needs it, + * rather than requiring every current and future caller + * (vcmn_err included) to remember its own guard. + * + * This is a real, if rare, new return value. Every live caller + * in the tree has been individually audited to confirm this is + * safe: callers that discard the return value are unaffected + * (their buffer is already correctly truncated by Tier 1/2's + * _vsnprintf_s call); kmem_vasprintf() already anticipates and + * handles a negative return from its measuring call; + * kmem_asprintf() is hardened alongside this change specifically + * because it previously was not safe against one. + */ + if (KeGetCurrentIrql() >= DISPATCH_LEVEL) + return (-1); + + cap = SPL_VSNPRINTF_PROBE_MIN * 2; + if (size > cap) + cap = size; + if (cap >= SPL_VSNPRINTF_PROBE_MAX) + cap = SPL_VSNPRINTF_PROBE_MAX; + + for (;;) { + /* KM_SLEEP: always succeeds, never returns NULL. */ + char *tmp = kmem_alloc(cap, KM_SLEEP); + args_copy = args; + ret = _vsnprintf_s(tmp, cap, _TRUNCATE, fmt, args_copy); + kmem_free(tmp, cap); + if (ret >= 0) + return (ret); + if (cap >= SPL_VSNPRINTF_PROBE_MAX) + return (-1); /* honest failure, not a fabricated length */ + if (cap > SPL_VSNPRINTF_PROBE_MAX / 2) + cap = SPL_VSNPRINTF_PROBE_MAX; + else + cap *= 2; + } +} + char * kmem_asprintf(const char *fmt, ...) { @@ -6621,13 +6733,17 @@ kmem_asprintf(const char *fmt, ...) char *buf; va_start(adx, fmt); - size = _vsnprintf(NULL, 0, fmt, adx) + 1; + size = spl_vsnprintf(NULL, 0, fmt, adx); va_end(adx); + if (size < 0) + return (NULL); /* honest failure, not KMEM_ZERO_SIZE_PTR */ + size++; + buf = kmem_alloc(size, KM_SLEEP); va_start(adx, fmt); - (void) _vsnprintf(buf, size, fmt, adx); + (void) spl_vsnprintf(buf, size, fmt, adx); va_end(adx); return (buf); @@ -6645,11 +6761,11 @@ kmem_vasprintf(const char *fmt, va_list ap) int size; int r = -1; - size = vsnprintf(NULL, 0, fmt, ap); + size = spl_vsnprintf(NULL, 0, fmt, ap); if ((size >= 0) && (size < INT_MAX)) { ptr = (char *)kmem_alloc(size + 1, KM_SLEEP); // +1 for null if (ptr) { - r = vsnprintf(ptr, size + 1, fmt, ap); // +1 for null + r = spl_vsnprintf(ptr, size + 1, fmt, ap); // +1 for null if ((r < 0) || (r > size)) { kmem_free(ptr, size); r = -1; diff --git a/module/os/windows/spl/spl-kstat.c b/module/os/windows/spl/spl-kstat.c index bd2a2c045b10..61579cf812dd 100644 --- a/module/os/windows/spl/spl-kstat.c +++ b/module/os/windows/spl/spl-kstat.c @@ -155,7 +155,7 @@ struct sbuf { /* sbuf_new() and family does exist in XNU, but Apple wont let us call them */ #define M_SBUF 105 /* string buffers */ #define SBMALLOC(size) \ - (struct sbuf *)ExAllocatePoolWithTag(NonPagedPoolNx, (size), '!SFZ') + (struct sbuf *)ExAllocatePoolUninitialized(NonPagedPoolNx, (size), '!SFZ') #define SBFREE(buf) ExFreePoolWithTag((buf), '!SFZ') #define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0) @@ -309,10 +309,20 @@ sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap) do { va_copy(ap_copy, ap); - len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1, + len = spl_vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1, fmt, ap_copy); // left-side must be assignable. Win tries to set to 0. // va_end(ap_copy); + /* + * spl_vsnprintf() can return -1 on failure (e.g. format + * needs more than its ~1 MiB growth ceiling). Treat that + * as "nothing written" rather than let a negative len + * flow into the signed s_len accounting below, which + * would silently decrement s_len and corrupt the next + * sbuf_vprintf() call's buffer offset. + */ + if (len < 0) + len = 0; } while (len > SBUF_FREESPACE(s) && sbuf_extend(s, len - SBUF_FREESPACE(s)) == 0); s->s_len += min(len, SBUF_FREESPACE(s)); @@ -785,7 +795,7 @@ void kstat_set_string(char *dst, const char *src) { bzero(dst, KSTAT_STRLEN); - (void) strncpy(dst, src, KSTAT_STRLEN - 1); + (void) spl_strlcpy(dst, src, KSTAT_STRLEN); } void @@ -1034,7 +1044,8 @@ kstat_create_zone(const char *ks_module, int ks_instance, const char *ks_name, if (ks_name == NULL) { char buf[KSTAT_STRLEN]; kstat_set_string(buf, ks_module); - (void) sprintf(namebuf, "%s%d", buf, ks_instance); + (void) spl_snprintf(namebuf, sizeof (namebuf), "%s%d", buf, + ks_instance); ks_name = namebuf; } diff --git a/module/os/windows/spl/spl-proc_list.c b/module/os/windows/spl/spl-proc_list.c index 1d750447c77c..6fea0a75cf34 100644 --- a/module/os/windows/spl/spl-proc_list.c +++ b/module/os/windows/spl/spl-proc_list.c @@ -35,7 +35,7 @@ seq_printf(struct seq_file *f, const char *fmt, ...) va_list adx; va_start(adx, fmt); - (void) vsnprintf(f->sf_buf, f->sf_size, fmt, adx); + (void) spl_vsnprintf(f->sf_buf, f->sf_size, fmt, adx); va_end(adx); } @@ -81,7 +81,7 @@ procfs_list_addr(kstat_t *ksp, loff_t n) ksp->ks_private1 = list_next(&pl->pl_list, elt); if (ksp->ks_private1) { - p = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof (*p), '!SFZ'); + p = ExAllocatePoolUninitialized(NonPagedPoolNx, sizeof (*p), '!SFZ'); p->pli_pl = pl; p->pli_elt = ksp->ks_private1; } diff --git a/module/os/windows/spl/spl-seg_kmem.c b/module/os/windows/spl/spl-seg_kmem.c index 958d7a4f2acb..e4b2f7bdfe4a 100644 --- a/module/os/windows/spl/spl-seg_kmem.c +++ b/module/os/windows/spl/spl-seg_kmem.c @@ -121,7 +121,7 @@ osif_malloc(uint64_t size) #ifdef _KERNEL void *tr = NULL; - tr = ExAllocatePoolWithTag(NonPagedPoolNx, size, '!SFZ'); + tr = ExAllocatePoolUninitialized(NonPagedPoolNx, size, '!SFZ'); ASSERT(P2PHASE(tr, PAGE_SIZE) == 0); if (tr != NULL) { atomic_inc_64(&stat_osif_malloc_success); diff --git a/module/os/windows/spl/spl-taskq.c b/module/os/windows/spl/spl-taskq.c index 499485d904c2..a521ea40982a 100644 --- a/module/os/windows/spl/spl-taskq.c +++ b/module/os/windows/spl/spl-taskq.c @@ -2458,7 +2458,7 @@ taskq_create_common(const char *name, int instance, int nthreads, pri_t pri, * Make sure the name is 0-terminated, and conforms to the rules for * C indentifiers */ - (void) strncpy(tq->tq_name, name, TASKQ_NAMELEN + 1); + (void) spl_strlcpy(tq->tq_name, name, TASKQ_NAMELEN + 1); strident_canon(tq->tq_name, TASKQ_NAMELEN + 1); tq->tq_flags = flags | TASKQ_CHANGING; diff --git a/module/os/windows/spl/spl-windows.c b/module/os/windows/spl/spl-windows.c index 3aabbaa91116..3faa96058587 100644 --- a/module/os/windows/spl/spl-windows.c +++ b/module/os/windows/spl/spl-windows.c @@ -713,7 +713,7 @@ spl_GetZfsTotalMemory(PUNICODE_STRING RegistryPath) break; // Something is wrong - or we finished // Allocate space to hold - regBuffer = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePoolWithTag( + regBuffer = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePoolUninitialized( NonPagedPoolNx, length, 'zfsr'); if (regBuffer == NULL) @@ -801,7 +801,7 @@ spl_getZfsPreallocSize(PUNICODE_STRING RegistryPath) break; // Something is wrong - or we finished // Allocate space to hold - regBuffer = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePoolWithTag( + regBuffer = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePoolUninitialized( NonPagedPoolNx, length, 'zfsr'); if (regBuffer == NULL) diff --git a/module/os/windows/zfs/zfs_ctldir.c b/module/os/windows/zfs/zfs_ctldir.c index b3fc0875a7e1..488c5f30f87d 100644 --- a/module/os/windows/zfs/zfs_ctldir.c +++ b/module/os/windows/zfs/zfs_ctldir.c @@ -995,8 +995,8 @@ zfsctl_snapshot_name(zfsvfs_t *zfsvfs, const char *snap_name, int len, if ((strlen(full_name) + 1 + strlen(snap_name)) >= len) return (SET_ERROR(ENAMETOOLONG)); - (void) strcat(full_name, "@"); - (void) strcat(full_name, snap_name); + (void) spl_strlcat(full_name, "@", len); + (void) spl_strlcat(full_name, snap_name, len); return (0); } diff --git a/module/os/windows/zfs/zfs_debug.c b/module/os/windows/zfs/zfs_debug.c index 01584506703d..36c3fd09e99b 100644 --- a/module/os/windows/zfs/zfs_debug.c +++ b/module/os/windows/zfs/zfs_debug.c @@ -228,7 +228,7 @@ __dprintf(boolean_t dprint, const char *file, const char *func, } va_start(adx, fmt); - size = vsnprintf(NULL, 0, fmt, adx); + size = spl_vsnprintf(NULL, 0, fmt, adx); va_end(adx); size += snprintf(NULL, 0, "%s%s:%d:%s(): ", prefix, newfile, line, @@ -236,6 +236,16 @@ __dprintf(boolean_t dprint, const char *file, const char *func, size++; /* null byte in the "buf" string */ + /* + * size is negative only if both spl_vsnprintf() measuring calls + * above independently failed (e.g. each needed more than the + * ~1 MiB spl_vsnprintf() will grow to) - not realistic for a + * single log line, but kmem_alloc() must never see a negative + * size turn into a huge size_t. + */ + if (size <= 0) + return; + /* * There is one byte of string in sizeof (zfs_dbgmsg_t), used * for the terminating null. @@ -244,9 +254,9 @@ __dprintf(boolean_t dprint, const char *file, const char *func, int roger = 0; va_start(adx, fmt); - i = snprintf(buf, size + 1, "%s%s:%d:%s(): ", + i = snprintf(buf, size, "%s%s:%d:%s(): ", prefix, newfile, line, func); - roger = vsnprintf(buf + i, size -i + 1, fmt, adx); + roger = spl_vsnprintf(buf + i, size - i, fmt, adx); va_end(adx); /* diff --git a/module/os/windows/zfs/zfs_ioctl_os.c b/module/os/windows/zfs/zfs_ioctl_os.c index 64f465b8b5d1..26155c33c8c4 100644 --- a/module/os/windows/zfs/zfs_ioctl_os.c +++ b/module/os/windows/zfs/zfs_ioctl_os.c @@ -139,7 +139,7 @@ NTSTATUS zpool_zfs_get_metrics(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_ perf->zpool_allocated = 0; perf->zpool_size = 0; perf->zfs_volSize = 0; - strncpy(perf->zpoolHealthState, "", sizeof(perf->zpoolHealthState)); + spl_strlcpy(perf->zpoolHealthState, "", sizeof(perf->zpoolHealthState)); perf->l2arc_alloc_size = 0; perf->l2arc_space = 0; perf->special_mirror_alloc_size = 0; @@ -185,7 +185,8 @@ NTSTATUS zpool_zfs_get_metrics(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_ perf->zpool_allocated = pool_alloc; perf->zpool_size = pool_size; - strcpy(perf->zpoolHealthState, healthState); + spl_strlcpy(perf->zpoolHealthState, healthState, + sizeof (perf->zpoolHealthState)); } else perf->zfs_volSize = getZvolSize(perf->name); diff --git a/module/os/windows/zfs/zfs_vnops_windows.c b/module/os/windows/zfs/zfs_vnops_windows.c index 96b68d138a84..4b498fe41a1c 100644 --- a/module/os/windows/zfs/zfs_vnops_windows.c +++ b/module/os/windows/zfs/zfs_vnops_windows.c @@ -297,7 +297,9 @@ stream_parse(char *filename, char **streamname) *colon = 0; // Cut off streamname from filename // We now ADD ":$DATA" to the stream name. - strcat(*streamname, ":$DATA"); + size_t remaining = PATH_MAX - (*streamname - filename); + if (spl_strlcat(*streamname, ":$DATA", remaining) >= remaining) + return (SET_ERROR(ENAMETOOLONG)); return (0); } @@ -419,7 +421,7 @@ zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist, * - maharmstone */ REPARSE_DATA_BUFFER *rpb; - rpb = ExAllocatePoolWithTag(PagedPool, + rpb = spl_ExAllocatePoolZero(PagedPool, zp->z_size, '!FSZ'); zfs_uio_t uio; struct iovec iov = { rpb, zp->z_size }; @@ -1763,7 +1765,7 @@ pnp_query_id(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) zmo = (mount_t *)DeviceObject->DeviceExtension; - Irp->IoStatus.Information = (void *)ExAllocatePoolWithTag(PagedPool, + Irp->IoStatus.Information = (void *)spl_ExAllocatePoolZero(PagedPool, zmo->bus_name.Length + sizeof (UNICODE_NULL), '!OIZ'); if (Irp->IoStatus.Information == NULL) return (STATUS_NO_MEMORY); @@ -2281,7 +2283,7 @@ BufferUserBuffer(IN OUT PIRP Irp, IN ULONG BufferLength) if (Irp->AssociatedIrp.SystemBuffer == NULL) { UserBuffer = MapUserBuffer(Irp); Irp->AssociatedIrp.SystemBuffer = - FsRtlAllocatePoolWithQuotaTag(NonPagedPoolNx, + spl_ExAllocatePoolZero(NonPagedPoolNx, BufferLength, 'qtaf'); // @@ -5319,8 +5321,8 @@ _Function_class_(DRIVER_DISPATCH) TargetDeviceRelation) { PDEVICE_RELATIONS DeviceRelations; DeviceRelations = - (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, - sizeof (DEVICE_RELATIONS)); + (PDEVICE_RELATIONS)ExAllocatePoolUninitialized(PagedPool, + sizeof (DEVICE_RELATIONS), '!DRZ'); if (!DeviceRelations) { TraceEvent(TRACE_NOISY, "enomem DeviceRelations\n"); Status = STATUS_INSUFFICIENT_RESOURCES; diff --git a/module/os/windows/zfs/zfs_vnops_windows_lib.c b/module/os/windows/zfs/zfs_vnops_windows_lib.c index 402b182b7610..c5f2f2e31d52 100644 --- a/module/os/windows/zfs/zfs_vnops_windows_lib.c +++ b/module/os/windows/zfs/zfs_vnops_windows_lib.c @@ -1559,7 +1559,7 @@ zfs_uid2sid(uint64_t uid, SID **sid) // Root? num = (uid == 0) ? 1 : 2; - tmp = ExAllocatePoolWithTag(PagedPool, + tmp = ExAllocatePoolUninitialized(PagedPool, offsetof(SID, SubAuthority) + (num * sizeof (ULONG)), 'zsid'); tmp->Revision = 1; @@ -1618,7 +1618,7 @@ zfs_gid2sid(uint64_t gid, SID **sid) ASSERT(sid != NULL); - tmp = ExAllocatePoolWithTag(PagedPool, + tmp = ExAllocatePoolUninitialized(PagedPool, offsetof(SID, SubAuthority) + (num * sizeof (ULONG)), 'zsid'); tmp->Revision = 1; @@ -1660,7 +1660,7 @@ zfs_set_acl(dacl *dacls) i++; } - acl = ExAllocatePoolWithTag(PagedPool, size, 'zacl'); + acl = ExAllocatePoolUninitialized(PagedPool, size, 'zacl'); if (!acl) return (NULL); @@ -1726,7 +1726,7 @@ zfs_set_security_root(struct vnode *vp) ASSERT(buflen != 0); - void *tmp = ExAllocatePoolWithTag(PagedPool, buflen, 'ZSEC'); + void *tmp = ExAllocatePoolUninitialized(PagedPool, buflen, 'ZSEC'); if (tmp == NULL) goto err; diff --git a/module/os/windows/zfs/zfs_vnops_windows_mount.c b/module/os/windows/zfs/zfs_vnops_windows_mount.c index 297205e4a456..4b8dd03fe447 100644 --- a/module/os/windows/zfs/zfs_vnops_windows_mount.c +++ b/module/os/windows/zfs/zfs_vnops_windows_mount.c @@ -325,15 +325,13 @@ SendVolumeArrivalNotification(PUNICODE_STRING DeviceName) dprintf("=> SendVolumeArrivalNotification: '%wZ'\n", DeviceName); length = sizeof (MOUNTMGR_TARGET_NAME) + DeviceName->Length - 1; - targetName = ExAllocatePool(PagedPool, length); + targetName = spl_ExAllocatePoolZero(PagedPool, length, 'ZVAN'); if (targetName == NULL) { dprintf(" can't allocate MOUNTMGR_TARGET_NAME\n"); return (STATUS_INSUFFICIENT_RESOURCES); } - RtlZeroMemory(targetName, length); - targetName->DeviceNameLength = DeviceName->Length; RtlCopyMemory(targetName->DeviceName, DeviceName->Buffer, DeviceName->Length); @@ -448,15 +446,13 @@ SendVolumeCreatePoint(__in PUNICODE_STRING DeviceName, length = sizeof (MOUNTMGR_CREATE_POINT_INPUT) + MountPoint->Length + DeviceName->Length; - point = ExAllocatePool(PagedPool, length); + point = spl_ExAllocatePoolZero(PagedPool, length, 'ZVCP'); if (point == NULL) { dprintf(" can't allocate MOUNTMGR_CREATE_POINT_INPUT\n"); return (STATUS_INSUFFICIENT_RESOURCES); } - RtlZeroMemory(point, length); - dprintf(" DeviceName: %wZ\n", DeviceName); point->DeviceNameOffset = sizeof (MOUNTMGR_CREATE_POINT_INPUT); point->DeviceNameLength = DeviceName->Length; @@ -1012,7 +1008,7 @@ generateVolumeNameMountpoint(wchar_t *vol_mpt) wchar_t wc_guid[50]; generateGUID(&GUID); mbstowcs(&wc_guid, GUID, 50); - int len = _snwprintf(vol_mpt, 50, L"\\??\\Volume{%s}", wc_guid); + (void) RtlStringCchPrintfW(vol_mpt, 50, L"\\??\\Volume{%s}", wc_guid); } int diff --git a/module/os/windows/zfs/zfs_windows_zvol.c b/module/os/windows/zfs/zfs_windows_zvol.c index d71f3d8810c2..b0055dab7906 100644 --- a/module/os/windows/zfs/zfs_windows_zvol.c +++ b/module/os/windows/zfs/zfs_windows_zvol.c @@ -104,19 +104,15 @@ zvol_start(PDRIVER_OBJECT DriverObject, PUNICODE_STRING pRegistryPath) // supporting more would mean bigger changes in the zv_targets // array. now we can go up to 32,640 zvols. pwzvolDrvInfo->NumberOfBuses = 1; + SIZE_T zvContextArraySize = (SIZE_T)pwzvolDrvInfo->MaximumNumberOfTargets * + pwzvolDrvInfo->MaximumNumberOfLogicalUnits * sizeof (wzvolContext); + pwzvolDrvInfo->zvContextArray = - (wzvolContext*)ExAllocatePoolWithTag(NonPagedPoolNx, - ((SIZE_T)pwzvolDrvInfo->MaximumNumberOfTargets * - pwzvolDrvInfo->MaximumNumberOfLogicalUnits * - sizeof (wzvolContext)), MP_TAG_GENERAL); + (wzvolContext*)spl_ExAllocatePoolZero(NonPagedPoolNx, + zvContextArraySize, MP_TAG_GENERAL); if (pwzvolDrvInfo->zvContextArray == NULL) return (STATUS_NO_MEMORY); - RtlZeroMemory(pwzvolDrvInfo->zvContextArray, - ((SIZE_T)pwzvolDrvInfo->MaximumNumberOfTargets * - pwzvolDrvInfo->MaximumNumberOfLogicalUnits * - (sizeof (wzvolContext)))); - RtlZeroMemory(&hwInitData, sizeof (VIRTUAL_HW_INITIALIZATION_DATA)); hwInitData.HwInitializationDataSize = @@ -345,11 +341,9 @@ wzvol_HwReportAdapter(__in pHW_HBA_EXT pHBAExt) WnodeSizeInstanceName + WnodeSizeDataBlock; - pWnode = ExAllocatePoolWithTag(NonPagedPoolNx, size, MP_TAG_GENERAL); + pWnode = spl_ExAllocatePoolZero(NonPagedPoolNx, size, MP_TAG_GENERAL); if (NULL != pWnode) { - RtlZeroMemory(pWnode, size); - // Fill out most of header. StorPort will set the // ProviderId and TimeStamp in the header. @@ -445,11 +439,9 @@ wzvol_HwReportLink(__in pHW_HBA_EXT pHBAExt) WnodeSizeInstanceName + WnodeSizeDataBlock; - pWnode = ExAllocatePoolWithTag(NonPagedPoolNx, size, MP_TAG_GENERAL); + pWnode = spl_ExAllocatePoolZero(NonPagedPoolNx, size, MP_TAG_GENERAL); if (NULL != pWnode) { - RtlZeroMemory(pWnode, size); - // Fill out most of header. StorPort will set the // ProviderId and TimeStamp in the header. @@ -535,11 +527,9 @@ wzvol_HwReportLog(__in pHW_HBA_EXT pHBAExt) WnodeSizeInstanceName + WnodeSizeDataBlock; - pWnode = ExAllocatePoolWithTag(NonPagedPoolNx, size, MP_TAG_GENERAL); + pWnode = spl_ExAllocatePoolZero(NonPagedPoolNx, size, MP_TAG_GENERAL); if (NULL != pWnode) { - RtlZeroMemory(pWnode, size); - // Fill out most of header. StorPort will set the // ProviderId and TimeStamp in the header. diff --git a/module/os/windows/zfs/zfs_windows_zvol_scsi.c b/module/os/windows/zfs/zfs_windows_zvol_scsi.c index d73fbaddaf63..10142719668c 100644 --- a/module/os/windows/zfs/zfs_windows_zvol_scsi.c +++ b/module/os/windows/zfs/zfs_windows_zvol_scsi.c @@ -156,7 +156,7 @@ wzvol_assign_targetid(zvol_state_t *zv) { wzvolContext* zv_targets = STOR_wzvolDriverInfo.zvContextArray; ASSERT(zv->zv_zso->zso_target_context == NULL); - PIO_REMOVE_LOCK pIoRemLock = ExAllocatePoolWithTag(NonPagedPoolNx, + PIO_REMOVE_LOCK pIoRemLock = ExAllocatePoolUninitialized(NonPagedPoolNx, sizeof (*pIoRemLock), MP_TAG_GENERAL); if (!pIoRemLock) { @@ -397,7 +397,7 @@ ScsiGetMPIOExt( } if (pNextEntry == &pHBAExt->pwzvolDrvObj->ListMPIOExt) { - pLUMPIOExt = ExAllocatePoolWithTag(NonPagedPoolNx, + pLUMPIOExt = spl_ExAllocatePoolZero(NonPagedPoolNx, sizeof (HW_LU_EXTENSION_MPIO), MP_TAG_GENERAL); if (!pLUMPIOExt) { @@ -405,8 +405,6 @@ ScsiGetMPIOExt( goto Done; } - RtlZeroMemory(pLUMPIOExt, sizeof (HW_LU_EXTENSION_MPIO)); - pLUMPIOExt->ScsiAddr.PathId = pSrb->PathId; pLUMPIOExt->ScsiAddr.TargetId = pSrb->TargetId; pLUMPIOExt->ScsiAddr.Lun = pSrb->Lun; @@ -515,7 +513,8 @@ ScsiOpInquiry( pHBAExt->ProductRevision, 4); memset((PCHAR)pInqData->VendorSpecific, ' ', sizeof (pInqData->VendorSpecific)); - sprintf(pInqData->VendorSpecific, "%.04d-%.04d-%.04d", + spl_snprintf(pInqData->VendorSpecific, + sizeof (pInqData->VendorSpecific), "%.04d-%.04d-%.04d", pSrb->PathId, pSrb->TargetId, pSrb->Lun); pInqData->VendorSpecific[strlen(pInqData->VendorSpecific)] = ' '; @@ -1088,7 +1087,11 @@ DiReadWriteSetup(zvol_state_t *zv, MpWkRtnAction action, zfsiodesc_t *pIo) { // cannot use kmem_alloc with sleep if IRQL dispatch so get straight // from NP pool. - pMP_WorkRtnParms pWkRtnParms = (pMP_WorkRtnParms)ExAllocatePoolWithTag( + // Not spl_ExAllocatePoolZero(): this allocation is deliberately + // larger than what gets zeroed below - the extra IoSizeofWorkItem() + // bytes are opaque storage that IoInitializeWorkItem() fills in + // itself, so zeroing them would be redundant work. + pMP_WorkRtnParms pWkRtnParms = (pMP_WorkRtnParms)ExAllocatePoolUninitialized( NonPagedPoolNx, ALIGN_UP_BY(sizeof (MP_WorkRtnParms), 16) + IoSizeofWorkItem(), MP_TAG_GENERAL); if (NULL == pWkRtnParms) { diff --git a/module/zcommon/zfs_fletcher.c b/module/zcommon/zfs_fletcher.c index ffd395474010..00bb44365a5d 100644 --- a/module/zcommon/zfs_fletcher.c +++ b/module/zcommon/zfs_fletcher.c @@ -900,12 +900,12 @@ fletcher_4_param_get(char *buffer, zfs_kernel_param_t *unused) /* list fastest */ fmt = IMPL_FMT(impl, IMPL_FASTEST); - cnt += sprintf(buffer + cnt, fmt, "fastest"); + cnt += spl_snprintf(buffer + cnt, PAGE_SIZE - cnt, fmt, "fastest"); /* list all supported implementations */ for (uint32_t i = 0; i < fletcher_4_supp_impls_cnt; ++i) { fmt = IMPL_FMT(impl, i); - cnt += sprintf(buffer + cnt, fmt, + cnt += spl_snprintf(buffer + cnt, PAGE_SIZE - cnt, fmt, fletcher_4_supp_impls[i]->name); } diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index 551043fafbfc..31153df201cf 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -3000,8 +3000,8 @@ dmu_send_estimate_fast(dsl_dataset_t *origds, dsl_dataset_t *fromds, char dsname[ZFS_MAX_DATASET_NAME_LEN + 6]; dsl_dataset_name(origds, dsname); - (void) strcat(dsname, "/"); - (void) strcat(dsname, recv_clone_name); + (void) spl_strlcat(dsname, "/", sizeof (dsname)); + (void) spl_strlcat(dsname, recv_clone_name, sizeof (dsname)); err = dsl_dataset_hold(origds->ds_dir->dd_pool, dsname, FTAG, &ds); diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c index e1c1e5d8ba81..48b4970eb5f7 100644 --- a/module/zfs/dsl_dir.c +++ b/module/zfs/dsl_dir.c @@ -441,8 +441,7 @@ getcomponent(const char *path, char *component, const char **nextp) if (p - path >= ZFS_MAX_DATASET_NAME_LEN) return (SET_ERROR(ENAMETOOLONG)); - (void) strncpy(component, path, p - path); - component[p - path] = '\0'; + (void) spl_strlcpy(component, path, (p - path) + 1); p++; } else if (p[0] == '@') { /* @@ -455,8 +454,7 @@ getcomponent(const char *path, char *component, const char **nextp) if (p - path >= ZFS_MAX_DATASET_NAME_LEN) return (SET_ERROR(ENAMETOOLONG)); - (void) strncpy(component, path, p - path); - component[p - path] = '\0'; + (void) spl_strlcpy(component, path, (p - path) + 1); } else { panic("invalid p=%p", (void *)p); } diff --git a/module/zfs/dsl_prop.c b/module/zfs/dsl_prop.c index ed42ac5aef3c..75afbaf78de3 100644 --- a/module/zfs/dsl_prop.c +++ b/module/zfs/dsl_prop.c @@ -58,7 +58,7 @@ dodefault(zfs_prop_t prop, int intsz, int numints, void *buf) if (intsz != 1) return (SET_ERROR(EOVERFLOW)); - (void) strncpy(buf, zfs_prop_default_string(prop), + (void) spl_strlcpy(buf, zfs_prop_default_string(prop), numints); } else { if (intsz != 8 || numints < 1) @@ -1029,8 +1029,8 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, if (flags & DSL_PROP_GET_LOCAL) continue; - (void) strncpy(buf, za.za_name, (suffix - za.za_name)); - buf[suffix - za.za_name] = '\0'; + (void) spl_strlcpy(buf, za.za_name, + (suffix - za.za_name) + 1); propname = buf; if (!(flags & DSL_PROP_GET_RECEIVED)) { diff --git a/module/zfs/spa_misc.c b/module/zfs/spa_misc.c index 904256323143..0e3063ab9297 100644 --- a/module/zfs/spa_misc.c +++ b/module/zfs/spa_misc.c @@ -400,7 +400,7 @@ spa_load_failed(spa_t *spa, const char *fmt, ...) char buf[256]; va_start(adx, fmt); - (void) vsnprintf(buf, sizeof (buf), fmt, adx); + (void) spl_vsnprintf(buf, sizeof (buf), fmt, adx); va_end(adx); zfs_dbgmsg("spa_load(%s, config %s): FAILED: %s", spa->spa_name, @@ -415,7 +415,7 @@ spa_load_note(spa_t *spa, const char *fmt, ...) char buf[256]; va_start(adx, fmt); - (void) vsnprintf(buf, sizeof (buf), fmt, adx); + (void) spl_vsnprintf(buf, sizeof (buf), fmt, adx); va_end(adx); zfs_dbgmsg("spa_load(%s, config %s): %s", spa->spa_name, @@ -1677,7 +1677,7 @@ spa_altroot(spa_t *spa, char *buf, size_t buflen) if (spa->spa_root == NULL) buf[0] = '\0'; else - (void) strncpy(buf, spa->spa_root, buflen); + (void) spl_strlcpy(buf, spa->spa_root, buflen); } int diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index 255202ab2bee..0935b32ed48e 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -145,7 +145,7 @@ vdev_dbgmsg(vdev_t *vd, const char *fmt, ...) char buf[256]; va_start(adx, fmt); - (void) vsnprintf(buf, sizeof (buf), fmt, adx); + (void) spl_vsnprintf(buf, sizeof (buf), fmt, adx); va_end(adx); if (vd->vdev_path != NULL) { diff --git a/module/zfs/zcp.c b/module/zfs/zcp.c index 4c8af269ee01..08aced4c7f29 100644 --- a/module/zfs/zcp.c +++ b/module/zfs/zcp.c @@ -261,7 +261,7 @@ zcp_table_to_nvlist(lua_State *state, int index, int depth) /* check if this could collide with a number or bool */ long long tmp; int parselen; - if ((sscanf(key, "%lld%n", &tmp, &parselen) > 0 && + if ((sscanf_s(key, "%lld%n", &tmp, &parselen) > 0 && parselen == strlen(key)) || strcmp(key, "true") == 0 || strcmp(key, "false") == 0) { @@ -1244,7 +1244,7 @@ zcp_args_error(lua_State *state, const char *fname, const zcp_arg_t *pargs, va_list argp; va_start(argp, fmt); - VERIFY3U(len, >, vsnprintf(errmsg, len, fmt, argp)); + VERIFY3U(len, >, spl_vsnprintf(errmsg, len, fmt, argp)); va_end(argp); /* diff --git a/module/zfs/zcp_get.c b/module/zfs/zcp_get.c index 7256e4de1915..1d334b7783bb 100644 --- a/module/zfs/zcp_get.c +++ b/module/zfs/zcp_get.c @@ -611,8 +611,7 @@ parse_userquota_prop(const char *prop_name, zfs_userquota_prop_t *type, */ int domain_len = strrchr(cp, '-') - cp; domain_val = kmem_alloc(domain_len + 1, KM_SLEEP); - (void) strncpy(domain_val, cp, domain_len); - domain_val[domain_len] = '\0'; + (void) spl_strlcpy(domain_val, cp, domain_len + 1); cp += domain_len + 1; (void) ddi_strtoll(cp, &end, 10, (longlong_t *)rid); diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index e7bb4a32f38f..c266aac80a62 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -745,7 +745,7 @@ zfs_get_parent(const char *datasetname, char *parent, int parentsize) /* * Remove the @bla or /bla from the end of the name to get the parent. */ - (void) strncpy(parent, datasetname, parentsize); + (void) spl_strlcpy(parent, datasetname, parentsize); cp = strrchr(parent, '@'); if (cp != NULL) { cp[0] = '\0'; diff --git a/module/zfs/zio.c b/module/zfs/zio.c index be06c386e3df..4cc3a2ef3584 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -929,7 +929,7 @@ zfs_blkptr_verify_log(spa_t *spa, const blkptr_t *bp, char buf[256]; va_start(adx, fmt); - (void) vsnprintf(buf, sizeof (buf), fmt, adx); + (void) spl_vsnprintf(buf, sizeof (buf), fmt, adx); va_end(adx); switch (blk_verify) { diff --git a/module/zfs/zio_inject.c b/module/zfs/zio_inject.c index f494db4a20ef..aacff6a6142f 100644 --- a/module/zfs/zio_inject.c +++ b/module/zfs/zio_inject.c @@ -894,7 +894,7 @@ zio_inject_list_next(int *id, char *name, size_t buflen, if (handler) { *record = handler->zi_record; *id = handler->zi_id; - (void) strncpy(name, spa_name(handler->zi_spa), buflen); + (void) spl_strlcpy(name, spa_name(handler->zi_spa), buflen); ret = 0; } else { ret = SET_ERROR(ENOENT);