diff --git a/.github/workflows/zfs-tests-functional.yml b/.github/workflows/zfs-tests-functional.yml index 40d03d25d4e5..ba3b2e11cfc2 100644 --- a/.github/workflows/zfs-tests-functional.yml +++ b/.github/workflows/zfs-tests-functional.yml @@ -32,7 +32,7 @@ jobs: sh autogen.sh - name: Configure run: | - ./configure --enable-debug --enable-debuginfo + ./configure --enable-debug --enable-debuginfo --enable-asan --enable-ubsan - name: Make run: | make --no-print-directory -s pkg-utils pkg-kmod diff --git a/.github/workflows/zfs-tests-sanity.yml b/.github/workflows/zfs-tests-sanity.yml index d1dc0e88c173..abe7974dddc1 100644 --- a/.github/workflows/zfs-tests-sanity.yml +++ b/.github/workflows/zfs-tests-sanity.yml @@ -28,7 +28,7 @@ jobs: sh autogen.sh - name: Configure run: | - ./configure --enable-debug --enable-debuginfo + ./configure --enable-debug --enable-debuginfo --enable-asan --enable-ubsan - name: Make run: | make --no-print-directory -s pkg-utils pkg-kmod diff --git a/.github/workflows/zloop.yml b/.github/workflows/zloop.yml index 5c1b9bd1ce22..0638bbe58f38 100644 --- a/.github/workflows/zloop.yml +++ b/.github/workflows/zloop.yml @@ -28,7 +28,7 @@ jobs: sh autogen.sh - name: Configure run: | - ./configure --enable-debug --enable-debuginfo + ./configure --enable-debug --enable-debuginfo --enable-asan --enable-ubsan - name: Make run: | make --no-print-directory -s pkg-utils pkg-kmod diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index ce6454f5eae9..b4c98fe4dc6c 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -8654,7 +8654,7 @@ main(int argc, char **argv) dump_opt[c] += verbose; } - libspl_assert_ok = (dump_opt['A'] == 1) || (dump_opt['A'] > 2); + libspl_set_assert_ok((dump_opt['A'] == 1) || (dump_opt['A'] > 2)); zfs_recover = (dump_opt['A'] > 1); argc -= optind; diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 5aa2508c382f..84f69f4e261b 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -575,8 +575,9 @@ usage(boolean_t requested) (void) fprintf(fp, gettext("\nSizes are specified in bytes " "with standard units such as K, M, G, etc.\n")); - (void) fprintf(fp, gettext("\nUser-defined properties can " - "be specified by using a name containing a colon (:).\n")); + (void) fprintf(fp, "%s", gettext("\nUser-defined properties " + "can be specified by using a name containing a colon " + "(:).\n")); (void) fprintf(fp, gettext("\nThe {user|group|project}" "[obj]{used|quota}@ properties must be appended with\n" "a user|group|project specifier of one of these forms:\n" diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 5d8d18255f51..43ab9c4cbb7b 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -1760,17 +1760,19 @@ zpool_do_create(int argc, char **argv) "feature@%s", feat->fi_uname); if (!nvlist_lookup_string(props, propname, &propval)) { - if (strcmp(propval, ZFS_FEATURE_DISABLED) == 0) + if (strcmp(propval, + ZFS_FEATURE_DISABLED) == 0) { (void) nvlist_remove_all(props, propname); - if (strcmp(propval, + } else if (strcmp(propval, ZFS_FEATURE_ENABLED) == 0 && - !requested_features[i]) + !requested_features[i]) { (void) fprintf(stderr, gettext( "Warning: feature \"%s\" enabled " "but is not in specified " "'compatibility' feature set.\n"), feat->fi_uname); + } } else if ( enable_pool_features && feat->fi_zfs_mod_supported && @@ -9210,7 +9212,7 @@ zpool_do_upgrade(int argc, char **argv) } } - (void) printf(gettext("This system supports ZFS pool feature " + (void) printf("%s", gettext("This system supports ZFS pool feature " "flags.\n\n")); if (showversions) { int i; diff --git a/cmd/ztest/ztest.c b/cmd/ztest/ztest.c index bb2f14298279..ed60d065c8a7 100644 --- a/cmd/ztest/ztest.c +++ b/cmd/ztest/ztest.c @@ -631,6 +631,8 @@ fatal(int do_perror, char *message, ...) (void) fflush(stdout); buf = umem_alloc(FATAL_MSG_SZ, UMEM_NOFAIL); + if (buf == NULL) + goto out; va_start(args, message); (void) sprintf(buf, "ztest: "); @@ -644,6 +646,7 @@ fatal(int do_perror, char *message, ...) (void) fprintf(stderr, "%s\n", buf); fatal_msg = buf; /* to ease debugging */ +out: if (ztest_dump_core) abort(); else diff --git a/cmd/zvol_id/zvol_id_main.c b/cmd/zvol_id/zvol_id_main.c index 22f2e848cba1..929a1a6e794d 100644 --- a/cmd/zvol_id/zvol_id_main.c +++ b/cmd/zvol_id/zvol_id_main.c @@ -35,6 +35,21 @@ #include #include +#if defined(ZFS_ASAN_ENABLED) +/* + * zvol_id is invoked by udev with the help of ptrace() + * making sanitized binary with leak detection croak + * because of tracing mechanisms collision + */ +extern const char *__asan_default_options(void); + +const char *__asan_default_options(void) { + return ("abort_on_error=true:halt_on_error=true:" + "allocator_may_return_null=true:disable_coredump=false:" + "detect_stack_use_after_return=true:detect_leaks=false"); +} +#endif + static int ioctl_get_msg(char *var, int fd) { diff --git a/config/Rules.am b/config/Rules.am index 20779ba49259..3146da6ee1d2 100644 --- a/config/Rules.am +++ b/config/Rules.am @@ -29,6 +29,7 @@ AM_CFLAGS += $(NO_OMIT_FRAME_POINTER) AM_CFLAGS += $(IMPLICIT_FALLTHROUGH) AM_CFLAGS += $(DEBUG_CFLAGS) AM_CFLAGS += $(ASAN_CFLAGS) +AM_CFLAGS += $(UBSAN_CFLAGS) AM_CFLAGS += $(CODE_COVERAGE_CFLAGS) $(NO_FORMAT_ZERO_LENGTH) if BUILD_FREEBSD AM_CFLAGS += -fPIC -Werror -Wno-unknown-pragmas -Wno-enum-conversion @@ -58,8 +59,17 @@ AM_CPPFLAGS += -D"__xpg_basename(...)=__xpg_basename(__VA_ARGS__) __attribute__( AM_CPPFLAGS += -D"basename(...)=basename(__VA_ARGS__) __attribute__((deprecated(\"basename(3) is underspecified. Use zfs_basename() instead!\")))" AM_CPPFLAGS += -D"dirname(...)=dirname(__VA_ARGS__) __attribute__((deprecated(\"dirname(3) is underspecified. Use zfs_dirnamelen() instead!\")))" +if ASAN_ENABLED +AM_CPPFLAGS += -DZFS_ASAN_ENABLED +endif + +if UBSAN_ENABLED +AM_CPPFLAGS += -DZFS_UBSAN_ENABLED +endif + AM_LDFLAGS = $(DEBUG_LDFLAGS) AM_LDFLAGS += $(ASAN_LDFLAGS) +AM_LDFLAGS += $(UBSAN_LDFLAGS) if BUILD_FREEBSD AM_LDFLAGS += -fstack-protector-strong -shared diff --git a/config/Substfiles.am b/config/Substfiles.am index 911903e10e69..b051438fb9e3 100644 --- a/config/Substfiles.am +++ b/config/Substfiles.am @@ -17,7 +17,9 @@ subst_sed_cmd = \ -e 's|@DEFAULT_INIT_NFS_SERVER[@]|$(DEFAULT_INIT_NFS_SERVER)|g' \ -e 's|@DEFAULT_INIT_SHELL[@]|$(DEFAULT_INIT_SHELL)|g' \ -e 's|@LIBFETCH_DYNAMIC[@]|$(LIBFETCH_DYNAMIC)|g' \ - -e 's|@LIBFETCH_SONAME[@]|$(LIBFETCH_SONAME)|g' + -e 's|@LIBFETCH_SONAME[@]|$(LIBFETCH_SONAME)|g' \ + -e 's|@ASAN_ENABLED[@]|$(ASAN_ENABLED)|g' \ + -e 's|@UBSAN_ENABLED[@]|$(UBSAN_ENABLED)|g' SUBSTFILES = CLEANFILES = $(SUBSTFILES) diff --git a/config/always-compiler-options.m4 b/config/always-compiler-options.m4 index ce84f7e60684..82b351c6e079 100644 --- a/config/always-compiler-options.m4 +++ b/config/always-compiler-options.m4 @@ -45,6 +45,53 @@ AC_DEFUN([ZFS_AC_CONFIG_ALWAYS_CC_ASAN], [ AC_SUBST([ASAN_ZFS]) ]) +dnl # +dnl # Enabled -fsanitize=undefined if supported by gcc. +dnl # +dnl # LDFLAGS needs -fsanitize=undefined at all times so libraries compiled with +dnl # it will be linked successfully. CFLAGS will vary by binary being built. +dnl # +dnl # The UBSAN_OPTIONS environment variable can be used to further control +dnl # the behavior of binaries and libraries build with -fsanitize=undefined. +dnl # +AC_DEFUN([ZFS_AC_CONFIG_ALWAYS_CC_UBSAN], [ + AC_MSG_CHECKING([whether to build with -fsanitize=undefined support]) + AC_ARG_ENABLE([ubsan], + [AS_HELP_STRING([--enable-ubsan], + [Enable -fsanitize=undefined support @<:@default=no@:>@])], + [], + [enable_ubsan=no]) + + AM_CONDITIONAL([UBSAN_ENABLED], [test x$enable_ubsan = xyes]) + AC_SUBST([UBSAN_ENABLED], [$enable_ubsan]) + AC_MSG_RESULT($enable_ubsan) + + AS_IF([ test "$enable_ubsan" = "yes" ], [ + AC_MSG_CHECKING([whether $CC supports -fsanitize=undefined]) + saved_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Werror -fsanitize=undefined" + AC_LINK_IFELSE([ + AC_LANG_SOURCE([[ int main() { return 0; } ]]) + ], [ + UBSAN_CFLAGS="-fsanitize=undefined" + UBSAN_LDFLAGS="-fsanitize=undefined" + UBSAN_ZFS="_with_ubsan" + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_ERROR([$CC does not support -fsanitize=undefined]) + ]) + CFLAGS="$saved_cflags" + ], [ + UBSAN_CFLAGS="" + UBSAN_LDFLAGS="" + UBSAN_ZFS="_without_ubsan" + ]) + + AC_SUBST([UBSAN_CFLAGS]) + AC_SUBST([UBSAN_LDFLAGS]) + AC_SUBST([UBSAN_ZFS]) +]) + dnl # dnl # Check if gcc supports -Wframe-larger-than= option. dnl # diff --git a/config/zfs-build.m4 b/config/zfs-build.m4 index b1604eb9d50e..35267795b2ba 100644 --- a/config/zfs-build.m4 +++ b/config/zfs-build.m4 @@ -218,6 +218,7 @@ AC_DEFUN([ZFS_AC_CONFIG_ALWAYS], [ ZFS_AC_CONFIG_ALWAYS_CC_NO_OMIT_FRAME_POINTER ZFS_AC_CONFIG_ALWAYS_CC_NO_IPA_SRA ZFS_AC_CONFIG_ALWAYS_CC_ASAN + ZFS_AC_CONFIG_ALWAYS_CC_UBSAN ZFS_AC_CONFIG_ALWAYS_TOOLCHAIN_SIMD ZFS_AC_CONFIG_ALWAYS_SYSTEM ZFS_AC_CONFIG_ALWAYS_ARCH @@ -323,6 +324,7 @@ AC_DEFUN([ZFS_AC_RPM], [ RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(DEBUG_KMEM_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(DEBUG_KMEM_TRACKING_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(ASAN_ZFS) 1"' + RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(UBSAN_ZFS) 1"' RPM_DEFINE_UTIL=' --define "_initconfdir $(initconfdir)"' diff --git a/include/zfs_fletcher.h b/include/zfs_fletcher.h index bb356c59acb1..28cb8ba234e5 100644 --- a/include/zfs_fletcher.h +++ b/include/zfs_fletcher.h @@ -160,4 +160,20 @@ _ZFS_FLETCHER_H const fletcher_4_ops_t fletcher_4_aarch64_neon_ops; } #endif +#if defined(ZFS_UBSAN_ENABLED) +#if defined(__has_attribute) +#if __has_attribute(no_sanitize_undefined) +#define ZFS_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize_undefined)) +#elif __has_attribute(no_sanitize) +#define ZFS_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined"))) +#else +#error "Compiler has to support attribute " + "`no_sanitize_undefined` or `no_sanitize(\"undefined\")`" + "when compiling with UBSan enabled" +#endif /* __has_attribute(no_sanitize_undefined) */ +#endif /* defined(__has_attribute) */ +#else +#define ZFS_NO_SANITIZE_UNDEFINED +#endif /* defined(ZFS_UBSAN_ENABLED) */ + #endif /* _ZFS_FLETCHER_H */ diff --git a/lib/libnvpair/libnvpair.abi b/lib/libnvpair/libnvpair.abi index 01be5785a4ae..f9874da81f82 100644 --- a/lib/libnvpair/libnvpair.abi +++ b/lib/libnvpair/libnvpair.abi @@ -78,6 +78,7 @@ + @@ -230,7 +231,6 @@ - @@ -2793,7 +2793,10 @@ - + + + + diff --git a/lib/libspl/assert.c b/lib/libspl/assert.c index 8e4333976f95..ad8fdcd8cf0a 100644 --- a/lib/libspl/assert.c +++ b/lib/libspl/assert.c @@ -25,7 +25,13 @@ #include -int libspl_assert_ok = 0; +static boolean_t libspl_assert_ok = B_FALSE; + +void +libspl_set_assert_ok(boolean_t val) +{ + libspl_assert_ok = val; +} /* printf version of libspl_assert */ void diff --git a/lib/libspl/include/assert.h b/lib/libspl/include/assert.h index fb964042b780..e968a2310774 100644 --- a/lib/libspl/include/assert.h +++ b/lib/libspl/include/assert.h @@ -32,9 +32,10 @@ #include #include #include +#include /* Set to non-zero to avoid abort()ing on an assertion failure */ -extern int libspl_assert_ok; +extern void libspl_set_assert_ok(boolean_t val); /* printf version of libspl_assert */ extern void libspl_assertf(const char *file, const char *func, int line, diff --git a/lib/libuutil/libuutil.abi b/lib/libuutil/libuutil.abi index 48575ebc6a9f..bf13d62e2f04 100644 --- a/lib/libuutil/libuutil.abi +++ b/lib/libuutil/libuutil.abi @@ -152,6 +152,7 @@ + @@ -255,7 +256,6 @@ - @@ -365,7 +365,10 @@ - + + + + diff --git a/lib/libuutil/uu_pname.c b/lib/libuutil/uu_pname.c index 28c4a8a9cf7b..43c9e77564ee 100644 --- a/lib/libuutil/uu_pname.c +++ b/lib/libuutil/uu_pname.c @@ -88,7 +88,8 @@ uu_warn_internal(int err, const char *format, va_list alist) if (pname != NULL) (void) fprintf(stderr, "%s: ", pname); - (void) vfprintf(stderr, format, alist); + if (format != NULL) + (void) vfprintf(stderr, format, alist); if (strrchr(format, '\n') == NULL) (void) fprintf(stderr, ": %s\n", strerror(err)); diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi index 122321103f7a..8f586804c9dd 100644 --- a/lib/libzfs/libzfs.abi +++ b/lib/libzfs/libzfs.abi @@ -191,6 +191,7 @@ + @@ -593,7 +594,6 @@ - @@ -906,7 +906,10 @@ - + + + + diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c index 0932cb1de6f0..62c46be9532b 100644 --- a/lib/libzfs/libzfs_mount.c +++ b/lib/libzfs/libzfs_mount.c @@ -1611,7 +1611,8 @@ zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) * At this point, we have the entire list of filesystems, so sort it by * mountpoint. */ - qsort(sets, used, sizeof (struct sets_s), mountpoint_compare); + if (used != 0) + qsort(sets, used, sizeof (struct sets_s), mountpoint_compare); /* * Walk through and first unshare everything. diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index dc0a76329afa..ab5b2b38188d 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -2899,22 +2899,24 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen, zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET); if (zhp == NULL) return (-1); - clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, - flags->force ? MS_FORCE : 0); - if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && + zfs_type_t type = zfs_get_type(zhp); + if (type == ZFS_TYPE_SNAPSHOT && zfs_spa_version(zhp, &spa_version) == 0 && spa_version >= SPA_VERSION_USERREFS) defer = B_TRUE; + clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, + flags->force ? MS_FORCE : 0); zfs_close(zhp); if (clp == NULL) return (-1); + err = changelist_prefix(clp); if (err) return (err); if (flags->verbose) (void) printf("attempting destroy %s\n", name); - if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { + if (type == ZFS_TYPE_SNAPSHOT) { nvlist_t *nv = fnvlist_alloc(); fnvlist_add_boolean(nv, name); err = lzc_destroy_snaps(nv, defer, NULL); @@ -4075,8 +4077,8 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, * properties: if we're asked to exclude this kind of * values we remove them from "recvprops" input nvlist. */ - if (!zfs_prop_inheritable(prop) && - !zfs_prop_user(name) && /* can be inherited too */ + if (!zfs_prop_user(name) && /* can be inherited too */ + !zfs_prop_inheritable(prop) && nvlist_exists(recvprops, newname)) fnvlist_remove(recvprops, newname); else diff --git a/lib/libzfs_core/libzfs_core.abi b/lib/libzfs_core/libzfs_core.abi index 5a3cbaf0b80d..111287d3ef56 100644 --- a/lib/libzfs_core/libzfs_core.abi +++ b/lib/libzfs_core/libzfs_core.abi @@ -135,6 +135,7 @@ + @@ -215,11 +216,13 @@ - + + + + - diff --git a/module/icp/algs/modes/gcm.c b/module/icp/algs/modes/gcm.c index d9796cd0ed49..8b3793daa5cf 100644 --- a/module/icp/algs/modes/gcm.c +++ b/module/icp/algs/modes/gcm.c @@ -348,8 +348,14 @@ gcm_mode_decrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length, ctx->gcm_pt_buf = NULL; return (CRYPTO_HOST_MEMORY); } - bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); - vmem_free(ctx->gcm_pt_buf, ctx->gcm_pt_buf_len); + + if (ctx->gcm_pt_buf != NULL) { + bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); + vmem_free(ctx->gcm_pt_buf, ctx->gcm_pt_buf_len); + } else { + ASSERT0(ctx->gcm_pt_buf_len); + } + ctx->gcm_pt_buf = new; ctx->gcm_pt_buf_len = new_len; bcopy(data, &ctx->gcm_pt_buf[ctx->gcm_processed_data_len], @@ -554,8 +560,15 @@ gcm_init(gcm_ctx_t *ctx, unsigned char *iv, size_t iv_len, * There's not a block full of data, pad rest of * buffer with zero */ - bzero(authp, block_size); - bcopy(&(auth_data[processed]), authp, remainder); + + if (auth_data != NULL) { + bzero(authp, block_size); + bcopy(&(auth_data[processed]), + authp, remainder); + } else { + ASSERT0(remainder); + } + datap = (uint8_t *)authp; remainder = 0; } else { diff --git a/module/icp/io/sha2_mod.c b/module/icp/io/sha2_mod.c index a43c7c5b7b8e..7ee16e1372ad 100644 --- a/module/icp/io/sha2_mod.c +++ b/module/icp/io/sha2_mod.c @@ -710,8 +710,13 @@ sha2_mac_init_ctx(sha2_hmac_ctx_t *ctx, void *keyval, uint_t length_in_bytes) (void) bzero(ipad, block_size); (void) bzero(opad, block_size); - (void) bcopy(keyval, ipad, length_in_bytes); - (void) bcopy(keyval, opad, length_in_bytes); + + if (keyval != NULL) { + (void) bcopy(keyval, ipad, length_in_bytes); + (void) bcopy(keyval, opad, length_in_bytes); + } else { + ASSERT0(length_in_bytes); + } /* XOR key with ipad (0x36) and opad (0x5c) */ for (i = 0; i < blocks_per_int64; i ++) { diff --git a/module/zcommon/zfs_fletcher.c b/module/zcommon/zfs_fletcher.c index 0678607ad7a4..16773d4de077 100644 --- a/module/zcommon/zfs_fletcher.c +++ b/module/zcommon/zfs_fletcher.c @@ -300,18 +300,21 @@ fletcher_2_byteswap(const void *buf, uint64_t size, (void) fletcher_2_incremental_byteswap((void *) buf, size, zcp); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_scalar_init(fletcher_4_ctx_t *ctx) { ZIO_SET_CHECKSUM(&ctx->scalar, 0, 0, 0, 0); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_scalar_fini(fletcher_4_ctx_t *ctx, zio_cksum_t *zcp) { memcpy(zcp, &ctx->scalar, sizeof (zio_cksum_t)); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_scalar_native(fletcher_4_ctx_t *ctx, const void *buf, uint64_t size) @@ -335,6 +338,7 @@ fletcher_4_scalar_native(fletcher_4_ctx_t *ctx, const void *buf, ZIO_SET_CHECKSUM(&ctx->scalar, a, b, c, d); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_scalar_byteswap(fletcher_4_ctx_t *ctx, const void *buf, uint64_t size) diff --git a/module/zcommon/zfs_fletcher_aarch64_neon.c b/module/zcommon/zfs_fletcher_aarch64_neon.c index c95a71681584..e84d69eb3415 100644 --- a/module/zcommon/zfs_fletcher_aarch64_neon.c +++ b/module/zcommon/zfs_fletcher_aarch64_neon.c @@ -48,12 +48,14 @@ #include #include +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_aarch64_neon_init(fletcher_4_ctx_t *ctx) { bzero(ctx->aarch64_neon, 4 * sizeof (zfs_fletcher_aarch64_neon_t)); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_aarch64_neon_fini(fletcher_4_ctx_t *ctx, zio_cksum_t *zcp) { diff --git a/module/zcommon/zfs_fletcher_avx512.c b/module/zcommon/zfs_fletcher_avx512.c index 963f089b04f5..8ee438ab9325 100644 --- a/module/zcommon/zfs_fletcher_avx512.c +++ b/module/zcommon/zfs_fletcher_avx512.c @@ -35,12 +35,14 @@ #define __asm __asm__ __volatile__ #endif +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_avx512f_init(fletcher_4_ctx_t *ctx) { bzero(ctx->avx512, 4 * sizeof (zfs_fletcher_avx512_t)); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_avx512f_fini(fletcher_4_ctx_t *ctx, zio_cksum_t *zcp) { diff --git a/module/zcommon/zfs_fletcher_intel.c b/module/zcommon/zfs_fletcher_intel.c index 5136a01eca51..16e61a96f8b1 100644 --- a/module/zcommon/zfs_fletcher_intel.c +++ b/module/zcommon/zfs_fletcher_intel.c @@ -47,12 +47,14 @@ #include #include +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_avx2_init(fletcher_4_ctx_t *ctx) { bzero(ctx->avx, 4 * sizeof (zfs_fletcher_avx_t)); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_avx2_fini(fletcher_4_ctx_t *ctx, zio_cksum_t *zcp) { diff --git a/module/zcommon/zfs_fletcher_sse.c b/module/zcommon/zfs_fletcher_sse.c index 15ce9b07ffbe..fc5938488e61 100644 --- a/module/zcommon/zfs_fletcher_sse.c +++ b/module/zcommon/zfs_fletcher_sse.c @@ -49,12 +49,14 @@ #include #include +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_sse2_init(fletcher_4_ctx_t *ctx) { bzero(ctx->sse, 4 * sizeof (zfs_fletcher_sse_t)); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_sse2_fini(fletcher_4_ctx_t *ctx, zio_cksum_t *zcp) { diff --git a/module/zcommon/zfs_fletcher_superscalar.c b/module/zcommon/zfs_fletcher_superscalar.c index 153f5c7d75e3..73a74b9ae0ab 100644 --- a/module/zcommon/zfs_fletcher_superscalar.c +++ b/module/zcommon/zfs_fletcher_superscalar.c @@ -47,12 +47,14 @@ #include #include +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_superscalar_init(fletcher_4_ctx_t *ctx) { bzero(ctx->superscalar, 4 * sizeof (zfs_fletcher_superscalar_t)); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_superscalar_fini(fletcher_4_ctx_t *ctx, zio_cksum_t *zcp) { @@ -68,6 +70,7 @@ fletcher_4_superscalar_fini(fletcher_4_ctx_t *ctx, zio_cksum_t *zcp) ZIO_SET_CHECKSUM(zcp, A, B, C, D); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_superscalar_native(fletcher_4_ctx_t *ctx, const void *buf, uint64_t size) @@ -107,6 +110,7 @@ fletcher_4_superscalar_native(fletcher_4_ctx_t *ctx, ctx->superscalar[3].v[1] = d2; } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_superscalar_byteswap(fletcher_4_ctx_t *ctx, const void *buf, uint64_t size) diff --git a/module/zcommon/zfs_fletcher_superscalar4.c b/module/zcommon/zfs_fletcher_superscalar4.c index 75e6a3baf980..2dbf8bbb8146 100644 --- a/module/zcommon/zfs_fletcher_superscalar4.c +++ b/module/zcommon/zfs_fletcher_superscalar4.c @@ -47,12 +47,14 @@ #include #include +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_superscalar4_init(fletcher_4_ctx_t *ctx) { bzero(ctx->superscalar, 4 * sizeof (zfs_fletcher_superscalar_t)); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_superscalar4_fini(fletcher_4_ctx_t *ctx, zio_cksum_t *zcp) { @@ -82,6 +84,7 @@ fletcher_4_superscalar4_fini(fletcher_4_ctx_t *ctx, zio_cksum_t *zcp) ZIO_SET_CHECKSUM(zcp, A, B, C, D); } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_superscalar4_native(fletcher_4_ctx_t *ctx, const void *buf, uint64_t size) @@ -147,6 +150,7 @@ fletcher_4_superscalar4_native(fletcher_4_ctx_t *ctx, ctx->superscalar[3].v[3] = d4; } +ZFS_NO_SANITIZE_UNDEFINED static void fletcher_4_superscalar4_byteswap(fletcher_4_ctx_t *ctx, const void *buf, uint64_t size) diff --git a/module/zfs/btree.c b/module/zfs/btree.c index 57b9dbbb2b50..5bcf621d5994 100644 --- a/module/zfs/btree.c +++ b/module/zfs/btree.c @@ -536,7 +536,6 @@ zfs_btree_insert_into_parent(zfs_btree_t *tree, zfs_btree_hdr_t *old_node, ASSERT3P(old_node->bth_parent, ==, new_node->bth_parent); uint64_t size = tree->bt_elem_size; zfs_btree_core_t *parent = old_node->bth_parent; - zfs_btree_hdr_t *par_hdr = &parent->btc_hdr; /* * If this is the root node we were splitting, we create a new root @@ -568,6 +567,7 @@ zfs_btree_insert_into_parent(zfs_btree_t *tree, zfs_btree_hdr_t *old_node, * Since we have the new separator, binary search for where to put * new_node. */ + zfs_btree_hdr_t *par_hdr = &parent->btc_hdr; zfs_btree_index_t idx; ASSERT(par_hdr->bth_core); VERIFY3P(zfs_btree_find_in_buf(tree, parent->btc_elems, @@ -1898,7 +1898,8 @@ static uint64_t zfs_btree_verify_counts_helper(zfs_btree_t *tree, zfs_btree_hdr_t *hdr) { if (!hdr->bth_core) { - if (tree->bt_root != hdr && hdr != &tree->bt_bulk->btl_hdr) { + if (tree->bt_root != hdr && tree->bt_bulk && + hdr != &tree->bt_bulk->btl_hdr) { uint64_t capacity = P2ALIGN((BTREE_LEAF_SIZE - sizeof (zfs_btree_hdr_t)) / tree->bt_elem_size, 2); VERIFY3U(hdr->bth_count, >=, (capacity / 2) - 1); diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index 7e6a7dbec1d7..a844ee2a73c5 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -4531,8 +4531,8 @@ vdev_stat_update(zio_t *zio, uint64_t psize) vdev_t *vd = zio->io_vd ? zio->io_vd : rvd; vdev_t *pvd; uint64_t txg = zio->io_txg; - vdev_stat_t *vs = &vd->vdev_stat; - vdev_stat_ex_t *vsx = &vd->vdev_stat_ex; + vdev_stat_t *vs = vd ? &vd->vdev_stat : NULL; + vdev_stat_ex_t *vsx = vd ? &vd->vdev_stat_ex : NULL; zio_type_t type = zio->io_type; int flags = zio->io_flags; diff --git a/module/zstd/lib/zstd.c b/module/zstd/lib/zstd.c index 2766e5b74f55..b57a9ef40d7b 100644 --- a/module/zstd/lib/zstd.c +++ b/module/zstd/lib/zstd.c @@ -279,12 +279,9 @@ intptr_t __msan_test_shadow(const volatile void *x, size_t size); #endif /* detects whether we are being compiled under asan */ -#if defined (__has_feature) -# if __has_feature(address_sanitizer) -# define ADDRESS_SANITIZER 1 -# endif -#elif defined(__SANITIZE_ADDRESS__) +#if defined (ZFS_ASAN_ENABLED) # define ADDRESS_SANITIZER 1 +# define ZSTD_ASAN_DONT_POISON_WORKSPACE #endif #if defined (ADDRESS_SANITIZER) diff --git a/module/zstd/zfs_zstd.c b/module/zstd/zfs_zstd.c index 2c698716c971..9ff9ec3ec73c 100644 --- a/module/zstd/zfs_zstd.c +++ b/module/zstd/zfs_zstd.c @@ -207,11 +207,7 @@ static struct zstd_pool *zstd_mempool_dctx; * and while ASAN does this, KASAN defines that and does not. So to avoid * changing the external code, we do this. */ -#if defined(__has_feature) -#if __has_feature(address_sanitizer) -#define ADDRESS_SANITIZER 1 -#endif -#elif defined(__SANITIZE_ADDRESS__) +#if defined(ZFS_ASAN_ENABLED) #define ADDRESS_SANITIZER 1 #endif #if defined(_KERNEL) && defined(ADDRESS_SANITIZER) diff --git a/rpm/generic/zfs.spec.in b/rpm/generic/zfs.spec.in index 4980c08f36c0..d66de0f33f29 100644 --- a/rpm/generic/zfs.spec.in +++ b/rpm/generic/zfs.spec.in @@ -55,6 +55,7 @@ %bcond_with debug %bcond_with debuginfo %bcond_with asan +%bcond_with ubsan %bcond_with systemd %bcond_with pam %bcond_without pyzfs @@ -344,6 +345,12 @@ image which is ZFS aware. %define asan --disable-asan %endif +%if %{with ubsan} + %define ubsan --enable-ubsan +%else + %define ubsan --disable-ubsan +%endif + %if 0%{?_systemd} %define systemd --enable-systemd --with-systemdunitdir=%{_unitdir} --with-systemdpresetdir=%{_presetdir} --with-systemdmodulesloaddir=%{_modulesloaddir} --with-systemdgeneratordir=%{_systemdgeneratordir} --disable-sysvinit %define systemd_svcs zfs-import-cache.service zfs-import-scan.service zfs-mount.service zfs-share.service zfs-zed.service zfs.target zfs-import.target zfs-volume-wait.service zfs-volumes.target @@ -379,6 +386,7 @@ image which is ZFS aware. %{debug} \ %{debuginfo} \ %{asan} \ + %{ubsan} \ %{systemd} \ %{pam} \ %{pyzfs} diff --git a/scripts/zfs-tests.sh b/scripts/zfs-tests.sh index 8c1b511664f2..fd734aed260a 100755 --- a/scripts/zfs-tests.sh +++ b/scripts/zfs-tests.sh @@ -148,7 +148,7 @@ trap cleanup EXIT # be dangerous and should only be used in a dedicated test environment. # cleanup_all() { - TEST_POOLS=$(sudo "$ZPOOL" list -H -o name | grep testpool) + TEST_POOLS=$(sudo env ASAN_OPTIONS=detect_leaks=false "$ZPOOL" list -H -o name | grep testpool) if [ "$UNAME" = "FreeBSD" ] ; then TEST_LOOPBACKS=$(sudo "${LOSETUP}" -l) else @@ -160,7 +160,7 @@ cleanup_all() { msg "--- Cleanup ---" msg "Removing pool(s): $(echo "${TEST_POOLS}" | tr '\n' ' ')" for TEST_POOL in $TEST_POOLS; do - sudo "$ZPOOL" destroy "${TEST_POOL}" + sudo env ASAN_OPTIONS=detect_leaks=false "$ZPOOL" destroy "${TEST_POOL}" done if [ "$UNAME" != "FreeBSD" ] ; then @@ -554,7 +554,7 @@ fi # space-delimited to newline-delimited. # if [ -z "${KEEP}" ]; then - KEEP="$(sudo "$ZPOOL" list -H -o name)" + KEEP="$(sudo env ASAN_OPTIONS=detect_leaks=false "$ZPOOL" list -H -o name)" if [ -z "${KEEP}" ]; then KEEP="rpool" fi diff --git a/scripts/zloop.sh b/scripts/zloop.sh index 1a512657be69..dd89ff6fdf29 100755 --- a/scripts/zloop.sh +++ b/scripts/zloop.sh @@ -215,7 +215,8 @@ shift $((OPTIND - 1)) # enable core dumps ulimit -c unlimited -export ASAN_OPTIONS=abort_on_error=1:disable_coredump=0 +export ASAN_OPTIONS=abort_on_error=true:halt_on_error=true:allocator_may_return_null=true:disable_coredump=false:detect_stack_use_after_return=true +export UBSAN_OPTIONS=abort_on_error=true:halt_on_error=true:print_stacktrace=true if [[ -f "$(core_file)" ]]; then echo -n "There's a core dump here you might want to look at first... " diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg index 13fee67521d7..51052e801e87 100644 --- a/tests/zfs-tests/include/commands.cfg +++ b/tests/zfs-tests/include/commands.cfg @@ -51,6 +51,7 @@ export SYSTEM_FILES_COMMON='arp iostat kill ksh + ldd ln logname ls diff --git a/tests/zfs-tests/include/default.cfg.in b/tests/zfs-tests/include/default.cfg.in index 1a9cc5a2bb62..cf382cfe994c 100644 --- a/tests/zfs-tests/include/default.cfg.in +++ b/tests/zfs-tests/include/default.cfg.in @@ -155,6 +155,21 @@ done export MAX_PARTITIONS=8 +if [ "@ASAN_ENABLED@" = "yes" ]; then + export ASAN_OPTIONS=abort_on_error=true:halt_on_error=true:allocator_may_return_null=true:disable_coredump=false:detect_stack_use_after_return=true + + # TODO + # disable memory leaks detection + # there are quite many of them and they are not as + # destructive to CLI programs as they are to daemons + export ASAN_OPTIONS="$ASAN_OPTIONS:detect_leaks=false" +fi + +if [ "@UBSAN_ENABLED@" = "yes" ]; then + export UBSAN_OPTIONS=abort_on_error=true:halt_on_error=true:print_stacktrace=true +fi + + case $(uname -o) in GNU/Linux) unpack_opts="--sparse -xf" diff --git a/tests/zfs-tests/tests/functional/alloc_class/alloc_class_013_pos.ksh b/tests/zfs-tests/tests/functional/alloc_class/alloc_class_013_pos.ksh index 7a0eb53436a1..624cab88af0c 100755 --- a/tests/zfs-tests/tests/functional/alloc_class/alloc_class_013_pos.ksh +++ b/tests/zfs-tests/tests/functional/alloc_class/alloc_class_013_pos.ksh @@ -41,8 +41,8 @@ log_must display_status "$TESTPOOL" # log_must zfs create -o dedup=on -V 2G $TESTPOOL/$TESTVOL - -log_must eval "new_fs $ZVOL_DEVDIR/$TESTPOOL/$TESTVOL >/dev/null 2>&1" +block_device_wait "$ZVOL_DEVDIR/$TESTPOOL/$TESTVOL" +log_must eval "new_fs $ZVOL_DEVDIR/$TESTPOOL/$TESTVOL >/dev/null" sync_pool log_must zpool list -v $TESTPOOL diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs/zfs_002_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs/zfs_002_pos.ksh index 1290d888a947..51a7ce1d96d7 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs/zfs_002_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs/zfs_002_pos.ksh @@ -97,7 +97,6 @@ if is_linux; then ulimit -c unlimited echo "$corefile" >/proc/sys/kernel/core_pattern echo 0 >/proc/sys/kernel/core_uses_pid - export ASAN_OPTIONS="abort_on_error=1:disable_coredump=0" elif is_freebsd; then ulimit -c unlimited savedcorefile=$(sysctl -n kern.corefile) diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool/zpool_002_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool/zpool_002_pos.ksh index caf8a9a2d0ee..e4ffa2a6c028 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zpool/zpool_002_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zpool/zpool_002_pos.ksh @@ -95,7 +95,6 @@ elif is_freebsd; then fi ulimit -c unlimited -export ASAN_OPTIONS="abort_on_error=1:disable_coredump=0" export ZFS_ABORT=yes for subcmd in "${cmds[@]}" "${badparams[@]}"; do diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool/zpool_003_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool/zpool_003_pos.ksh index 71d73c0f80e5..6f15c09a5229 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zpool/zpool_003_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zpool/zpool_003_pos.ksh @@ -89,7 +89,6 @@ elif is_freebsd; then fi ulimit -c unlimited -export ASAN_OPTIONS="abort_on_error=1:disable_coredump=0" export ZFS_ABORT=yes zpool >/dev/null 2>&1 diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_pos.ksh index fbb0c291046c..a60e12e1481e 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_pos.ksh @@ -35,7 +35,7 @@ DISK1=${DISKS%% *} log_must zpool create -f $TESTPOOL $DISK1 -log_must zpool trim $TESTPOOL +log_must zpool trim -r 1 "$TESTPOOL" [[ -z "$(trim_progress $TESTPOOL $DISK1)" ]] && \ log_fail "TRIM did not start" diff --git a/tests/zfs-tests/tests/functional/pam/pam_basic.ksh b/tests/zfs-tests/tests/functional/pam/pam_basic.ksh index f146a6e5f157..eab819ab8d24 100755 --- a/tests/zfs-tests/tests/functional/pam/pam_basic.ksh +++ b/tests/zfs-tests/tests/functional/pam/pam_basic.ksh @@ -22,6 +22,10 @@ . $STF_SUITE/tests/functional/pam/utilities.kshlib +if [ -n "$ASAN_OPTIONS" ]; then + export LD_PRELOAD=$(ldd "$(command -v zfs)" | awk '/libasan\.so/ {print $3}') +fi + log_mustnot ismounted "$TESTPOOL/pam/${username}" keystatus unavailable diff --git a/tests/zfs-tests/tests/functional/pam/pam_nounmount.ksh b/tests/zfs-tests/tests/functional/pam/pam_nounmount.ksh index eb9976f2f776..29ce437b40ee 100755 --- a/tests/zfs-tests/tests/functional/pam/pam_nounmount.ksh +++ b/tests/zfs-tests/tests/functional/pam/pam_nounmount.ksh @@ -22,6 +22,10 @@ . $STF_SUITE/tests/functional/pam/utilities.kshlib +if [ -n "$ASAN_OPTIONS" ]; then + export LD_PRELOAD=$(ldd "$(command -v zfs)" | awk '/libasan\.so/ {print $3}') +fi + log_mustnot ismounted "$TESTPOOL/pam/${username}" keystatus unavailable diff --git a/tests/zfs-tests/tests/functional/pam/pam_short_password.ksh b/tests/zfs-tests/tests/functional/pam/pam_short_password.ksh index 1f72c9468a46..100f279fffed 100755 --- a/tests/zfs-tests/tests/functional/pam/pam_short_password.ksh +++ b/tests/zfs-tests/tests/functional/pam/pam_short_password.ksh @@ -27,6 +27,10 @@ . $STF_SUITE/tests/functional/pam/utilities.kshlib +if [ -n "$ASAN_OPTIONS" ]; then + export LD_PRELOAD=$(ldd "$(command -v zfs)" | awk '/libasan\.so/ {print $3}') +fi + if [[ -z pamservice ]]; then pamservice=pam_zfs_key_test fi diff --git a/tests/zfs-tests/tests/functional/pyzfs/pyzfs_unittest.ksh.in b/tests/zfs-tests/tests/functional/pyzfs/pyzfs_unittest.ksh.in index 1f58d8116b68..84e20e2e7eba 100755 --- a/tests/zfs-tests/tests/functional/pyzfs/pyzfs_unittest.ksh.in +++ b/tests/zfs-tests/tests/functional/pyzfs/pyzfs_unittest.ksh.in @@ -16,6 +16,10 @@ . $STF_SUITE/include/libtest.shlib +if [ -n "$ASAN_OPTIONS" ]; then + export LD_PRELOAD=$(ldd "$(command -v zfs)" | awk '/libasan\.so/ {print $3}') +fi + # # DESCRIPTION: # Verify the libzfs_core Python test suite can be run successfully diff --git a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c index 55d939abd23b..39a72c22ab84 100644 --- a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c +++ b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c @@ -58,7 +58,7 @@ main(void) exit(3); } - if ((ret = system("sudo zpool freeze $TESTPOOL"))) { + if ((ret = system("sudo -E zpool freeze $TESTPOOL"))) { if (ret == -1) perror("system \"zpool freeze\""); else @@ -69,7 +69,7 @@ main(void) close(fd); - if ((ret = system("sudo zpool export $TESTPOOL"))) { + if ((ret = system("sudo -E zpool export $TESTPOOL"))) { if (ret == -1) perror("system \"zpool export\""); else @@ -78,7 +78,7 @@ main(void) exit(4); } - if ((ret = system("sudo zpool import $TESTPOOL"))) { + if ((ret = system("sudo -E zpool import $TESTPOOL"))) { if (ret == -1) perror("system \"zpool import\""); else