CLANG_VERSION: 16
CLANG: "clang-${CLANG_VERSION}"
SCAN_BUILD: "scan-build-${CLANG_VERSION}"
- ASAN_SYMBOLIZER_PATH: "/usr/lib/llvm-${CLANG_VERSION}/bin/llvm-symbolizer"
+ LLVM_SYMBOLIZER: "/usr/lib/llvm-${CLANG_VERSION}/bin/llvm-symbolizer"
CLANG_FORMAT: "clang-format-${CLANG_VERSION}"
CFLAGS_COMMON: -fno-omit-frame-pointer -fno-optimize-sibling-calls -O1 -g -Wall -Wextra
# Pass run-time flags to AddressSanitizer to get core dumps on error.
ASAN_OPTIONS: abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1
- TSAN_OPTIONS_COMMON: "disable_coredump=0 second_deadlock_stack=1 history_size=7 log_exe_name=true log_path=tsan"
+ ASAN_SYMBOLIZER_PATH: "${LLVM_SYMBOLIZER}"
+
+ TSAN_OPTIONS_COMMON: "disable_coredump=0 second_deadlock_stack=1 atexit_sleep_ms=1000 history_size=7 log_exe_name=true log_path=tsan"
+ TSAN_SYMBOLIZER: "external_symbolizer_path=/usr/bin/llvm-symbolizer"
+ TSAN_SUPPRESSIONS: "suppressions=${CI_PROJECT_DIR}/.tsan-suppress"
+ TSAN_OPTIONS_DEFAULT: "${TSAN_OPTIONS_COMMON} ${TSAN_SUPPRESSIONS} ${TSAN_SYMBOLIZER}"
+
UBSAN_OPTIONS: "halt_on_error=1:abort_on_error=1:disable_coredump=0"
TARBALL_EXTENSION: xz
system:gcc:tsan:
variables:
- TSAN_OPTIONS: "${TSAN_OPTIONS_COMMON} external_symbolizer_path=/usr/bin/llvm-symbolizer"
+ TSAN_OPTIONS: "${TSAN_OPTIONS_DEFAULT}"
<<: *fedora_37_amd64_image
<<: *system_test_tsan_job
needs:
unit:gcc:tsan:
variables:
- TSAN_OPTIONS: "${TSAN_OPTIONS_COMMON} external_symbolizer_path=/usr/bin/llvm-symbolizer"
+ TSAN_OPTIONS: "${TSAN_OPTIONS_DEFAULT}"
<<: *fedora_37_amd64_image
<<: *unit_test_tsan_job
needs:
system:clang:tsan:
variables:
- TSAN_OPTIONS: "${TSAN_OPTIONS_COMMON} external_symbolizer_path=/usr/lib/llvm-${CLANG_VERSION}/bin/llvm-symbolizer"
+ TSAN_OPTIONS: "${TSAN_OPTIONS_COMMON} ${TSAN_SUPPRESSIONS} external_symbolizer_path=${LLVM_SYMBOLIZER}"
<<: *base_image
<<: *system_test_tsan_job
needs:
unit:clang:tsan:
variables:
- TSAN_OPTIONS: "${TSAN_OPTIONS_COMMON} external_symbolizer_path=/usr/lib/llvm-${CLANG_VERSION}/bin/llvm-symbolizer suppressions=$CI_PROJECT_DIR/tsan-suppressions.txt"
+ TSAN_OPTIONS: "${TSAN_OPTIONS_COMMON} suppressions=$CI_PROJECT_DIR/.tsan-suppress-extra external_symbolizer_path=${LLVM_SYMBOLIZER}"
<<: *base_image
<<: *unit_test_tsan_job
needs:
LDFLAGS: "-fsanitize=thread"
EXTRA_CONFIGURE: "--enable-pthread-rwlock --without-jemalloc"
MAX_DISAGREEMENTS_PERCENTAGE: "0.5"
- TSAN_OPTIONS: "${TSAN_OPTIONS_COMMON} external_symbolizer_path=/usr/bin/llvm-symbolizer"
+ TSAN_OPTIONS: "${TSAN_OPTIONS_DEFAULT}"
script:
- bash respdiff.sh -s named -q "${PWD}/10k_a.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named"
after_script:
LDFLAGS: "-fsanitize=thread"
EXTRA_CONFIGURE: "--enable-pthread-rwlock --without-jemalloc"
MAX_DISAGREEMENTS_PERCENTAGE: "0.5"
- TSAN_OPTIONS: "${TSAN_OPTIONS_COMMON} external_symbolizer_path=/usr/bin/llvm-symbolizer"
+ TSAN_OPTIONS: "${TSAN_OPTIONS_DEFAULT}"
script:
- bash respdiff.sh -s named -q "${PWD}/100k_mixed.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named"
after_script:
.gitlab-ci.yml
.lgtm.yml
.pylintrc
+ .tsan-suppress
+ .tsan-suppress-extra
.uncrustify.cfg
doc/misc/*.zoneopt
doc/misc/options
doc/misc/rndc.grammar
- tsan-suppressions.txt
sonar-project.properties
Copyright: Internet Systems Consortium, Inc. ("ISC")
License: CC0-1.0
--- /dev/null
+# be more selective with liburcu
+race:rcu_barrier
+race:rcu_memb_barrier
--- /dev/null
+# Uninstrumented libraries
+called_from_lib:libfstrm.so
+called_from_lib:libdummyrpz.so
+# be more selective with liburcu
+race:rcu_barrier
+race:rcu_memb_barrier
LOG_FLAGS="$log_flags" \
TEST_LARGE_MAP="${TEST_LARGE_MAP}" \
CI_ENABLE_ALL_TESTS="${CI_ENABLE_ALL_TESTS}" \
+ ${TSAN_OPTIONS:+"TSAN_OPTIONS=${TSAN_OPTIONS}"} \
${VIRTUAL_ENV:+"VIRTUAL_ENV=${VIRTUAL_ENV}"} \
${PERL5LIB:+"PERL5LIB=${PERL5LIB}"} \
make -e check
* asynchronous cleanup
*/
static void
-reclaim_chunks_cb(struct rcu_head *rcu_head) {
- qp_rcuctx_t *rcuctx = caa_container_of(rcu_head, qp_rcuctx_t, rcu_head);
- __tsan_acquire(rcuctx);
+reclaim_chunks_cb(struct rcu_head *arg) {
+ qp_rcuctx_t *rcuctx = isc_urcu_container(arg, qp_rcuctx_t, rcu_head);
REQUIRE(QPRCU_VALID(rcuctx));
dns_qpmulti_t *multi = rcuctx->multi;
REQUIRE(QPMULTI_VALID(multi));
}
}
- __tsan_release(rcuctx);
- call_rcu(&rcuctx->rcu_head, reclaim_chunks_cb);
+ isc_urcu_cleanup(rcuctx, rcu_head, reclaim_chunks_cb);
LOG_STATS("qp will reclaim %u chunks", count);
}
}
static void
-qpmulti_destroy_cb(struct rcu_head *rcu_head) {
- qp_rcuctx_t *rcuctx = caa_container_of(rcu_head, qp_rcuctx_t, rcu_head);
- __tsan_acquire(rcuctx);
+qpmulti_destroy_cb(struct rcu_head *arg) {
+ qp_rcuctx_t *rcuctx = isc_urcu_container(arg, qp_rcuctx_t, rcu_head);
REQUIRE(QPRCU_VALID(rcuctx));
dns_qpmulti_t *multi = rcuctx->multi;
REQUIRE(QPMULTI_VALID(multi));
.multi = multi,
};
isc_mem_attach(qp->mctx, &rcuctx->mctx);
- __tsan_release(rcuctx);
- call_rcu(&rcuctx->rcu_head, qpmulti_destroy_cb);
+ isc_urcu_cleanup(rcuctx, rcu_head, qpmulti_destroy_cb);
}
/***********************************************************************
static void
free_gluelist_rcu(struct rcu_head *rcu_head) {
- rbtdb_glue_t *glue = caa_container_of(rcu_head, rbtdb_glue_t, rcu_head);
- __tsan_acquire(glue);
+ rbtdb_glue_t *glue = isc_urcu_container(rcu_head, rbtdb_glue_t,
+ rcu_head);
free_gluelist(glue);
}
caa_container_of(node, rdatasetheader_t, wfs_node);
rbtdb_glue_t *glue = rcu_xchg_pointer(&header->glue_list, NULL);
- __tsan_release(glue);
- call_rcu(&glue->rcu_head, free_gluelist_rcu);
+ isc_urcu_cleanup(glue, rcu_head, free_gluelist_rcu);
}
rcu_read_unlock();
}
*/
struct cds_wfcq_node *node, *next;
__cds_wfcq_for_each_blocking_safe(&jobs.head, &jobs.tail, node, next) {
- isc_job_t *job = caa_container_of(node, isc_job_t, wfcq_node);
- __tsan_acquire(job);
+ isc_job_t *job = isc_urcu_container(node, isc_job_t, wfcq_node);
job->cb(job->cbarg);
#pragma GCC diagnostic pop
+/*
+ * Help thread sanitizer to understand `call_rcu()`:
+ *
+ * The `rcu_head` argument to `call_rcu()` is expected to be embedded
+ * in a structure defined by the caller, which is named `_rcuctx` in
+ * these macros. The callback function uses `caa_container_of()` to
+ * recover the `rcuctx` pointer from the `_rcu_head` pointer that is
+ * passed to the callback.
+ *
+ * We explain the ordering dependency to tsan by releasing `_rcuctx`
+ * pointer before `call_rcu()` and acquiring it in the callback
+ * funtion. We pass the outer `_rcuctx` pointer to the `__tsan_`
+ * barriers, because it should match a pointer that is known by tsan
+ * to have been returned by `malloc()`.
+ */
+
+#define isc_urcu_cleanup(ptr, member, func) \
+ { \
+ __tsan_release(ptr); \
+ call_rcu(&(ptr)->member, func); \
+ }
+
+#define isc_urcu_container(ptr, type, member) \
+ ({ \
+ type *_ptr = caa_container_of(ptr, type, member); \
+ __tsan_acquire(_ptr); \
+ _ptr; \
+ })
+
#if defined(RCU_QSBR)
/*
return;
}
- isc_job_t *job = caa_container_of(node, isc_job_t, wfcq_node);
- __tsan_acquire(job);
+ isc_job_t *job = isc_urcu_container(node, isc_job_t, wfcq_node);
job->cb(job->cbarg);
}
+++ /dev/null
-# Uninstrumented library.
-called_from_lib:libfstrm.so
-called_from_lib:libdummyrpz.so