summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/arch/bytes.h2
-rw-r--r--src/lib/buf/buffers.c1
-rw-r--r--src/lib/cc/compat_compiler.h2
-rw-r--r--src/lib/cc/torint.h4
-rw-r--r--src/lib/compress/compress_lzma.c4
-rw-r--r--src/lib/compress/compress_zstd.c6
-rw-r--r--src/lib/confmgt/.may_include2
-rw-r--r--src/lib/confmgt/confparse.c1239
-rw-r--r--src/lib/confmgt/confparse.h212
-rw-r--r--src/lib/confmgt/include.am2
-rw-r--r--src/lib/confmgt/type_defs.c24
-rw-r--r--src/lib/confmgt/var_type_def_st.h6
-rw-r--r--src/lib/crypt_ops/aes_openssl.c2
-rw-r--r--src/lib/crypt_ops/compat_openssl.h2
-rw-r--r--src/lib/crypt_ops/crypto_dh_nss.c2
-rw-r--r--src/lib/crypt_ops/crypto_dh_openssl.c8
-rw-r--r--src/lib/crypt_ops/crypto_digest.c6
-rw-r--r--src/lib/crypt_ops/crypto_digest_openssl.c6
-rw-r--r--src/lib/crypt_ops/crypto_hkdf.c4
-rw-r--r--src/lib/crypt_ops/crypto_ope.c2
-rw-r--r--src/lib/crypt_ops/crypto_openssl_mgt.c6
-rw-r--r--src/lib/crypt_ops/crypto_rand.c4
-rw-r--r--src/lib/crypt_ops/crypto_rsa_openssl.c4
-rw-r--r--src/lib/crypt_ops/crypto_s2k.c4
-rw-r--r--src/lib/crypt_ops/crypto_sys.h2
-rw-r--r--src/lib/defs/logging_types.h2
-rw-r--r--src/lib/dispatch/.may_include1
-rw-r--r--src/lib/dispatch/dispatch_cfg.h6
-rw-r--r--src/lib/dispatch/dispatch_new.c12
-rw-r--r--src/lib/encoding/pem.c2
-rw-r--r--src/lib/err/backtrace.c78
-rw-r--r--src/lib/err/torerr.c64
-rw-r--r--src/lib/err/torerr.h7
-rw-r--r--src/lib/err/torerr_sys.c5
-rw-r--r--src/lib/evloop/compat_libevent.c2
-rw-r--r--src/lib/evloop/procmon.c2
-rw-r--r--src/lib/fs/conffile.c10
-rw-r--r--src/lib/fs/dir.c4
-rw-r--r--src/lib/fs/files.h4
-rw-r--r--src/lib/fs/mmap.c16
-rw-r--r--src/lib/fs/mmap.h5
-rw-r--r--src/lib/fs/path.c10
-rw-r--r--src/lib/lock/compat_mutex.c10
-rw-r--r--src/lib/lock/compat_mutex_pthreads.c16
-rw-r--r--src/lib/log/log.c159
-rw-r--r--src/lib/log/log.h36
-rw-r--r--src/lib/log/util_bug.c13
-rw-r--r--src/lib/log/util_bug.h4
-rw-r--r--src/lib/log/win32err.c2
-rw-r--r--src/lib/malloc/malloc.h2
-rw-r--r--src/lib/malloc/map_anon.c38
-rw-r--r--src/lib/math/fp.c19
-rw-r--r--src/lib/math/prob_distr.c76
-rw-r--r--src/lib/math/prob_distr.h46
-rw-r--r--src/lib/memarea/memarea.c4
-rw-r--r--src/lib/meminfo/meminfo.c2
-rw-r--r--src/lib/net/address.c2
-rw-r--r--src/lib/net/nettypes.h2
-rw-r--r--src/lib/net/network_sys.h2
-rw-r--r--src/lib/net/resolve.c33
-rw-r--r--src/lib/net/resolve.h17
-rw-r--r--src/lib/net/socket.c8
-rw-r--r--src/lib/net/socket.h2
-rw-r--r--src/lib/net/socketpair.c2
-rw-r--r--src/lib/osinfo/uname.c2
-rw-r--r--src/lib/process/daemon.c2
-rw-r--r--src/lib/process/env.c2
-rw-r--r--src/lib/process/process.c3
-rw-r--r--src/lib/process/process.h4
-rw-r--r--src/lib/process/process_unix.c11
-rw-r--r--src/lib/process/process_win32.c18
-rw-r--r--src/lib/process/restrict.c4
-rw-r--r--src/lib/process/setuid.c10
-rw-r--r--src/lib/process/winprocess_sys.c2
-rw-r--r--src/lib/sandbox/sandbox.c3
-rw-r--r--src/lib/sandbox/sandbox.h2
-rw-r--r--src/lib/smartlist_core/smartlist_core.h2
-rw-r--r--src/lib/testsupport/testsupport.h4
-rw-r--r--src/lib/thread/compat_threads.c10
-rw-r--r--src/lib/thread/thread_sys.h2
-rw-r--r--src/lib/thread/threads.h14
-rw-r--r--src/lib/time/compat_time.c4
-rw-r--r--src/lib/time/compat_time.h4
-rw-r--r--src/lib/tls/buffers_tls.c4
-rw-r--r--src/lib/tls/tortls.h4
-rw-r--r--src/lib/tls/tortls_openssl.c6
-rw-r--r--src/lib/tls/x509_openssl.c2
-rw-r--r--src/lib/trace/events.h2
88 files changed, 2099 insertions, 303 deletions
diff --git a/src/lib/arch/bytes.h b/src/lib/arch/bytes.h
index b8b6288139..4756ca2beb 100644
--- a/src/lib/arch/bytes.h
+++ b/src/lib/arch/bytes.h
@@ -129,7 +129,7 @@ tor_ntohll(uint64_t a)
{
return a;
}
-#else /* !(defined(WORDS_BIGENDIAN)) */
+#else /* !defined(WORDS_BIGENDIAN) */
static inline uint16_t
tor_htons(uint16_t a)
{
diff --git a/src/lib/buf/buffers.c b/src/lib/buf/buffers.c
index 3828583855..28710ebab3 100644
--- a/src/lib/buf/buffers.c
+++ b/src/lib/buf/buffers.c
@@ -580,6 +580,7 @@ buf_add_vprintf(buf_t *buf, const char *format, va_list args)
/* XXXX Faster implementations are easy enough, but let's optimize later */
char *tmp;
tor_vasprintf(&tmp, format, args);
+ tor_assert(tmp != NULL);
buf_add(buf, tmp, strlen(tmp));
tor_free(tmp);
}
diff --git a/src/lib/cc/compat_compiler.h b/src/lib/cc/compat_compiler.h
index 92301449e8..3ef866ecce 100644
--- a/src/lib/cc/compat_compiler.h
+++ b/src/lib/cc/compat_compiler.h
@@ -82,7 +82,7 @@
# define ENABLE_GCC_WARNING(warningopt) \
PRAGMA_DIAGNOSTIC_(warning PRAGMA_JOIN_STRINGIFY_(-W,warningopt))
#endif /* defined(__clang__) || GCC_VERSION >= 406 */
-#else /* !(defined(__GNUC__)) */
+#else /* !defined(__GNUC__) */
/* not gcc at all */
# define DISABLE_GCC_WARNING(warning)
# define ENABLE_GCC_WARNING(warning)
diff --git a/src/lib/cc/torint.h b/src/lib/cc/torint.h
index 523f378ed7..94b79d30a1 100644
--- a/src/lib/cc/torint.h
+++ b/src/lib/cc/torint.h
@@ -96,7 +96,7 @@ typedef int32_t ssize_t;
# else
# define TOR_PRIuSZ PRIu32
# endif
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
# define TOR_PRIuSZ "zu"
#endif /* defined(_WIN32) */
@@ -106,7 +106,7 @@ typedef int32_t ssize_t;
# else
# define TOR_PRIdSZ PRId32
# endif
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
# define TOR_PRIdSZ "zd"
#endif /* defined(_WIN32) */
diff --git a/src/lib/compress/compress_lzma.c b/src/lib/compress/compress_lzma.c
index 2dab37e433..915f4949ae 100644
--- a/src/lib/compress/compress_lzma.c
+++ b/src/lib/compress/compress_lzma.c
@@ -221,7 +221,7 @@ tor_lzma_compress_new(int compress,
tor_free(result);
return NULL;
/* LCOV_EXCL_STOP */
-#else /* !(defined(HAVE_LZMA)) */
+#else /* !defined(HAVE_LZMA) */
(void)compress;
(void)method;
(void)level;
@@ -312,7 +312,7 @@ tor_lzma_compress_process(tor_lzma_compress_state_t *state,
lzma_error_str(retval));
return TOR_COMPRESS_ERROR;
}
-#else /* !(defined(HAVE_LZMA)) */
+#else /* !defined(HAVE_LZMA) */
(void)state;
(void)out;
(void)out_len;
diff --git a/src/lib/compress/compress_zstd.c b/src/lib/compress/compress_zstd.c
index a99ea67e0b..9076665295 100644
--- a/src/lib/compress/compress_zstd.c
+++ b/src/lib/compress/compress_zstd.c
@@ -93,7 +93,7 @@ tor_zstd_get_version_str(void)
ZSTD_versionNumber());
return version_str;
-#else /* !(defined(HAVE_ZSTD)) */
+#else /* !defined(HAVE_ZSTD) */
return NULL;
#endif /* defined(HAVE_ZSTD) */
}
@@ -317,7 +317,7 @@ tor_zstd_compress_new(int compress,
tor_free(result);
return NULL;
// LCOV_EXCL_STOP
-#else /* !(defined(HAVE_ZSTD)) */
+#else /* !defined(HAVE_ZSTD) */
(void)compress;
(void)method;
(void)level;
@@ -454,7 +454,7 @@ tor_zstd_compress_process(tor_zstd_compress_state_t *state,
return TOR_COMPRESS_OK;
}
-#else /* !(defined(HAVE_ZSTD)) */
+#else /* !defined(HAVE_ZSTD) */
(void)state;
(void)out;
(void)out_len;
diff --git a/src/lib/confmgt/.may_include b/src/lib/confmgt/.may_include
index d85dbf6904..2564133917 100644
--- a/src/lib/confmgt/.may_include
+++ b/src/lib/confmgt/.may_include
@@ -7,3 +7,5 @@ lib/encoding/*.h
lib/log/*.h
lib/malloc/*.h
lib/string/*.h
+lib/testsupport/*.h
+ext/*.h
diff --git a/src/lib/confmgt/confparse.c b/src/lib/confmgt/confparse.c
new file mode 100644
index 0000000000..08e562f654
--- /dev/null
+++ b/src/lib/confmgt/confparse.c
@@ -0,0 +1,1239 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file confparse.c
+ *
+ * \brief Back-end for parsing and generating key-value files, used to
+ * implement the torrc file format and the state file.
+ *
+ * This module is used by config.c to parse and encode torrc
+ * configuration files, and by statefile.c to parse and encode the
+ * $DATADIR/state file.
+ *
+ * To use this module, its callers provide an instance of
+ * config_format_t to describe the mappings from a set of configuration
+ * options to a number of fields in a C structure. With this mapping,
+ * the functions here can convert back and forth between the C structure
+ * specified, and a linked list of key-value pairs.
+ */
+
+#define CONFPARSE_PRIVATE
+#include "orconfig.h"
+#include "lib/confmgt/confparse.h"
+
+#include "lib/confmgt/structvar.h"
+#include "lib/confmgt/unitparse.h"
+#include "lib/container/bitarray.h"
+#include "lib/container/smartlist.h"
+#include "lib/encoding/confline.h"
+#include "lib/log/escape.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+#include "lib/string/compat_ctype.h"
+#include "lib/string/printf.h"
+#include "lib/string/util_string.h"
+
+#include "ext/siphash.h"
+
+/**
+ * A managed_var_t is an internal wrapper around a config_var_t in
+ * a config_format_t structure. It is used by config_mgr_t to
+ * keep track of which option goes with which structure. */
+typedef struct managed_var_t {
+ /**
+ * A pointer to the config_var_t for this option.
+ */
+ const config_var_t *cvar;
+ /**
+ * The index of the object in which this option is stored. It is
+ * IDX_TOPLEVEL to indicate that the object is the top-level object.
+ **/
+ int object_idx;
+} managed_var_t;
+
+static void config_reset(const config_mgr_t *fmt, void *options,
+ const managed_var_t *var, int use_defaults);
+static void config_mgr_register_fmt(config_mgr_t *mgr,
+ const config_format_t *fmt,
+ int object_idx);
+
+/** Release all storage held in a managed_var_t. */
+static void
+managed_var_free_(managed_var_t *mv)
+{
+ if (!mv)
+ return;
+ tor_free(mv);
+}
+#define managed_var_free(mv) \
+ FREE_AND_NULL(managed_var_t, managed_var_free_, (mv))
+
+struct config_suite_t {
+ /** A list of configuration objects managed by a given configuration
+ * manager. They are stored in the same order as the config_format_t
+ * objects in the manager's list of subformats. */
+ smartlist_t *configs;
+};
+
+/**
+ * Allocate a new empty config_suite_t.
+ **/
+static config_suite_t *
+config_suite_new(void)
+{
+ config_suite_t *suite = tor_malloc_zero(sizeof(config_suite_t));
+ suite->configs = smartlist_new();
+ return suite;
+}
+
+/** Release all storage held by a config_suite_t. (Does not free
+ * any configuration objects it holds; the caller must do that first.) */
+static void
+config_suite_free_(config_suite_t *suite)
+{
+ if (!suite)
+ return;
+ smartlist_free(suite->configs);
+ tor_free(suite);
+}
+
+#define config_suite_free(suite) \
+ FREE_AND_NULL(config_suite_t, config_suite_free_, (suite))
+
+struct config_mgr_t {
+ /** The 'top-level' configuration format. This one is used for legacy
+ * options that have not yet been assigned to different sub-modules.
+ *
+ * (NOTE: for now, this is the only config_format_t that a config_mgr_t
+ * contains. A subsequent commit will add more. XXXX)
+ */
+ const config_format_t *toplevel;
+ /**
+ * List of second-level configuration format objects that this manager
+ * also knows about.
+ */
+ smartlist_t *subconfigs;
+ /** A smartlist of managed_var_t objects for all configuration formats. */
+ smartlist_t *all_vars;
+ /** A smartlist of config_abbrev_t objects for all configuration
+ * formats. These objects are used to track synonyms and abbreviations for
+ * different configuration options. */
+ smartlist_t *all_abbrevs;
+ /** A smartlist of config_deprecation_t for all configuration formats. */
+ smartlist_t *all_deprecations;
+ /** True if this manager has been frozen and cannot have any more formats
+ * added to it. A manager must be frozen before it can be used to construct
+ * or manipulate objects. */
+ bool frozen;
+ /** A replacement for the magic number of the toplevel object. We override
+ * that number to make it unique for this particular config_mgr_t, so that
+ * an object constructed with one mgr can't be used with another, even if
+ * those managers' contents are equal.
+ */
+ struct_magic_decl_t toplevel_magic;
+};
+
+#define IDX_TOPLEVEL (-1)
+
+/** Create a new config_mgr_t to manage a set of configuration objects to be
+ * wrapped under <b>toplevel_fmt</b>. */
+config_mgr_t *
+config_mgr_new(const config_format_t *toplevel_fmt)
+{
+ config_mgr_t *mgr = tor_malloc_zero(sizeof(config_mgr_t));
+ mgr->subconfigs = smartlist_new();
+ mgr->all_vars = smartlist_new();
+ mgr->all_abbrevs = smartlist_new();
+ mgr->all_deprecations = smartlist_new();
+
+ config_mgr_register_fmt(mgr, toplevel_fmt, IDX_TOPLEVEL);
+ mgr->toplevel = toplevel_fmt;
+
+ return mgr;
+}
+
+/** Add a config_format_t to a manager, with a specified (unique) index. */
+static void
+config_mgr_register_fmt(config_mgr_t *mgr,
+ const config_format_t *fmt,
+ int object_idx)
+{
+ int i;
+
+ tor_assertf(!mgr->frozen,
+ "Tried to add a format to a configuration manager after "
+ "it had been frozen.");
+
+ if (object_idx != IDX_TOPLEVEL) {
+ tor_assertf(fmt->config_suite_offset < 0,
+ "Tried to register a toplevel format in a non-toplevel position");
+ }
+ tor_assertf(fmt != mgr->toplevel &&
+ ! smartlist_contains(mgr->subconfigs, fmt),
+ "Tried to register an already-registered format.");
+
+ /* register variables */
+ for (i = 0; fmt->vars[i].member.name; ++i) {
+ managed_var_t *mv = tor_malloc_zero(sizeof(managed_var_t));
+ mv->cvar = &fmt->vars[i];
+ mv->object_idx = object_idx;
+ smartlist_add(mgr->all_vars, mv);
+ }
+
+ /* register abbrevs */
+ if (fmt->abbrevs) {
+ for (i = 0; fmt->abbrevs[i].abbreviated; ++i) {
+ smartlist_add(mgr->all_abbrevs, (void*)&fmt->abbrevs[i]);
+ }
+ }
+
+ /* register deprecations. */
+ if (fmt->deprecations) {
+ const config_deprecation_t *d;
+ for (d = fmt->deprecations; d->name; ++d) {
+ smartlist_add(mgr->all_deprecations, (void*)d);
+ }
+ }
+}
+
+/**
+ * Add a new format to this configuration object. Asserts on failure.
+ *
+ * Returns an internal "index" value used to identify this format within
+ * all of those formats contained in <b>mgr</b>. This index value
+ * should not generally be used outside of this module.
+ **/
+int
+config_mgr_add_format(config_mgr_t *mgr,
+ const config_format_t *fmt)
+{
+ tor_assert(mgr);
+ int idx = smartlist_len(mgr->subconfigs);
+ config_mgr_register_fmt(mgr, fmt, idx);
+ smartlist_add(mgr->subconfigs, (void *)fmt);
+ return idx;
+}
+
+/** Return a pointer to the config_suite_t * pointer inside a
+ * configuration object; returns NULL if there is no such member. */
+static inline config_suite_t **
+config_mgr_get_suite_ptr(const config_mgr_t *mgr, void *toplevel)
+{
+ if (mgr->toplevel->config_suite_offset < 0)
+ return NULL;
+ return STRUCT_VAR_P(toplevel, mgr->toplevel->config_suite_offset);
+}
+
+/**
+ * Return a pointer to the configuration object within <b>toplevel</b> whose
+ * index is <b>idx</b>.
+ *
+ * NOTE: XXXX Eventually, there will be multiple objects supported within the
+ * toplevel object. For example, the or_options_t will contain pointers
+ * to configuration objects for other modules. This function gets
+ * the sub-object for a particular module.
+ */
+STATIC void *
+config_mgr_get_obj_mutable(const config_mgr_t *mgr, void *toplevel, int idx)
+{
+ tor_assert(mgr);
+ tor_assert(toplevel);
+ if (idx == IDX_TOPLEVEL)
+ return toplevel;
+
+ tor_assertf(idx >= 0 && idx < smartlist_len(mgr->subconfigs),
+ "Index %d is out of range.", idx);
+ config_suite_t **suite = config_mgr_get_suite_ptr(mgr, toplevel);
+ tor_assert(suite);
+ tor_assert(smartlist_len(mgr->subconfigs) ==
+ smartlist_len((*suite)->configs));
+
+ return smartlist_get((*suite)->configs, idx);
+}
+
+/** As config_mgr_get_obj_mutable(), but return a const pointer. */
+STATIC const void *
+config_mgr_get_obj(const config_mgr_t *mgr, const void *toplevel, int idx)
+{
+ return config_mgr_get_obj_mutable(mgr, (void*)toplevel, idx);
+}
+
+/** Sorting helper for smartlist of managed_var_t */
+static int
+managed_var_cmp(const void **a, const void **b)
+{
+ const managed_var_t *mv1 = *(const managed_var_t**)a;
+ const managed_var_t *mv2 = *(const managed_var_t**)b;
+
+ return strcasecmp(mv1->cvar->member.name, mv2->cvar->member.name);
+}
+
+/**
+ * Mark a configuration manager as "frozen", so that no more formats can be
+ * added, and so that it can be used for manipulating configuration objects.
+ **/
+void
+config_mgr_freeze(config_mgr_t *mgr)
+{
+ static uint64_t mgr_count = 0;
+
+ smartlist_sort(mgr->all_vars, managed_var_cmp);
+ memcpy(&mgr->toplevel_magic, &mgr->toplevel->magic,
+ sizeof(struct_magic_decl_t));
+ uint64_t magic_input[3] = { mgr->toplevel_magic.magic_val,
+ (uint64_t) (uintptr_t) mgr,
+ ++mgr_count };
+ mgr->toplevel_magic.magic_val =
+ (uint32_t)siphash24g(magic_input, sizeof(magic_input));
+ mgr->frozen = true;
+}
+
+/** Release all storage held in <b>mgr</b> */
+void
+config_mgr_free_(config_mgr_t *mgr)
+{
+ if (!mgr)
+ return;
+ SMARTLIST_FOREACH(mgr->all_vars, managed_var_t *, mv, managed_var_free(mv));
+ smartlist_free(mgr->all_vars);
+ smartlist_free(mgr->all_abbrevs);
+ smartlist_free(mgr->all_deprecations);
+ smartlist_free(mgr->subconfigs);
+ memset(mgr, 0, sizeof(*mgr));
+ tor_free(mgr);
+}
+
+/** Return a new smartlist_t containing a config_var_t for every variable that
+ * <b>mgr</b> knows about. The elements of this smartlist do not need
+ * to be freed; they have the same lifespan as <b>mgr</b>. */
+smartlist_t *
+config_mgr_list_vars(const config_mgr_t *mgr)
+{
+ smartlist_t *result = smartlist_new();
+ tor_assert(mgr);
+ SMARTLIST_FOREACH(mgr->all_vars, managed_var_t *, mv,
+ smartlist_add(result, (void*) mv->cvar));
+ return result;
+}
+
+/** Return a new smartlist_t containing the names of all deprecated variables.
+ * The elements of this smartlist do not need to be freed; they have the same
+ * lifespan as <b>mgr</b>.
+ */
+smartlist_t *
+config_mgr_list_deprecated_vars(const config_mgr_t *mgr)
+{
+ smartlist_t *result = smartlist_new();
+ tor_assert(mgr);
+ SMARTLIST_FOREACH(mgr->all_deprecations, config_deprecation_t *, d,
+ smartlist_add(result, (char*)d->name));
+ return result;
+}
+
+/** Assert that the magic fields in <b>options</b> and its subsidiary
+ * objects are all okay. */
+static void
+config_mgr_assert_magic_ok(const config_mgr_t *mgr,
+ const void *options)
+{
+ tor_assert(mgr);
+ tor_assert(options);
+ tor_assert(mgr->frozen);
+ struct_check_magic(options, &mgr->toplevel_magic);
+
+ config_suite_t **suitep = config_mgr_get_suite_ptr(mgr, (void*)options);
+ if (suitep == NULL) {
+ tor_assert(smartlist_len(mgr->subconfigs) == 0);
+ return;
+ }
+
+ tor_assert(smartlist_len((*suitep)->configs) ==
+ smartlist_len(mgr->subconfigs));
+ SMARTLIST_FOREACH_BEGIN(mgr->subconfigs, const config_format_t *, fmt) {
+ void *obj = smartlist_get((*suitep)->configs, fmt_sl_idx);
+ tor_assert(obj);
+ struct_check_magic(obj, &fmt->magic);
+ } SMARTLIST_FOREACH_END(fmt);
+}
+
+/** Macro: assert that <b>cfg</b> has the right magic field for
+ * <b>mgr</b>. */
+#define CONFIG_CHECK(mgr, cfg) STMT_BEGIN \
+ config_mgr_assert_magic_ok((mgr), (cfg)); \
+ STMT_END
+
+/** Allocate an empty configuration object of a given format type. */
+void *
+config_new(const config_mgr_t *mgr)
+{
+ tor_assert(mgr->frozen);
+ void *opts = tor_malloc_zero(mgr->toplevel->size);
+ struct_set_magic(opts, &mgr->toplevel_magic);
+ config_suite_t **suitep = config_mgr_get_suite_ptr(mgr, opts);
+ if (suitep) {
+ *suitep = config_suite_new();
+ SMARTLIST_FOREACH_BEGIN(mgr->subconfigs, const config_format_t *, fmt) {
+ void *obj = tor_malloc_zero(fmt->size);
+ struct_set_magic(obj, &fmt->magic);
+ smartlist_add((*suitep)->configs, obj);
+ } SMARTLIST_FOREACH_END(fmt);
+ }
+ CONFIG_CHECK(mgr, opts);
+ return opts;
+}
+
+/*
+ * Functions to parse config options
+ */
+
+/** If <b>option</b> is an official abbreviation for a longer option,
+ * return the longer option. Otherwise return <b>option</b>.
+ * If <b>command_line</b> is set, apply all abbreviations. Otherwise, only
+ * apply abbreviations that work for the config file and the command line.
+ * If <b>warn_obsolete</b> is set, warn about deprecated names. */
+const char *
+config_expand_abbrev(const config_mgr_t *mgr, const char *option,
+ int command_line, int warn_obsolete)
+{
+ SMARTLIST_FOREACH_BEGIN(mgr->all_abbrevs, const config_abbrev_t *, abbrev) {
+ /* Abbreviations are case insensitive. */
+ if (!strcasecmp(option, abbrev->abbreviated) &&
+ (command_line || !abbrev->commandline_only)) {
+ if (warn_obsolete && abbrev->warn) {
+ log_warn(LD_CONFIG,
+ "The configuration option '%s' is deprecated; "
+ "use '%s' instead.",
+ abbrev->abbreviated,
+ abbrev->full);
+ }
+ /* Keep going through the list in case we want to rewrite it more.
+ * (We could imagine recursing here, but I don't want to get the
+ * user into an infinite loop if we craft our list wrong.) */
+ option = abbrev->full;
+ }
+ } SMARTLIST_FOREACH_END(abbrev);
+ return option;
+}
+
+/** If <b>key</b> is a deprecated configuration option, return the message
+ * explaining why it is deprecated (which may be an empty string). Return NULL
+ * if it is not deprecated. The <b>key</b> field must be fully expanded. */
+const char *
+config_find_deprecation(const config_mgr_t *mgr, const char *key)
+{
+ if (BUG(mgr == NULL) || BUG(key == NULL))
+ return NULL; // LCOV_EXCL_LINE
+
+ SMARTLIST_FOREACH_BEGIN(mgr->all_deprecations, const config_deprecation_t *,
+ d) {
+ if (!strcasecmp(d->name, key)) {
+ return d->why_deprecated ? d->why_deprecated : "";
+ }
+ } SMARTLIST_FOREACH_END(d);
+ return NULL;
+}
+
+/**
+ * Find the managed_var_t object for a variable whose name is <b>name</b>
+ * according to <b>mgr</b>. Return that object, or NULL if none exists.
+ *
+ * If <b>allow_truncated</b> is true, then accept any variable whose
+ * name begins with <b>name</b>.
+ *
+ * If <b>idx_out</b> is not NULL, set *<b>idx_out</b> to the position of
+ * that variable within mgr-&gt;all_vars, or to -1 if the variable is
+ * not found.
+ */
+static const managed_var_t *
+config_mgr_find_var(const config_mgr_t *mgr,
+ const char *key,
+ bool allow_truncated, int *idx_out)
+{
+ const size_t keylen = strlen(key);
+ if (idx_out)
+ *idx_out = -1;
+
+ if (!keylen)
+ return NULL; /* if they say "--" on the command line, it's not an option */
+
+ /* First, check for an exact (case-insensitive) match */
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ if (!strcasecmp(mv->cvar->member.name, key)) {
+ if (idx_out)
+ *idx_out = mv_sl_idx;
+ return mv;
+ }
+ } SMARTLIST_FOREACH_END(mv);
+
+ if (!allow_truncated)
+ return NULL;
+
+ /* If none, check for an abbreviated match */
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ if (!strncasecmp(key, mv->cvar->member.name, keylen)) {
+ log_warn(LD_CONFIG, "The abbreviation '%s' is deprecated. "
+ "Please use '%s' instead",
+ key, mv->cvar->member.name);
+ if (idx_out)
+ *idx_out = mv_sl_idx;
+ return mv;
+ }
+ } SMARTLIST_FOREACH_END(mv);
+
+ /* Okay, unrecognized option */
+ return NULL;
+}
+
+/**
+ * If <b>key</b> is a name or an abbreviation configuration option, return
+ * the corresponding canonical name for it. Warn if the abbreviation is
+ * non-standard. Return NULL if the option does not exist.
+ */
+const char *
+config_find_option_name(const config_mgr_t *mgr, const char *key)
+{
+ key = config_expand_abbrev(mgr, key, 0, 0);
+ const managed_var_t *mv = config_mgr_find_var(mgr, key, true, NULL);
+ if (mv)
+ return mv->cvar->member.name;
+ else
+ return NULL;
+}
+
+/** Return the number of option entries in <b>fmt</b>. */
+static int
+config_count_options(const config_mgr_t *mgr)
+{
+ return smartlist_len(mgr->all_vars);
+}
+
+/**
+ * Return true iff at least one bit from <b>flag</b> is set on <b>var</b>,
+ * either in <b>var</b>'s flags, or on the flags of its type.
+ **/
+static bool
+config_var_has_flag(const config_var_t *var, uint32_t flag)
+{
+ uint32_t have_flags = var->flags | struct_var_get_flags(&var->member);
+
+ return (have_flags & flag) != 0;
+}
+
+/**
+ * Return true if assigning a value to <b>var</b> replaces the previous
+ * value. Return false if assigning a value to <b>var</b> appends
+ * to the previous value.
+ **/
+static bool
+config_var_is_replaced_on_set(const config_var_t *var)
+{
+ return ! config_var_has_flag(var, CFLG_NOREPLACE);
+}
+
+/**
+ * Return true iff <b>var</b> may be assigned by name (e.g., via the
+ * CLI, the configuration files, or the controller API).
+ **/
+bool
+config_var_is_settable(const config_var_t *var)
+{
+ return ! config_var_has_flag(var, CFLG_NOSET);
+}
+
+/**
+ * Return true iff the controller is allowed to fetch the value of
+ * <b>var</b>.
+ **/
+static bool
+config_var_is_gettable(const config_var_t *var)
+{
+ /* Arguably, invisible or obsolete options should not be gettable. However,
+ * they have been gettable for a long time, and making them ungettable could
+ * have compatibility effects. For now, let's leave them alone.
+ */
+
+ // return ! config_var_has_flag(var, CVFLAG_OBSOLETE|CFGLAGS_INVISIBLE);
+ (void)var;
+ return true;
+}
+
+/**
+ * Return true iff we need to check <b>var</b> for changes when we are
+ * comparing config options for changes.
+ *
+ * A false result might mean that the variable is a derived variable, and that
+ * comparing the variable it derives from compares this one too-- or it might
+ * mean that there is no data to compare.
+ **/
+static bool
+config_var_should_list_changes(const config_var_t *var)
+{
+ return ! config_var_has_flag(var, CFLG_NOCMP);
+}
+
+/**
+ * Return true iff we need to copy the data for <b>var</b> when we are
+ * copying a config option.
+ *
+ * A false option might mean that the variable is a derived variable, and that
+ * copying the variable it derives from copies it-- or it might mean that
+ * there is no data to copy.
+ **/
+static bool
+config_var_needs_copy(const config_var_t *var)
+{
+ return ! config_var_has_flag(var, CFLG_NOCOPY);
+}
+
+/**
+ * Return true iff variable <b>var</b> should appear on list of variable
+ * names given to the controller or the CLI.
+ *
+ * (Note that this option is imperfectly obeyed. The
+ * --list-torrc-options command looks at the "settable" flag, whereas
+ * "GETINFO config/defaults" and "list_deprecated_*()" do not filter
+ * their results. It would be good for consistency to try to converge
+ * these behaviors in the future.)
+ **/
+bool
+config_var_is_listable(const config_var_t *var)
+{
+ return ! config_var_has_flag(var, CFLG_NOLIST);
+}
+
+/**
+ * Return true iff variable <b>var</b> should be written out when we
+ * are writing our configuration to disk, to a controller, or via the
+ * --dump-config command.
+ *
+ * This option may be set because a variable is hidden, or because it is
+ * derived from another variable which will already be written out.
+ **/
+static bool
+config_var_is_dumpable(const config_var_t *var)
+{
+ return ! config_var_has_flag(var, CFLG_NODUMP);
+}
+
+/*
+ * Functions to assign config options.
+ */
+
+/** <b>c</b>-\>key is known to be a real key. Update <b>options</b>
+ * with <b>c</b>-\>value and return 0, or return -1 if bad value.
+ *
+ * Called from config_assign_line() and option_reset().
+ */
+static int
+config_assign_value(const config_mgr_t *mgr, void *options,
+ config_line_t *c, char **msg)
+{
+ const managed_var_t *var;
+
+ CONFIG_CHECK(mgr, options);
+
+ var = config_mgr_find_var(mgr, c->key, true, NULL);
+ tor_assert(var);
+ tor_assert(!strcmp(c->key, var->cvar->member.name));
+ void *object = config_mgr_get_obj_mutable(mgr, options, var->object_idx);
+
+ return struct_var_kvassign(object, c, msg, &var->cvar->member);
+}
+
+/** Mark every linelist in <b>options</b> "fragile", so that fresh assignments
+ * to it will replace old ones. */
+static void
+config_mark_lists_fragile(const config_mgr_t *mgr, void *options)
+{
+ tor_assert(mgr);
+ tor_assert(options);
+
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ void *object = config_mgr_get_obj_mutable(mgr, options, mv->object_idx);
+ struct_var_mark_fragile(object, &mv->cvar->member);
+ } SMARTLIST_FOREACH_END(mv);
+}
+
+/**
+ * Log a warning that declaring that the option called <b>what</b>
+ * is deprecated because of the reason in <b>why</b>.
+ *
+ * (Both arguments must be non-NULL.)
+ **/
+void
+warn_deprecated_option(const char *what, const char *why)
+{
+ const char *space = (why && strlen(why)) ? " " : "";
+ log_warn(LD_CONFIG, "The %s option is deprecated, and will most likely "
+ "be removed in a future version of Tor.%s%s (If you think this is "
+ "a mistake, please let us know!)",
+ what, space, why);
+}
+
+/** If <b>c</b> is a syntactically valid configuration line, update
+ * <b>options</b> with its value and return 0. Otherwise return -1 for bad
+ * key, -2 for bad value.
+ *
+ * If <b>clear_first</b> is set, clear the value first. Then if
+ * <b>use_defaults</b> is set, set the value to the default.
+ *
+ * Called from config_assign().
+ */
+static int
+config_assign_line(const config_mgr_t *mgr, void *options,
+ config_line_t *c, unsigned flags,
+ bitarray_t *options_seen, char **msg)
+{
+ const unsigned use_defaults = flags & CAL_USE_DEFAULTS;
+ const unsigned clear_first = flags & CAL_CLEAR_FIRST;
+ const unsigned warn_deprecations = flags & CAL_WARN_DEPRECATIONS;
+ const managed_var_t *mvar;
+
+ CONFIG_CHECK(mgr, options);
+
+ int var_index = -1;
+ mvar = config_mgr_find_var(mgr, c->key, true, &var_index);
+ if (!mvar) {
+ const config_format_t *fmt = mgr->toplevel;
+ if (fmt->extra) {
+ void *lvalue = STRUCT_VAR_P(options, fmt->extra->offset);
+ log_info(LD_CONFIG,
+ "Found unrecognized option '%s'; saving it.", c->key);
+ config_line_append((config_line_t**)lvalue, c->key, c->value);
+ return 0;
+ } else {
+ tor_asprintf(msg,
+ "Unknown option '%s'. Failing.", c->key);
+ return -1;
+ }
+ }
+
+ const config_var_t *cvar = mvar->cvar;
+ tor_assert(cvar);
+
+ /* Put keyword into canonical case. */
+ if (strcmp(cvar->member.name, c->key)) {
+ tor_free(c->key);
+ c->key = tor_strdup(cvar->member.name);
+ }
+
+ const char *deprecation_msg;
+ if (warn_deprecations &&
+ (deprecation_msg = config_find_deprecation(mgr, cvar->member.name))) {
+ warn_deprecated_option(cvar->member.name, deprecation_msg);
+ }
+
+ if (!strlen(c->value)) {
+ /* reset or clear it, then return */
+ if (!clear_first) {
+ if (! config_var_is_replaced_on_set(cvar) &&
+ c->command != CONFIG_LINE_CLEAR) {
+ /* We got an empty linelist from the torrc or command line.
+ As a special case, call this an error. Warn and ignore. */
+ log_warn(LD_CONFIG,
+ "Linelist option '%s' has no value. Skipping.", c->key);
+ } else { /* not already cleared */
+ config_reset(mgr, options, mvar, use_defaults);
+ }
+ }
+ return 0;
+ } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) {
+ // This block is unreachable, since a CLEAR line always has an
+ // empty value, and so will trigger be handled by the previous
+ // "if (!strlen(c->value))" block.
+
+ // LCOV_EXCL_START
+ tor_assert_nonfatal_unreached();
+ config_reset(mgr, options, mvar, use_defaults);
+ // LCOV_EXCL_STOP
+ }
+
+ if (options_seen && config_var_is_replaced_on_set(cvar)) {
+ /* We're tracking which options we've seen, and this option is not
+ * supposed to occur more than once. */
+ tor_assert(var_index >= 0);
+ if (bitarray_is_set(options_seen, var_index)) {
+ log_warn(LD_CONFIG, "Option '%s' used more than once; all but the last "
+ "value will be ignored.", cvar->member.name);
+ }
+ bitarray_set(options_seen, var_index);
+ }
+
+ if (config_assign_value(mgr, options, c, msg) < 0)
+ return -2;
+ return 0;
+}
+
+/** Restore the option named <b>key</b> in options to its default value.
+ * Called from config_assign(). */
+STATIC void
+config_reset_line(const config_mgr_t *mgr, void *options,
+ const char *key, int use_defaults)
+{
+ const managed_var_t *var;
+
+ CONFIG_CHECK(mgr, options);
+
+ var = config_mgr_find_var(mgr, key, true, NULL);
+ if (!var)
+ return; /* give error on next pass. */
+
+ config_reset(mgr, options, var, use_defaults);
+}
+
+/** Return true iff value needs to be quoted and escaped to be used in
+ * a configuration file. */
+static int
+config_value_needs_escape(const char *value)
+{
+ if (*value == '\"')
+ return 1;
+ while (*value) {
+ switch (*value)
+ {
+ case '\r':
+ case '\n':
+ case '#':
+ /* Note: quotes and backspaces need special handling when we are using
+ * quotes, not otherwise, so they don't trigger escaping on their
+ * own. */
+ return 1;
+ default:
+ if (!TOR_ISPRINT(*value))
+ return 1;
+ }
+ ++value;
+ }
+ return 0;
+}
+
+/** Return newly allocated line or lines corresponding to <b>key</b> in the
+ * configuration <b>options</b>. If <b>escape_val</b> is true and a
+ * value needs to be quoted before it's put in a config file, quote and
+ * escape that value. Return NULL if no such key exists. */
+config_line_t *
+config_get_assigned_option(const config_mgr_t *mgr, const void *options,
+ const char *key, int escape_val)
+{
+ const managed_var_t *var;
+ config_line_t *result;
+
+ tor_assert(options && key);
+
+ CONFIG_CHECK(mgr, options);
+
+ var = config_mgr_find_var(mgr, key, true, NULL);
+ if (!var) {
+ log_warn(LD_CONFIG, "Unknown option '%s'. Failing.", key);
+ return NULL;
+ }
+ if (! config_var_is_gettable(var->cvar)) {
+ log_warn(LD_CONFIG, "Option '%s' is obsolete or unfetchable. Failing.",
+ key);
+ return NULL;
+ }
+ const void *object = config_mgr_get_obj(mgr, options, var->object_idx);
+
+ result = struct_var_kvencode(object, &var->cvar->member);
+
+ if (escape_val) {
+ config_line_t *line;
+ for (line = result; line; line = line->next) {
+ if (line->value && config_value_needs_escape(line->value)) {
+ char *newval = esc_for_log(line->value);
+ tor_free(line->value);
+ line->value = newval;
+ }
+ }
+ }
+
+ return result;
+}
+/** Iterate through the linked list of requested options <b>list</b>.
+ * For each item, convert as appropriate and assign to <b>options</b>.
+ * If an item is unrecognized, set *msg and return -1 immediately,
+ * else return 0 for success.
+ *
+ * If <b>clear_first</b>, interpret config options as replacing (not
+ * extending) their previous values. If <b>clear_first</b> is set,
+ * then <b>use_defaults</b> to decide if you set to defaults after
+ * clearing, or make the value 0 or NULL.
+ *
+ * Here are the use cases:
+ * 1. A non-empty AllowInvalid line in your torrc. Appends to current
+ * if linelist, replaces current if csv.
+ * 2. An empty AllowInvalid line in your torrc. Should clear it.
+ * 3. "RESETCONF AllowInvalid" sets it to default.
+ * 4. "SETCONF AllowInvalid" makes it NULL.
+ * 5. "SETCONF AllowInvalid=foo" clears it and sets it to "foo".
+ *
+ * Use_defaults Clear_first
+ * 0 0 "append"
+ * 1 0 undefined, don't use
+ * 0 1 "set to null first"
+ * 1 1 "set to defaults first"
+ * Return 0 on success, -1 on bad key, -2 on bad value.
+ *
+ * As an additional special case, if a LINELIST config option has
+ * no value and clear_first is 0, then warn and ignore it.
+ */
+
+/*
+There are three call cases for config_assign() currently.
+
+Case one: Torrc entry
+options_init_from_torrc() calls config_assign(0, 0)
+ calls config_assign_line(0, 0).
+ if value is empty, calls config_reset(0) and returns.
+ calls config_assign_value(), appends.
+
+Case two: setconf
+options_trial_assign() calls config_assign(0, 1)
+ calls config_reset_line(0)
+ calls config_reset(0)
+ calls option_clear().
+ calls config_assign_line(0, 1).
+ if value is empty, returns.
+ calls config_assign_value(), appends.
+
+Case three: resetconf
+options_trial_assign() calls config_assign(1, 1)
+ calls config_reset_line(1)
+ calls config_reset(1)
+ calls option_clear().
+ calls config_assign_value(default)
+ calls config_assign_line(1, 1).
+ returns.
+*/
+int
+config_assign(const config_mgr_t *mgr, void *options, config_line_t *list,
+ unsigned config_assign_flags, char **msg)
+{
+ config_line_t *p;
+ bitarray_t *options_seen;
+ const int n_options = config_count_options(mgr);
+ const unsigned clear_first = config_assign_flags & CAL_CLEAR_FIRST;
+ const unsigned use_defaults = config_assign_flags & CAL_USE_DEFAULTS;
+
+ CONFIG_CHECK(mgr, options);
+
+ /* pass 1: normalize keys */
+ for (p = list; p; p = p->next) {
+ const char *full = config_expand_abbrev(mgr, p->key, 0, 1);
+ if (strcmp(full,p->key)) {
+ tor_free(p->key);
+ p->key = tor_strdup(full);
+ }
+ }
+
+ /* pass 2: if we're reading from a resetting source, clear all
+ * mentioned config options, and maybe set to their defaults. */
+ if (clear_first) {
+ for (p = list; p; p = p->next)
+ config_reset_line(mgr, options, p->key, use_defaults);
+ }
+
+ options_seen = bitarray_init_zero(n_options);
+ /* pass 3: assign. */
+ while (list) {
+ int r;
+ if ((r=config_assign_line(mgr, options, list, config_assign_flags,
+ options_seen, msg))) {
+ bitarray_free(options_seen);
+ return r;
+ }
+ list = list->next;
+ }
+ bitarray_free(options_seen);
+
+ /** Now we're done assigning a group of options to the configuration.
+ * Subsequent group assignments should _replace_ linelists, not extend
+ * them. */
+ config_mark_lists_fragile(mgr, options);
+
+ return 0;
+}
+
+/** Reset config option <b>var</b> to 0, 0.0, NULL, or the equivalent.
+ * Called from config_reset() and config_free(). */
+static void
+config_clear(const config_mgr_t *mgr, void *options, const managed_var_t *var)
+{
+ void *object = config_mgr_get_obj_mutable(mgr, options, var->object_idx);
+ struct_var_free(object, &var->cvar->member);
+}
+
+/** Clear the option indexed by <b>var</b> in <b>options</b>. Then if
+ * <b>use_defaults</b>, set it to its default value.
+ * Called by config_init() and option_reset_line() and option_assign_line(). */
+static void
+config_reset(const config_mgr_t *mgr, void *options,
+ const managed_var_t *var, int use_defaults)
+{
+ config_line_t *c;
+ char *msg = NULL;
+ CONFIG_CHECK(mgr, options);
+ config_clear(mgr, options, var); /* clear it first */
+
+ if (!use_defaults)
+ return; /* all done */
+
+ if (var->cvar->initvalue) {
+ c = tor_malloc_zero(sizeof(config_line_t));
+ c->key = tor_strdup(var->cvar->member.name);
+ c->value = tor_strdup(var->cvar->initvalue);
+ if (config_assign_value(mgr, options, c, &msg) < 0) {
+ // LCOV_EXCL_START
+ log_warn(LD_BUG, "Failed to assign default: %s", msg);
+ tor_free(msg); /* if this happens it's a bug */
+ // LCOV_EXCL_STOP
+ }
+ config_free_lines(c);
+ }
+}
+
+/** Release storage held by <b>options</b>. */
+void
+config_free_(const config_mgr_t *mgr, void *options)
+{
+ if (!options)
+ return;
+
+ tor_assert(mgr);
+
+ if (mgr->toplevel->clear_fn) {
+ mgr->toplevel->clear_fn(mgr, options);
+ }
+ config_suite_t **suitep = config_mgr_get_suite_ptr(mgr, options);
+ if (suitep) {
+ tor_assert(smartlist_len((*suitep)->configs) ==
+ smartlist_len(mgr->subconfigs));
+ SMARTLIST_FOREACH_BEGIN(mgr->subconfigs, const config_format_t *, fmt) {
+ void *obj = smartlist_get((*suitep)->configs, fmt_sl_idx);
+ if (fmt->clear_fn) {
+ fmt->clear_fn(mgr, obj);
+ }
+ } SMARTLIST_FOREACH_END(fmt);
+ }
+
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ config_clear(mgr, options, mv);
+ } SMARTLIST_FOREACH_END(mv);
+
+ if (mgr->toplevel->extra) {
+ config_line_t **linep = STRUCT_VAR_P(options,
+ mgr->toplevel->extra->offset);
+ config_free_lines(*linep);
+ *linep = NULL;
+ }
+
+ if (suitep) {
+ SMARTLIST_FOREACH((*suitep)->configs, void *, obj, tor_free(obj));
+ config_suite_free(*suitep);
+ }
+
+ tor_free(options);
+}
+
+/** Return true iff the option <b>name</b> has the same value in <b>o1</b>
+ * and <b>o2</b>. Must not be called for LINELIST_S or OBSOLETE options.
+ */
+int
+config_is_same(const config_mgr_t *mgr,
+ const void *o1, const void *o2,
+ const char *name)
+{
+ CONFIG_CHECK(mgr, o1);
+ CONFIG_CHECK(mgr, o2);
+
+ const managed_var_t *var = config_mgr_find_var(mgr, name, true, NULL);
+ if (!var) {
+ return true;
+ }
+ const void *obj1 = config_mgr_get_obj(mgr, o1, var->object_idx);
+ const void *obj2 = config_mgr_get_obj(mgr, o2, var->object_idx);
+
+ return struct_var_eq(obj1, obj2, &var->cvar->member);
+}
+
+/**
+ * Return a list of the options which have changed between <b>options1</b> and
+ * <b>options2</b>. If an option has reverted to its default value, it has a
+ * value entry of NULL.
+ *
+ * <b>options1</b> and <b>options2</b> must be top-level configuration objects
+ * of the type managed by <b>mgr</b>.
+ **/
+config_line_t *
+config_get_changes(const config_mgr_t *mgr,
+ const void *options1, const void *options2)
+{
+ config_line_t *result = NULL;
+ config_line_t **next = &result;
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, managed_var_t *, mv) {
+ if (! config_var_should_list_changes(mv->cvar)) {
+ /* something else will check this var, or it doesn't need checking */
+ continue;
+ }
+ const void *obj1 = config_mgr_get_obj(mgr, options1, mv->object_idx);
+ const void *obj2 = config_mgr_get_obj(mgr, options2, mv->object_idx);
+
+ if (struct_var_eq(obj1, obj2, &mv->cvar->member)) {
+ continue;
+ }
+
+ const char *varname = mv->cvar->member.name;
+ config_line_t *line =
+ config_get_assigned_option(mgr, options2, varname, 1);
+
+ if (line) {
+ *next = line;
+ } else {
+ *next = tor_malloc_zero(sizeof(config_line_t));
+ (*next)->key = tor_strdup(varname);
+ }
+ while (*next)
+ next = &(*next)->next;
+ } SMARTLIST_FOREACH_END(mv);
+
+ return result;
+}
+
+/** Copy storage held by <b>old</b> into a new or_options_t and return it. */
+void *
+config_dup(const config_mgr_t *mgr, const void *old)
+{
+ void *newopts;
+
+ newopts = config_new(mgr);
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, managed_var_t *, mv) {
+ if (! config_var_needs_copy(mv->cvar)) {
+ // Something else will copy this option, or it doesn't need copying.
+ continue;
+ }
+ const void *oldobj = config_mgr_get_obj(mgr, old, mv->object_idx);
+ void *newobj = config_mgr_get_obj_mutable(mgr, newopts, mv->object_idx);
+ if (struct_var_copy(newobj, oldobj, &mv->cvar->member) < 0) {
+ // LCOV_EXCL_START
+ log_err(LD_BUG, "Unable to copy value for %s.",
+ mv->cvar->member.name);
+ tor_assert_unreached();
+ // LCOV_EXCL_STOP
+ }
+ } SMARTLIST_FOREACH_END(mv);
+
+ return newopts;
+}
+/** Set all vars in the configuration object <b>options</b> to their default
+ * values. */
+void
+config_init(const config_mgr_t *mgr, void *options)
+{
+ CONFIG_CHECK(mgr, options);
+
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ if (!mv->cvar->initvalue)
+ continue; /* defaults to NULL or 0 */
+ config_reset(mgr, options, mv, 1);
+ } SMARTLIST_FOREACH_END(mv);
+}
+
+/** Allocate and return a new string holding the written-out values of the vars
+ * in 'options'. If 'minimal', do not write out any default-valued vars.
+ * Else, if comment_defaults, write default values as comments.
+ */
+char *
+config_dump(const config_mgr_t *mgr, const void *default_options,
+ const void *options, int minimal,
+ int comment_defaults)
+{
+ const config_format_t *fmt = mgr->toplevel;
+ smartlist_t *elements;
+ const void *defaults = default_options;
+ void *defaults_tmp = NULL;
+ config_line_t *line, *assigned;
+ char *result;
+ char *msg = NULL;
+
+ if (defaults == NULL) {
+ defaults = defaults_tmp = config_new(mgr);
+ config_init(mgr, defaults_tmp);
+ }
+
+ /* XXX use a 1 here so we don't add a new log line while dumping */
+ if (default_options == NULL) {
+ if (fmt->validate_fn(NULL, defaults_tmp, defaults_tmp, 1, &msg) < 0) {
+ // LCOV_EXCL_START
+ log_err(LD_BUG, "Failed to validate default config: %s", msg);
+ tor_free(msg);
+ tor_assert(0);
+ // LCOV_EXCL_STOP
+ }
+ }
+
+ elements = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, managed_var_t *, mv) {
+ int comment_option = 0;
+ /* Don't save 'hidden' control variables. */
+ if (! config_var_is_dumpable(mv->cvar))
+ continue;
+ const char *name = mv->cvar->member.name;
+ if (minimal && config_is_same(mgr, options, defaults, name))
+ continue;
+ else if (comment_defaults &&
+ config_is_same(mgr, options, defaults, name))
+ comment_option = 1;
+
+ line = assigned =
+ config_get_assigned_option(mgr, options, name, 1);
+
+ for (; line; line = line->next) {
+ if (!strcmpstart(line->key, "__")) {
+ /* This check detects "hidden" variables inside LINELIST_V structures.
+ */
+ continue;
+ }
+ smartlist_add_asprintf(elements, "%s%s %s\n",
+ comment_option ? "# " : "",
+ line->key, line->value);
+ }
+ config_free_lines(assigned);
+ } SMARTLIST_FOREACH_END(mv);
+
+ if (fmt->extra) {
+ line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->offset);
+ for (; line; line = line->next) {
+ smartlist_add_asprintf(elements, "%s %s\n", line->key, line->value);
+ }
+ }
+
+ result = smartlist_join_strings(elements, "", 0, NULL);
+ SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
+ smartlist_free(elements);
+ config_free(mgr, defaults_tmp);
+ return result;
+}
+
+/**
+ * Return true if every member of <b>options</b> is in-range and well-formed.
+ * Return false otherwise. Log errors at level <b>severity</b>.
+ */
+bool
+config_check_ok(const config_mgr_t *mgr, const void *options, int severity)
+{
+ bool all_ok = true;
+
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ if (!struct_var_ok(options, &mv->cvar->member)) {
+ log_fn(severity, LD_BUG, "Invalid value for %s",
+ mv->cvar->member.name);
+ all_ok = false;
+ }
+ } SMARTLIST_FOREACH_END(mv);
+
+ return all_ok;
+}
diff --git a/src/lib/confmgt/confparse.h b/src/lib/confmgt/confparse.h
new file mode 100644
index 0000000000..2332f69790
--- /dev/null
+++ b/src/lib/confmgt/confparse.h
@@ -0,0 +1,212 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file confparse.h
+ *
+ * \brief Header for confparse.c.
+ */
+
+#ifndef TOR_CONFPARSE_H
+#define TOR_CONFPARSE_H
+
+#include "lib/conf/conftypes.h"
+#include "lib/conf/confmacros.h"
+#include "lib/testsupport/testsupport.h"
+
+/**
+ * An abbreviation or alias for a configuration option.
+ **/
+typedef struct config_abbrev_t {
+ /** The option name as abbreviated. Not case-sensitive. */
+ const char *abbreviated;
+ /** The full name of the option. Not case-sensitive. */
+ const char *full;
+ /** True if this abbreviation should only be allowed on the command line. */
+ int commandline_only;
+ /** True if we should warn whenever this abbreviation is used. */
+ int warn;
+} config_abbrev_t;
+
+/**
+ * A note that a configuration option is deprecated, with an explanation why.
+ */
+typedef struct config_deprecation_t {
+ /** The option that is deprecated. */
+ const char *name;
+ /** A user-facing string explaining why the option is deprecated. */
+ const char *why_deprecated;
+} config_deprecation_t;
+
+/**
+ * Handy macro for declaring "In the config file or on the command line, you
+ * can abbreviate <b>tok</b>s as <b>tok</b>". Used inside an array of
+ * config_abbrev_t.
+ *
+ * For example, to declare "NumCpu" as an abbreviation for "NumCPUs",
+ * you can say PLURAL(NumCpu).
+ **/
+#define PLURAL(tok) { #tok, #tok "s", 0, 0 }
+
+/**
+ * Type of a callback to validate whether a given configuration is
+ * well-formed and consistent.
+ *
+ * The configuration to validate is passed as <b>newval</b>. The previous
+ * configuration, if any, is provided in <b>oldval</b>. The
+ * <b>default_val</b> argument receives a configuration object initialized
+ * with default values for all its fields. The <b>from_setconf</b> argument
+ * is true iff the input comes from a SETCONF controller command.
+ *
+ * On success, return 0. On failure, set *<b>msg_out</b> to a newly allocated
+ * error message, and return -1.
+ *
+ * REFACTORING NOTE: Currently, this callback type is only used from inside
+ * config_dump(); later in our refactoring, it will be cleaned up and used
+ * more generally.
+ */
+typedef int (*validate_fn_t)(void *oldval,
+ void *newval,
+ void *default_val,
+ int from_setconf,
+ char **msg_out);
+
+struct config_mgr_t;
+
+/**
+ * Callback to clear all non-managed fields of a configuration object.
+ *
+ * <b>obj</b> is the configuration object whose non-managed fields should be
+ * cleared.
+ *
+ * (Regular fields get cleared by config_reset(), but you might have fields
+ * in the object that do not correspond to configuration variables. If those
+ * fields need to be cleared or freed, this is where to do it.)
+ */
+typedef void (*clear_cfg_fn_t)(const struct config_mgr_t *mgr, void *obj);
+
+/** Information on the keys, value types, key-to-struct-member mappings,
+ * variable descriptions, validation functions, and abbreviations for a
+ * configuration or storage format. */
+typedef struct config_format_t {
+ size_t size; /**< Size of the struct that everything gets parsed into. */
+ struct_magic_decl_t magic; /**< Magic number info for this struct. */
+ const config_abbrev_t *abbrevs; /**< List of abbreviations that we expand
+ * when parsing this format. */
+ const config_deprecation_t *deprecations; /** List of deprecated options */
+ const config_var_t *vars; /**< List of variables we recognize, their default
+ * values, and where we stick them in the
+ * structure. */
+ validate_fn_t validate_fn; /**< Function to validate config. */
+ clear_cfg_fn_t clear_fn; /**< Function to clear the configuration. */
+ /** If present, extra denotes a LINELIST variable for unrecognized
+ * lines. Otherwise, unrecognized lines are an error. */
+ const struct_member_t *extra;
+ /** The position of a config_suite_t pointer within the toplevel object,
+ * or -1 if there is no such pointer. */
+ ptrdiff_t config_suite_offset;
+} config_format_t;
+
+/**
+ * A collection of config_format_t objects to describe several objects
+ * that are all configured with the same configuration file.
+ *
+ * (NOTE: for now, this only handles a single config_format_t.)
+ **/
+typedef struct config_mgr_t config_mgr_t;
+
+config_mgr_t *config_mgr_new(const config_format_t *toplevel_fmt);
+void config_mgr_free_(config_mgr_t *mgr);
+int config_mgr_add_format(config_mgr_t *mgr,
+ const config_format_t *fmt);
+void config_mgr_freeze(config_mgr_t *mgr);
+#define config_mgr_free(mgr) \
+ FREE_AND_NULL(config_mgr_t, config_mgr_free_, (mgr))
+struct smartlist_t *config_mgr_list_vars(const config_mgr_t *mgr);
+struct smartlist_t *config_mgr_list_deprecated_vars(const config_mgr_t *mgr);
+
+/** A collection of managed configuration objects. */
+typedef struct config_suite_t config_suite_t;
+
+/**
+ * Flag for config_assign: if set, then "resetting" an option changes it to
+ * its default value, as specified in the config_var_t. Otherwise,
+ * "resetting" an option changes it to a type-dependent null value --
+ * typically 0 or NULL.
+ *
+ * (An option is "reset" when it is set to an empty value, or as described in
+ * CAL_CLEAR_FIRST).
+ **/
+#define CAL_USE_DEFAULTS (1u<<0)
+/**
+ * Flag for config_assign: if set, then we reset every provided config
+ * option before we set it.
+ *
+ * For example, if this flag is not set, then passing a multi-line option to
+ * config_assign will cause any previous value to be extended. But if this
+ * flag is set, then a multi-line option will replace any previous value.
+ **/
+#define CAL_CLEAR_FIRST (1u<<1)
+/**
+ * Flag for config_assign: if set, we warn about deprecated options.
+ **/
+#define CAL_WARN_DEPRECATIONS (1u<<2)
+
+void *config_new(const config_mgr_t *fmt);
+void config_free_(const config_mgr_t *fmt, void *options);
+#define config_free(mgr, options) do { \
+ config_free_((mgr), (options)); \
+ (options) = NULL; \
+ } while (0)
+
+struct config_line_t *config_get_assigned_option(const config_mgr_t *mgr,
+ const void *options, const char *key,
+ int escape_val);
+int config_is_same(const config_mgr_t *fmt,
+ const void *o1, const void *o2,
+ const char *name);
+struct config_line_t *config_get_changes(const config_mgr_t *mgr,
+ const void *options1, const void *options2);
+void config_init(const config_mgr_t *mgr, void *options);
+void *config_dup(const config_mgr_t *mgr, const void *old);
+char *config_dump(const config_mgr_t *mgr, const void *default_options,
+ const void *options, int minimal,
+ int comment_defaults);
+bool config_check_ok(const config_mgr_t *mgr, const void *options,
+ int severity);
+int config_assign(const config_mgr_t *mgr, void *options,
+ struct config_line_t *list,
+ unsigned flags, char **msg);
+const char *config_find_deprecation(const config_mgr_t *mgr,
+ const char *key);
+const char *config_find_option_name(const config_mgr_t *mgr,
+ const char *key);
+const char *config_expand_abbrev(const config_mgr_t *mgr,
+ const char *option,
+ int command_line, int warn_obsolete);
+void warn_deprecated_option(const char *what, const char *why);
+
+bool config_var_is_settable(const config_var_t *var);
+bool config_var_is_listable(const config_var_t *var);
+
+/* Helper macros to compare an option across two configuration objects */
+#define CFG_EQ_BOOL(a,b,opt) ((a)->opt == (b)->opt)
+#define CFG_EQ_INT(a,b,opt) ((a)->opt == (b)->opt)
+#define CFG_EQ_STRING(a,b,opt) (!strcmp_opt((a)->opt, (b)->opt))
+#define CFG_EQ_SMARTLIST(a,b,opt) smartlist_strings_eq((a)->opt, (b)->opt)
+#define CFG_EQ_LINELIST(a,b,opt) config_lines_eq((a)->opt, (b)->opt)
+#define CFG_EQ_ROUTERSET(a,b,opt) routerset_equal((a)->opt, (b)->opt)
+
+#ifdef CONFPARSE_PRIVATE
+STATIC void config_reset_line(const config_mgr_t *mgr, void *options,
+ const char *key, int use_defaults);
+STATIC void *config_mgr_get_obj_mutable(const config_mgr_t *mgr,
+ void *toplevel, int idx);
+STATIC const void *config_mgr_get_obj(const config_mgr_t *mgr,
+ const void *toplevel, int idx);
+#endif /* defined(CONFPARSE_PRIVATE) */
+
+#endif /* !defined(TOR_CONFPARSE_H) */
diff --git a/src/lib/confmgt/include.am b/src/lib/confmgt/include.am
index aa5b37fdb5..81cd868e5e 100644
--- a/src/lib/confmgt/include.am
+++ b/src/lib/confmgt/include.am
@@ -6,6 +6,7 @@ endif
# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_confmgt_a_SOURCES = \
+ src/lib/confmgt/confparse.c \
src/lib/confmgt/structvar.c \
src/lib/confmgt/type_defs.c \
src/lib/confmgt/typedvar.c \
@@ -18,6 +19,7 @@ src_lib_libtor_confmgt_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
+ src/lib/confmgt/confparse.h \
src/lib/confmgt/structvar.h \
src/lib/confmgt/type_defs.h \
src/lib/confmgt/typedvar.h \
diff --git a/src/lib/confmgt/type_defs.c b/src/lib/confmgt/type_defs.c
index 324b62e56c..ed930fb02a 100644
--- a/src/lib/confmgt/type_defs.c
+++ b/src/lib/confmgt/type_defs.c
@@ -37,12 +37,17 @@
#include <stddef.h>
#include <string.h>
+#include <errno.h>
//////
// CONFIG_TYPE_STRING
// CONFIG_TYPE_FILENAME
//
// These two types are the same for now, but they have different names.
+//
+// Warning: For this type, the default value (NULL) and "" are considered
+// different values. That is generally risky, and best avoided for other
+// types in the future.
//////
static int
@@ -283,8 +288,23 @@ double_parse(void *target, const char *value, char **errmsg,
(void)params;
(void)errmsg;
double *v = (double*)target;
- // XXXX This is the preexisting behavior, but we should detect errors here.
- *v = atof(value);
+ char *endptr=NULL;
+ errno = 0;
+ *v = strtod(value, &endptr);
+ if (endptr == value || *endptr != '\0') {
+ // Either there are no converted characters, or there were some characters
+ // that didn't get converted.
+ tor_asprintf(errmsg, "Could not convert %s to a number.", escaped(value));
+ return -1;
+ }
+ if (errno == ERANGE) {
+ // strtod will set errno to ERANGE on underflow or overflow.
+ bool underflow = -.00001 < *v && *v < .00001;
+ tor_asprintf(errmsg,
+ "%s is too %s to express as a floating-point number.",
+ escaped(value), underflow ? "small" : "large");
+ return -1;
+ }
return 0;
}
diff --git a/src/lib/confmgt/var_type_def_st.h b/src/lib/confmgt/var_type_def_st.h
index f1131ff116..2bf3d37cae 100644
--- a/src/lib/confmgt/var_type_def_st.h
+++ b/src/lib/confmgt/var_type_def_st.h
@@ -39,6 +39,12 @@ struct config_line_t;
* All functions here take a <b>params</b> argument, whose value
* is determined by the type definition. Two types may have the
* same functions, but differ only in parameters.
+ *
+ * Implementation considerations: If "" encodes a valid value for a type, try
+ * to make sure that it encodes the same thing as the default value for the
+ * type (that is, the value that is set by config_clear() or memset(0)). If
+ * this is not the case, you need to make extra certain that your parse/encode
+ * implementations preserve the NULL/"" distinction.
**/
struct var_type_fns_t {
/**
diff --git a/src/lib/crypt_ops/aes_openssl.c b/src/lib/crypt_ops/aes_openssl.c
index 2f985d4512..64564892ad 100644
--- a/src/lib/crypt_ops/aes_openssl.c
+++ b/src/lib/crypt_ops/aes_openssl.c
@@ -148,7 +148,7 @@ evaluate_ctr_for_aes(void)
{
return 0;
}
-#else /* !(defined(USE_EVP_AES_CTR)) */
+#else /* !defined(USE_EVP_AES_CTR) */
/*======================================================================*/
/* Interface to AES code, and counter implementation */
diff --git a/src/lib/crypt_ops/compat_openssl.h b/src/lib/crypt_ops/compat_openssl.h
index 9c10386c34..61ca51315f 100644
--- a/src/lib/crypt_ops/compat_openssl.h
+++ b/src/lib/crypt_ops/compat_openssl.h
@@ -45,7 +45,7 @@
((st) == SSL3_ST_SW_SRVR_HELLO_B))
#define OSSL_HANDSHAKE_STATE int
#define CONST_IF_OPENSSL_1_1_API
-#else /* !(!defined(OPENSSL_1_1_API)) */
+#else /* defined(OPENSSL_1_1_API) */
#define STATE_IS_SW_SERVER_HELLO(st) \
((st) == TLS_ST_SW_SRVR_HELLO)
#define CONST_IF_OPENSSL_1_1_API const
diff --git a/src/lib/crypt_ops/crypto_dh_nss.c b/src/lib/crypt_ops/crypto_dh_nss.c
index 379eb84a4f..aa95fb508f 100644
--- a/src/lib/crypt_ops/crypto_dh_nss.c
+++ b/src/lib/crypt_ops/crypto_dh_nss.c
@@ -5,7 +5,7 @@
/* See LICENSE for licensing information */
/**
- * \file crypto_dh_nss.h
+ * \file crypto_dh_nss.c
*
* \brief NSS implementation of Diffie-Hellman over Z_p.
**/
diff --git a/src/lib/crypt_ops/crypto_dh_openssl.c b/src/lib/crypt_ops/crypto_dh_openssl.c
index 75cee1b596..e7f22d749b 100644
--- a/src/lib/crypt_ops/crypto_dh_openssl.c
+++ b/src/lib/crypt_ops/crypto_dh_openssl.c
@@ -68,7 +68,7 @@ crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
goto out;
if (!DH_set0_pqg(dh, dh_p, NULL, dh_g))
goto out;
-#else /* !(defined(OPENSSL_1_1_API)) */
+#else /* !defined(OPENSSL_1_1_API) */
if (!(dh->p = BN_dup(p)))
goto out;
if (!(dh->g = BN_dup(g)))
@@ -103,7 +103,7 @@ crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
#endif /* 0 */
/**
- * Helper: convert <b>hex<b> to a bignum, and return it. Assert that the
+ * Helper: convert <b>hex</b> to a bignum, and return it. Assert that the
* operation was successful.
*/
static BIGNUM *
@@ -231,7 +231,7 @@ new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g)
if (!DH_set_length(res_dh, DH_PRIVATE_KEY_BITS))
goto err;
-#else /* !(defined(OPENSSL_1_1_API)) */
+#else /* !defined(OPENSSL_1_1_API) */
res_dh->p = dh_p;
res_dh->g = dh_g;
res_dh->length = DH_PRIVATE_KEY_BITS;
@@ -298,7 +298,7 @@ crypto_dh_generate_public(crypto_dh_t *dh)
"the-universe chances really do happen. Treating as a failure.");
return -1;
}
-#else /* !(defined(OPENSSL_1_1_API)) */
+#else /* !defined(OPENSSL_1_1_API) */
if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) {
/* LCOV_EXCL_START
* If this happens, then openssl's DH implementation is busted. */
diff --git a/src/lib/crypt_ops/crypto_digest.c b/src/lib/crypt_ops/crypto_digest.c
index 64a7d2d52c..ba226f8756 100644
--- a/src/lib/crypt_ops/crypto_digest.c
+++ b/src/lib/crypt_ops/crypto_digest.c
@@ -149,7 +149,7 @@ struct crypto_xof_t {
* outside the tests yet.
*/
EVP_MD_CTX *ctx;
-#else /* !(defined(OPENSSL_HAS_SHAKE3_EVP)) */
+#else /* !defined(OPENSSL_HAS_SHAKE3_EVP) */
keccak_state s;
#endif /* defined(OPENSSL_HAS_SHAKE3_EVP) */
};
@@ -169,7 +169,7 @@ crypto_xof_new(void)
tor_assert(xof->ctx);
int r = EVP_DigestInit(xof->ctx, EVP_shake256());
tor_assert(r == 1);
-#else /* !(defined(OPENSSL_HAS_SHAKE256)) */
+#else /* !defined(OPENSSL_HAS_SHAKE256) */
keccak_xof_init(&xof->s, 256);
#endif /* defined(OPENSSL_HAS_SHAKE256) */
return xof;
@@ -236,7 +236,7 @@ crypto_xof(uint8_t *output, size_t output_len,
r = EVP_DigestFinalXOF(ctx, output, output_len);
tor_assert(r == 1);
EVP_MD_CTX_free(ctx);
-#else /* !(defined(OPENSSL_HAS_SHA3)) */
+#else /* !defined(OPENSSL_HAS_SHA3) */
crypto_xof_t *xof = crypto_xof_new();
crypto_xof_add_bytes(xof, input, input_len);
crypto_xof_squeeze_bytes(xof, output, output_len);
diff --git a/src/lib/crypt_ops/crypto_digest_openssl.c b/src/lib/crypt_ops/crypto_digest_openssl.c
index c631b0eac0..b0d8b6aee9 100644
--- a/src/lib/crypt_ops/crypto_digest_openssl.c
+++ b/src/lib/crypt_ops/crypto_digest_openssl.c
@@ -212,7 +212,7 @@ crypto_digest_new_internal(digest_algorithm_t algorithm)
return NULL;
}
break;
-#else /* !(defined(OPENSSL_HAS_SHA3)) */
+#else /* !defined(OPENSSL_HAS_SHA3) */
case DIGEST_SHA3_256:
keccak_digest_init(&r->d.sha3, 256);
break;
@@ -310,7 +310,7 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
tor_assert(r);
}
break;
-#else /* !(defined(OPENSSL_HAS_SHA3)) */
+#else /* !defined(OPENSSL_HAS_SHA3) */
case DIGEST_SHA3_256: /* FALLSTHROUGH */
case DIGEST_SHA3_512:
keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
@@ -354,7 +354,7 @@ crypto_digest_get_digest(crypto_digest_t *digest,
EVP_MD_CTX_free(tmp);
tor_assert(res == 1);
goto done;
-#else /* !(defined(OPENSSL_HAS_SHA3)) */
+#else /* !defined(OPENSSL_HAS_SHA3) */
/* Tiny-Keccak handles copying into a temporary ctx, and also can handle
* short output buffers by truncating appropriately. */
keccak_digest_sum(&digest->d.sha3, (uint8_t *)out, out_len);
diff --git a/src/lib/crypt_ops/crypto_hkdf.c b/src/lib/crypt_ops/crypto_hkdf.c
index e0f3d65ad1..e0d241d4ea 100644
--- a/src/lib/crypt_ops/crypto_hkdf.c
+++ b/src/lib/crypt_ops/crypto_hkdf.c
@@ -109,7 +109,7 @@ crypto_expand_key_material_rfc5869_sha256_openssl(
return 0;
}
-#else /* !(defined(HAVE_OPENSSL_HKDF)) */
+#else /* !defined(HAVE_OPENSSL_HKDF) */
/**
* Perform RFC5869 HKDF computation using our own legacy implementation.
@@ -191,7 +191,7 @@ crypto_expand_key_material_rfc5869_sha256(
salt_in_len, info_in,
info_in_len,
key_out, key_out_len);
-#else /* !(defined(HAVE_OPENSSL_HKDF)) */
+#else /* !defined(HAVE_OPENSSL_HKDF) */
return crypto_expand_key_material_rfc5869_sha256_legacy(key_in,
key_in_len, salt_in,
salt_in_len, info_in,
diff --git a/src/lib/crypt_ops/crypto_ope.c b/src/lib/crypt_ops/crypto_ope.c
index 4bd4b35706..ed832d852e 100644
--- a/src/lib/crypt_ops/crypto_ope.c
+++ b/src/lib/crypt_ops/crypto_ope.c
@@ -57,7 +57,7 @@ ope_val_from_le(ope_val_t x)
((x) >> 8) |
(((x)&0xff) << 8);
}
-#else /* !(defined(WORDS_BIGENDIAN)) */
+#else /* !defined(WORDS_BIGENDIAN) */
#define ope_val_from_le(x) (x)
#endif /* defined(WORDS_BIGENDIAN) */
diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c
index 917ef4597b..617c0dcd76 100644
--- a/src/lib/crypt_ops/crypto_openssl_mgt.c
+++ b/src/lib/crypt_ops/crypto_openssl_mgt.c
@@ -178,6 +178,10 @@ crypto_openssl_free_all(void)
tor_free(crypto_openssl_version_str);
tor_free(crypto_openssl_header_version_str);
+ /* Destroying a locked mutex is undefined behaviour. This mutex may be
+ * locked, because multiple threads can access it. But we need to destroy
+ * it, otherwise re-initialisation will trigger undefined behaviour.
+ * See #31735 for details. */
#ifndef NEW_THREAD_API
if (n_openssl_mutexes_) {
int n = n_openssl_mutexes_;
@@ -202,7 +206,7 @@ crypto_openssl_early_init(void)
OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
OPENSSL_INIT_ADD_ALL_CIPHERS |
OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
-#else /* !(defined(OPENSSL_1_1_API)) */
+#else /* !defined(OPENSSL_1_1_API) */
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
#endif /* defined(OPENSSL_1_1_API) */
diff --git a/src/lib/crypt_ops/crypto_rand.c b/src/lib/crypt_ops/crypto_rand.c
index a80a98f267..afbafbfa35 100644
--- a/src/lib/crypt_ops/crypto_rand.c
+++ b/src/lib/crypt_ops/crypto_rand.c
@@ -248,7 +248,7 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
(void)out;
(void)out_len;
return -1;
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
static const char *filenames[] = {
"/dev/srandom", "/dev/urandom", "/dev/random", NULL
};
@@ -520,7 +520,7 @@ crypto_rand_unmocked(char *to, size_t n)
#undef BUFLEN
}
-#else /* !(defined(ENABLE_NSS)) */
+#else /* !defined(ENABLE_NSS) */
int r = RAND_bytes((unsigned char*)to, (int)n);
/* We consider a PRNG failure non-survivable. Let's assert so that we get a
* stack trace about where it happened.
diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c
index fbdc76ccd6..05d7c26b25 100644
--- a/src/lib/crypt_ops/crypto_rsa_openssl.c
+++ b/src/lib/crypt_ops/crypto_rsa_openssl.c
@@ -53,7 +53,7 @@ crypto_pk_key_is_private(const crypto_pk_t *k)
const BIGNUM *p, *q;
RSA_get0_factors(k->key, &p, &q);
return p != NULL; /* XXX/yawning: Should we check q? */
-#else /* !(defined(OPENSSL_1_1_API)) */
+#else /* !defined(OPENSSL_1_1_API) */
return k && k->key && k->key->p;
#endif /* defined(OPENSSL_1_1_API) */
}
@@ -287,7 +287,7 @@ crypto_pk_num_bits(crypto_pk_t *env)
tor_assert(n != NULL);
return RSA_bits(env->key);
-#else /* !(defined(OPENSSL_1_1_API)) */
+#else /* !defined(OPENSSL_1_1_API) */
tor_assert(env->key->n);
return BN_num_bits(env->key->n);
#endif /* defined(OPENSSL_1_1_API) */
diff --git a/src/lib/crypt_ops/crypto_s2k.c b/src/lib/crypt_ops/crypto_s2k.c
index 5cf98e3e64..361db18927 100644
--- a/src/lib/crypt_ops/crypto_s2k.c
+++ b/src/lib/crypt_ops/crypto_s2k.c
@@ -285,7 +285,7 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
if (rv < 0)
return S2K_FAILED;
return (int)key_out_len;
-#else /* !(defined(ENABLE_OPENSSL)) */
+#else /* !defined(ENABLE_OPENSSL) */
SECItem passItem = { .type = siBuffer,
.data = (unsigned char *) secret,
.len = (int)secret_len };
@@ -348,7 +348,7 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
if (rv != 0)
return S2K_FAILED;
return (int)key_out_len;
-#else /* !(defined(HAVE_SCRYPT)) */
+#else /* !defined(HAVE_SCRYPT) */
return S2K_NO_SCRYPT_SUPPORT;
#endif /* defined(HAVE_SCRYPT) */
}
diff --git a/src/lib/crypt_ops/crypto_sys.h b/src/lib/crypt_ops/crypto_sys.h
index 894243b175..ff449d2e0b 100644
--- a/src/lib/crypt_ops/crypto_sys.h
+++ b/src/lib/crypt_ops/crypto_sys.h
@@ -2,7 +2,7 @@
/* See LICENSE for licensing information */
/**
- * \file log_crypto.h
+ * \file crypto_sys.h
* \brief Declare subsystem object for the crypto module.
**/
diff --git a/src/lib/defs/logging_types.h b/src/lib/defs/logging_types.h
index 57db818007..d3eacde464 100644
--- a/src/lib/defs/logging_types.h
+++ b/src/lib/defs/logging_types.h
@@ -20,4 +20,4 @@
/** Mask of zero or more log domains, OR'd together. */
typedef uint64_t log_domain_mask_t;
-#endif
+#endif /* !defined(TOR_LOGGING_TYPES_H) */
diff --git a/src/lib/dispatch/.may_include b/src/lib/dispatch/.may_include
index 7f2df5859f..884f4c0dbc 100644
--- a/src/lib/dispatch/.may_include
+++ b/src/lib/dispatch/.may_include
@@ -8,3 +8,4 @@ lib/dispatch/*.h
lib/intmath/*.h
lib/log/*.h
lib/malloc/*.h
+lib/testsupport/*.h \ No newline at end of file
diff --git a/src/lib/dispatch/dispatch_cfg.h b/src/lib/dispatch/dispatch_cfg.h
index 61fade7240..348dce8d40 100644
--- a/src/lib/dispatch/dispatch_cfg.h
+++ b/src/lib/dispatch/dispatch_cfg.h
@@ -8,6 +8,7 @@
#define TOR_DISPATCH_CFG_H
#include "lib/dispatch/msgtypes.h"
+#include "lib/testsupport/testsupport.h"
/**
* A "dispatch_cfg" is the configuration used to set up a dispatcher.
@@ -36,4 +37,9 @@ int dcfg_add_recv(dispatch_cfg_t *cfg, message_id_t msg,
void dcfg_free_(dispatch_cfg_t *cfg);
+#ifdef DISPATCH_NEW_PRIVATE
+struct smartlist_t;
+STATIC int max_in_u16_sl(const struct smartlist_t *sl, int dflt);
+#endif
+
#endif /* !defined(TOR_DISPATCH_CFG_H) */
diff --git a/src/lib/dispatch/dispatch_new.c b/src/lib/dispatch/dispatch_new.c
index b89ef43ea7..d8e59d610a 100644
--- a/src/lib/dispatch/dispatch_new.c
+++ b/src/lib/dispatch/dispatch_new.c
@@ -9,6 +9,7 @@
* \brief Code to construct a dispatch_t from a dispatch_cfg_t.
**/
+#define DISPATCH_NEW_PRIVATE
#define DISPATCH_PRIVATE
#include "orconfig.h"
@@ -26,14 +27,14 @@
/** Given a smartlist full of (possibly NULL) pointers to uint16_t values,
* return the largest value, or dflt if the list is empty. */
-static int
-max_in_sl(const smartlist_t *sl, int dflt)
+STATIC int
+max_in_u16_sl(const smartlist_t *sl, int dflt)
{
uint16_t *maxptr = NULL;
SMARTLIST_FOREACH_BEGIN(sl, uint16_t *, u) {
if (!maxptr)
maxptr = u;
- else if (*u > *maxptr)
+ else if (u && *u > *maxptr)
maxptr = u;
} SMARTLIST_FOREACH_END(u);
@@ -118,11 +119,12 @@ dispatch_new(const dispatch_cfg_t *cfg)
smartlist_len(cfg->recv_by_msg)) + 1;
/* Any channel that any message has counts towards the number of channels. */
- const size_t n_chans = (size_t) MAX(1, max_in_sl(cfg->chan_by_msg,0)) + 1;
+ const size_t n_chans = (size_t)
+ MAX(1, max_in_u16_sl(cfg->chan_by_msg,0)) + 1;
/* Any type that a message has, or that has functions, counts towards
* the number of types. */
- const size_t n_types = (size_t) MAX(max_in_sl(cfg->type_by_msg,0),
+ const size_t n_types = (size_t) MAX(max_in_u16_sl(cfg->type_by_msg,0),
smartlist_len(cfg->fns_by_type)) + 1;
d->n_msgs = n_msgs;
diff --git a/src/lib/encoding/pem.c b/src/lib/encoding/pem.c
index 24b238b130..95f93ebeff 100644
--- a/src/lib/encoding/pem.c
+++ b/src/lib/encoding/pem.c
@@ -42,7 +42,7 @@ pem_encoded_size(size_t src_len, const char *objtype)
/**
* PEM-encode the <b>srclen</b>-byte object at <b>src</b> into the
- * <b>destlen<\b>-byte buffer at <b>dest</b>, tagging it with <b>objtype</b>.
+ * <b>destlen</b>-byte buffer at <b>dest</b>, tagging it with <b>objtype</b>.
* Return 0 on success and -1 on failure.
*/
int
diff --git a/src/lib/err/backtrace.c b/src/lib/err/backtrace.c
index 75d5093c54..ce8ddcd7c0 100644
--- a/src/lib/err/backtrace.c
+++ b/src/lib/err/backtrace.c
@@ -52,12 +52,14 @@
#include <pthread.h>
#endif
+#include "lib/cc/ctassert.h"
+
#define EXPOSE_CLEAN_BACKTRACE
#include "lib/err/backtrace.h"
-#include "lib/err/torerr.h"
#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
- defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION)
+ defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION) && \
+ defined(HAVE_PTHREAD_H)
#define USE_BACKTRACE
#endif
@@ -72,15 +74,40 @@
static char bt_version[128] = "";
#ifdef USE_BACKTRACE
+
/** Largest stack depth to try to dump. */
#define MAX_DEPTH 256
-/** Static allocation of stack to dump. This is static so we avoid stack
- * pressure. */
-static void *cb_buf[MAX_DEPTH];
+/** The size of the callback buffer, so we can clear it in unlock_cb_buf(). */
+#define SIZEOF_CB_BUF (MAX_DEPTH * sizeof(void *))
/** Protects cb_buf from concurrent access. Pthreads, since this code
* is Unix-only, and since this code needs to be lowest-level. */
static pthread_mutex_t cb_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
+/** Lock and return a static stack pointer buffer that can hold up to
+ * MAX_DEPTH function pointers. */
+static void **
+lock_cb_buf(void)
+{
+ /* Lock the mutex first, before even declaring the buffer. */
+ pthread_mutex_lock(&cb_buf_mutex);
+
+ /** Static allocation of stack to dump. This is static so we avoid stack
+ * pressure. */
+ static void *cb_buf[MAX_DEPTH];
+ CTASSERT(SIZEOF_CB_BUF == sizeof(cb_buf));
+ memset(cb_buf, 0, SIZEOF_CB_BUF);
+
+ return cb_buf;
+}
+
+/** Unlock the static stack pointer buffer. */
+static void
+unlock_cb_buf(void **cb_buf)
+{
+ memset(cb_buf, 0, SIZEOF_CB_BUF);
+ pthread_mutex_unlock(&cb_buf_mutex);
+}
+
/** Change a stacktrace in <b>stack</b> of depth <b>depth</b> so that it will
* log the correct function from which a signal was received with context
* <b>ctx</b>. (When we get a signal, the current function will not have
@@ -104,7 +131,7 @@ clean_backtrace(void **stack, size_t depth, const ucontext_t *ctx)
return;
stack[n] = (void*) ctx->PC_FROM_UCONTEXT;
-#else /* !(defined(PC_FROM_UCONTEXT)) */
+#else /* !defined(PC_FROM_UCONTEXT) */
(void) depth;
(void) ctx;
(void) stack;
@@ -122,7 +149,7 @@ log_backtrace_impl(int severity, log_domain_mask_t domain, const char *msg,
char **symbols;
size_t i;
- pthread_mutex_lock(&cb_buf_mutex);
+ void **cb_buf = lock_cb_buf();
depth = backtrace(cb_buf, MAX_DEPTH);
symbols = backtrace_symbols(cb_buf, (int)depth);
@@ -140,7 +167,7 @@ log_backtrace_impl(int severity, log_domain_mask_t domain, const char *msg,
raw_free(symbols);
done:
- pthread_mutex_unlock(&cb_buf_mutex);
+ unlock_cb_buf(cb_buf);
}
static void crash_handler(int sig, siginfo_t *si, void *ctx_)
@@ -156,6 +183,8 @@ crash_handler(int sig, siginfo_t *si, void *ctx_)
int n_fds, i;
const int *fds = NULL;
+ void **cb_buf = lock_cb_buf();
+
(void) si;
depth = backtrace(cb_buf, MAX_DEPTH);
@@ -172,7 +201,9 @@ crash_handler(int sig, siginfo_t *si, void *ctx_)
for (i=0; i < n_fds; ++i)
backtrace_symbols_fd(cb_buf, (int)depth, fds[i]);
- abort();
+ unlock_cb_buf(cb_buf);
+
+ tor_raw_abort_();
}
/** Write a backtrace to all of the emergency-error fds. */
@@ -183,20 +214,26 @@ dump_stack_symbols_to_error_fds(void)
const int *fds = NULL;
size_t depth;
+ void **cb_buf = lock_cb_buf();
+
depth = backtrace(cb_buf, MAX_DEPTH);
n_fds = tor_log_get_sigsafe_err_fds(&fds);
for (i=0; i < n_fds; ++i)
backtrace_symbols_fd(cb_buf, (int)depth, fds[i]);
+
+ unlock_cb_buf(cb_buf);
}
+/* The signals that we want our backtrace handler to trap */
+static int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS,
+ SIGIO, -1 };
+
/** Install signal handlers as needed so that when we crash, we produce a
* useful stack trace. Return 0 on success, -errno on failure. */
static int
install_bt_handler(void)
{
- int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS,
- SIGIO, -1 };
int i, rv=0;
struct sigaction sa;
@@ -219,10 +256,12 @@ install_bt_handler(void)
* libc has pre-loaded the symbols we need to dump things, so that later
* reads won't be denied by the sandbox code */
char **symbols;
+ void **cb_buf = lock_cb_buf();
size_t depth = backtrace(cb_buf, MAX_DEPTH);
symbols = backtrace_symbols(cb_buf, (int) depth);
if (symbols)
raw_free(symbols);
+ unlock_cb_buf(cb_buf);
}
return rv;
@@ -232,6 +271,23 @@ install_bt_handler(void)
static void
remove_bt_handler(void)
{
+ int i;
+
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigfillset(&sa.sa_mask);
+
+ for (i = 0; trap_signals[i] >= 0; ++i) {
+ /* remove_bt_handler() is called on shutdown, from low-level code.
+ * It's not a fatal error, so we just ignore it. */
+ (void)sigaction(trap_signals[i], &sa, NULL);
+ }
+
+ /* cb_buf_mutex is statically initialised, so we can not destroy it.
+ * If we destroy it, and then re-initialise tor, all our backtraces will
+ * fail. */
}
#endif /* defined(USE_BACKTRACE) */
diff --git a/src/lib/err/torerr.c b/src/lib/err/torerr.c
index 48fcf35e06..0a4ee5d417 100644
--- a/src/lib/err/torerr.c
+++ b/src/lib/err/torerr.c
@@ -110,6 +110,14 @@ tor_log_get_sigsafe_err_fds(const int **out)
* Update the list of fds that get errors from inside a signal handler or
* other emergency condition. Ignore any beyond the first
* TOR_SIGSAFE_LOG_MAX_FDS.
+ *
+ * These fds must remain open even after the log module has shut down. (And
+ * they should remain open even while logs are being reconfigured.) Therefore,
+ * any fds closed by the log module should be dup()ed, and the duplicate fd
+ * should be given to the err module in fds. In particular, the log module
+ * closes the file log fds, but does not close the stdio log fds.
+ *
+ * If fds is NULL or n is 0, clears the list of error fds.
*/
void
tor_log_set_sigsafe_err_fds(const int *fds, int n)
@@ -118,8 +126,18 @@ tor_log_set_sigsafe_err_fds(const int *fds, int n)
n = TOR_SIGSAFE_LOG_MAX_FDS;
}
- memcpy(sigsafe_log_fds, fds, n * sizeof(int));
- n_sigsafe_log_fds = n;
+ /* Clear the entire array. This code mitigates against some race conditions,
+ * but there are still some races here:
+ * - err logs are disabled while the array is cleared, and
+ * - a thread can read the old value of n_sigsafe_log_fds, then read a
+ * partially written array.
+ * We could fix these races using atomics, but atomics use the err module. */
+ n_sigsafe_log_fds = 0;
+ memset(sigsafe_log_fds, 0, sizeof(sigsafe_log_fds));
+ if (fds && n > 0) {
+ memcpy(sigsafe_log_fds, fds, n * sizeof(int));
+ n_sigsafe_log_fds = n;
+ }
}
/**
@@ -133,6 +151,32 @@ tor_log_reset_sigsafe_err_fds(void)
}
/**
+ * Close the list of fds that get errors from inside a signal handler or
+ * other emergency condition. These fds are shared with the logging code:
+ * closing them flushes the log buffers, and prevents any further logging.
+ *
+ * This function closes stderr, so it should only be called immediately before
+ * process shutdown.
+ */
+void
+tor_log_close_sigsafe_err_fds(void)
+{
+ int n_fds, i;
+ const int *fds = NULL;
+
+ n_fds = tor_log_get_sigsafe_err_fds(&fds);
+ for (i = 0; i < n_fds; ++i) {
+ /* tor_log_close_sigsafe_err_fds_on_error() is called on error and on
+ * shutdown, so we can't log or take any useful action if close()
+ * fails. */
+ (void)close(fds[i]);
+ }
+
+ /* Don't even try logging, we've closed all the log fds. */
+ tor_log_set_sigsafe_err_fds(NULL, 0);
+}
+
+/**
* Set the granularity (in ms) to use when reporting fatal errors outside
* the logging system.
*/
@@ -171,6 +215,18 @@ tor_raw_assertion_failed_msg_(const char *file, int line, const char *expr,
tor_log_err_sigsafe_write("\n");
}
+/**
+ * Call the abort() function to kill the current process with a fatal
+ * error. But first, close the raw error file descriptors, so error messages
+ * are written before process termination.
+ **/
+void
+tor_raw_abort_(void)
+{
+ tor_log_close_sigsafe_err_fds();
+ abort();
+}
+
/* As format_{hex,dex}_number_sigsafe, but takes a <b>radix</b> argument
* in range 2..16 inclusive. */
static int
@@ -205,7 +261,7 @@ format_number_sigsafe(unsigned long x, char *buf, int buf_len,
unsigned digit = (unsigned) (x % radix);
if (cp <= buf) {
/* Not tor_assert(); see above. */
- abort();
+ tor_raw_abort_();
}
--cp;
*cp = "0123456789ABCDEF"[digit];
@@ -214,7 +270,7 @@ format_number_sigsafe(unsigned long x, char *buf, int buf_len,
/* NOT tor_assert; see above. */
if (cp != buf) {
- abort(); // LCOV_EXCL_LINE
+ tor_raw_abort_(); // LCOV_EXCL_LINE
}
return len;
diff --git a/src/lib/err/torerr.h b/src/lib/err/torerr.h
index c2da6697a9..0e839cb1ba 100644
--- a/src/lib/err/torerr.h
+++ b/src/lib/err/torerr.h
@@ -20,13 +20,13 @@
#define raw_assert(expr) STMT_BEGIN \
if (!(expr)) { \
tor_raw_assertion_failed_msg_(__FILE__, __LINE__, #expr, NULL); \
- abort(); \
+ tor_raw_abort_(); \
} \
STMT_END
#define raw_assert_unreached(expr) raw_assert(0)
#define raw_assert_unreached_msg(msg) STMT_BEGIN \
tor_raw_assertion_failed_msg_(__FILE__, __LINE__, "0", (msg)); \
- abort(); \
+ tor_raw_abort_(); \
STMT_END
void tor_raw_assertion_failed_msg_(const char *file, int line,
@@ -40,8 +40,11 @@ void tor_log_err_sigsafe(const char *m, ...);
int tor_log_get_sigsafe_err_fds(const int **out);
void tor_log_set_sigsafe_err_fds(const int *fds, int n);
void tor_log_reset_sigsafe_err_fds(void);
+void tor_log_close_sigsafe_err_fds(void);
void tor_log_sigsafe_err_set_granularity(int ms);
+void tor_raw_abort_(void) ATTR_NORETURN;
+
int format_hex_number_sigsafe(unsigned long x, char *buf, int max_len);
int format_dec_number_sigsafe(unsigned long x, char *buf, int max_len);
diff --git a/src/lib/err/torerr_sys.c b/src/lib/err/torerr_sys.c
index 34f70f1f0b..eb818004fb 100644
--- a/src/lib/err/torerr_sys.c
+++ b/src/lib/err/torerr_sys.c
@@ -27,8 +27,11 @@ subsys_torerr_initialize(void)
static void
subsys_torerr_shutdown(void)
{
- tor_log_reset_sigsafe_err_fds();
+ /* Stop handling signals with backtraces, then close the logs. */
clean_up_backtrace_handler();
+ /* We can't log any log messages after this point: we've closed all the log
+ * fds, including stdio. */
+ tor_log_close_sigsafe_err_fds();
}
const subsys_fns_t sys_torerr = {
diff --git a/src/lib/evloop/compat_libevent.c b/src/lib/evloop/compat_libevent.c
index 91eacb9938..500c74831c 100644
--- a/src/lib/evloop/compat_libevent.c
+++ b/src/lib/evloop/compat_libevent.c
@@ -422,7 +422,7 @@ mainloop_event_activate(mainloop_event_t *event)
*
* If the event is scheduled for a different time, cancel it and run
* after this delay instead. If the event is currently pending to run
- * <em>now</b>, has no effect.
+ * <b>now</b>, has no effect.
*
* Do not call this function with <b>tv</b> == NULL -- use
* mainloop_event_activate() instead.
diff --git a/src/lib/evloop/procmon.c b/src/lib/evloop/procmon.c
index 52469fa5fc..b2d81fc14b 100644
--- a/src/lib/evloop/procmon.c
+++ b/src/lib/evloop/procmon.c
@@ -303,7 +303,7 @@ tor_process_monitor_poll_cb(periodic_timer_t *event, void *procmon_)
tor_free(errmsg);
}
}
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
/* Unix makes this part easy, if a bit racy. */
its_dead_jim = kill(procmon->pid, 0);
its_dead_jim = its_dead_jim && (errno == ESRCH);
diff --git a/src/lib/fs/conffile.c b/src/lib/fs/conffile.c
index 7bb2f23931..0d5d56b335 100644
--- a/src/lib/fs/conffile.c
+++ b/src/lib/fs/conffile.c
@@ -153,16 +153,18 @@ config_process_include(const char *path, int recursion_level, int extended,
int rv = -1;
SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) {
config_line_t *included_config = NULL;
+ config_line_t *included_config_last = NULL;
if (config_get_included_config(config_file, recursion_level, extended,
- &included_config, list_last,
+ &included_config, &included_config_last,
opened_lst) < 0) {
goto done;
}
*next = included_config;
- if (*list_last)
- next = &(*list_last)->next;
-
+ if (included_config_last) {
+ next = &included_config_last->next;
+ *list_last = included_config_last;
+ }
} SMARTLIST_FOREACH_END(config_file);
*list = ret_list;
rv = 0;
diff --git a/src/lib/fs/dir.c b/src/lib/fs/dir.c
index 3c31e00d99..390836b048 100644
--- a/src/lib/fs/dir.c
+++ b/src/lib/fs/dir.c
@@ -262,7 +262,7 @@ check_private_dir,(const char *dirname, cpd_check_t check,
}
}
close(fd);
-#else /* !(!defined(_WIN32)) */
+#else /* defined(_WIN32) */
/* Win32 case: we can't open() a directory. */
(void)effective_user;
@@ -347,7 +347,7 @@ tor_listdir, (const char *dirname))
}
FindClose(handle);
tor_free(pattern);
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
const char *prot_dname = sandbox_intern_string(dirname);
DIR *d;
struct dirent *de;
diff --git a/src/lib/fs/files.h b/src/lib/fs/files.h
index 81dba8c140..ed983f3b3c 100644
--- a/src/lib/fs/files.h
+++ b/src/lib/fs/files.h
@@ -123,7 +123,7 @@ ssize_t compat_getdelim_(char **lineptr, size_t *n, int delim, FILE *stream);
*/
#define tor_getdelim(lineptr, n, delim, stream) \
getdelim((lineptr), (n), (delim), (stream))
-#else /* !(defined(HAVE_GETDELIM)) */
+#else /* !defined(HAVE_GETDELIM) */
#define tor_getdelim(lineptr, n, delim, stream) \
compat_getdelim_((lineptr), (n), (delim), (stream))
#endif /* defined(HAVE_GETDELIM) */
@@ -137,7 +137,7 @@ ssize_t compat_getdelim_(char **lineptr, size_t *n, int delim, FILE *stream);
*/
#define tor_getline(lineptr, n, stream) \
getline((lineptr), (n), (stream))
-#else /* !(defined(HAVE_GETLINE)) */
+#else /* !defined(HAVE_GETLINE) */
#define tor_getline(lineptr, n, stream) \
tor_getdelim((lineptr), (n), '\n', (stream))
#endif /* defined(HAVE_GETLINE) */
diff --git a/src/lib/fs/mmap.c b/src/lib/fs/mmap.c
index f71c0cff7a..9d50a476bd 100644
--- a/src/lib/fs/mmap.c
+++ b/src/lib/fs/mmap.c
@@ -42,8 +42,8 @@
* failure, return NULL. Sets errno properly, using ERANGE to mean
* "empty file". Must only be called on trusted Tor-owned files, as changing
* the underlying file's size causes unspecified behavior. */
-tor_mmap_t *
-tor_mmap_file(const char *filename)
+MOCK_IMPL(tor_mmap_t *,
+tor_mmap_file,(const char *filename))
{
int fd; /* router file */
char *string;
@@ -111,8 +111,8 @@ tor_mmap_file(const char *filename)
}
/** Release storage held for a memory mapping; returns 0 on success,
* or -1 on failure (and logs a warning). */
-int
-tor_munmap_file(tor_mmap_t *handle)
+MOCK_IMPL(int,
+tor_munmap_file,(tor_mmap_t *handle))
{
int res;
@@ -132,8 +132,8 @@ tor_munmap_file(tor_mmap_t *handle)
return res;
}
#elif defined(_WIN32)
-tor_mmap_t *
-tor_mmap_file(const char *filename)
+MOCK_IMPL(tor_mmap_t *,
+tor_mmap_file,(const char *filename))
{
TCHAR tfilename[MAX_PATH]= {0};
tor_mmap_t *res = tor_malloc_zero(sizeof(tor_mmap_t));
@@ -213,8 +213,8 @@ tor_mmap_file(const char *filename)
}
/* Unmap the file, and return 0 for success or -1 for failure */
-int
-tor_munmap_file(tor_mmap_t *handle)
+MOCK_IMPL(int,
+tor_munmap_file,(tor_mmap_t *handle))
{
if (handle == NULL)
return 0;
diff --git a/src/lib/fs/mmap.h b/src/lib/fs/mmap.h
index 61aad544b2..beb0535109 100644
--- a/src/lib/fs/mmap.h
+++ b/src/lib/fs/mmap.h
@@ -13,6 +13,7 @@
#define TOR_MMAP_H
#include "lib/cc/compat_compiler.h"
+#include "lib/testsupport/testsupport.h"
#include <stddef.h>
#ifdef _WIN32
@@ -35,7 +36,7 @@ typedef struct tor_mmap_t {
} tor_mmap_t;
-tor_mmap_t *tor_mmap_file(const char *filename);
-int tor_munmap_file(tor_mmap_t *handle);
+MOCK_DECL(tor_mmap_t *, tor_mmap_file, (const char *filename));
+MOCK_DECL(int, tor_munmap_file, (tor_mmap_t *handle));
#endif /* !defined(TOR_MMAP_H) */
diff --git a/src/lib/fs/path.c b/src/lib/fs/path.c
index b3ef61979d..28dde62aea 100644
--- a/src/lib/fs/path.c
+++ b/src/lib/fs/path.c
@@ -72,7 +72,7 @@ expand_filename(const char *filename)
* Chapter+3.+Input+Validation/3.7+Validating+Filenames+and+Paths/
*/
return tor_strdup(filename);
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
if (*filename == '~') {
char *home, *result=NULL;
const char *rest;
@@ -102,7 +102,7 @@ expand_filename(const char *filename)
}
tor_free(username);
rest = slash ? (slash+1) : "";
-#else /* !(defined(HAVE_PWD_H)) */
+#else /* !defined(HAVE_PWD_H) */
log_warn(LD_CONFIG, "Couldn't expand homedir on system without pwd.h");
return tor_strdup(filename);
#endif /* defined(HAVE_PWD_H) */
@@ -153,7 +153,7 @@ clean_fname_for_stat(char *name)
return;
name[len-1]='\0';
}
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
(void)name;
#endif /* defined(_WIN32) */
}
@@ -233,7 +233,7 @@ alloc_getcwd(void)
raw_free(cwd); // alias for free to avoid tripping check-spaces.
}
return result;
-#else /* !(defined(HAVE_GET_CURRENT_DIR_NAME)) */
+#else /* !defined(HAVE_GET_CURRENT_DIR_NAME) */
size_t size = 1024;
char *buf = NULL;
char *ptr = NULL;
@@ -268,7 +268,7 @@ make_path_absolute(char *fname)
if (absfname_malloced) raw_free(absfname_malloced);
return absfname;
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
char *absfname = NULL, *path = NULL;
tor_assert(fname);
diff --git a/src/lib/lock/compat_mutex.c b/src/lib/lock/compat_mutex.c
index 4ad5929715..670bd0174c 100644
--- a/src/lib/lock/compat_mutex.c
+++ b/src/lib/lock/compat_mutex.c
@@ -29,7 +29,15 @@ tor_mutex_new_nonrecursive(void)
tor_mutex_init_nonrecursive(m);
return m;
}
-/** Release all storage and system resources held by <b>m</b>. */
+/** Release all storage and system resources held by <b>m</b>.
+ *
+ * Destroying a locked mutex is undefined behaviour. Global mutexes may be
+ * locked when they are passed to this function, because multiple threads can
+ * still access them. So we can either:
+ * - destroy on shutdown, and re-initialise when tor re-initialises, or
+ * - skip destroying and re-initialisation, using a sentinel variable.
+ * See #31735 for details.
+ */
void
tor_mutex_free_(tor_mutex_t *m)
{
diff --git a/src/lib/lock/compat_mutex_pthreads.c b/src/lib/lock/compat_mutex_pthreads.c
index ee5f520cd0..f82ad9f0e8 100644
--- a/src/lib/lock/compat_mutex_pthreads.c
+++ b/src/lib/lock/compat_mutex_pthreads.c
@@ -88,12 +88,26 @@ tor_mutex_release(tor_mutex_t *m)
}
/** Clean up the mutex <b>m</b> so that it no longer uses any system
* resources. Does not free <b>m</b>. This function must only be called on
- * mutexes from tor_mutex_init(). */
+ * mutexes from tor_mutex_init().
+ *
+ * Destroying a locked mutex is undefined behaviour. Global mutexes may be
+ * locked when they are passed to this function, because multiple threads can
+ * still access them. So we can either:
+ * - destroy on shutdown, and re-initialise when tor re-initialises, or
+ * - skip destroying and re-initialisation, using a sentinel variable.
+ * See #31735 for details.
+ */
void
tor_mutex_uninit(tor_mutex_t *m)
{
int err;
raw_assert(m);
+ /* If the mutex is already locked, wait until after it is unlocked to destroy
+ * it. Locking and releasing the mutex makes undefined behaviour less likely,
+ * but does not prevent it. Another thread can lock the mutex between release
+ * and destroy. */
+ tor_mutex_acquire(m);
+ tor_mutex_release(m);
err = pthread_mutex_destroy(&m->mutex);
if (PREDICT_UNLIKELY(err)) {
// LCOV_EXCL_START
diff --git a/src/lib/log/log.c b/src/lib/log/log.c
index 012e25193e..cb92ef07ef 100644
--- a/src/lib/log/log.c
+++ b/src/lib/log/log.c
@@ -55,10 +55,6 @@
#include <android/log.h>
#endif // HAVE_ANDROID_LOG_H.
-/** Given a severity, yields an index into log_severity_list_t.masks to use
- * for that severity. */
-#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR)
-
/** @{ */
/** The string we stick at the end of a log message when it is too long,
* and its length. */
@@ -225,6 +221,7 @@ int log_global_min_severity_ = LOG_NOTICE;
static void delete_log(logfile_t *victim);
static void close_log(logfile_t *victim);
+static void close_log_sigsafe(logfile_t *victim);
static char *domain_to_string(log_domain_mask_t domain,
char *buf, size_t buflen);
@@ -279,8 +276,8 @@ static int log_time_granularity = 1;
/** Define log time granularity for all logs to be <b>granularity_msec</b>
* milliseconds. */
-void
-set_log_time_granularity(int granularity_msec)
+MOCK_IMPL(void,
+set_log_time_granularity,(int granularity_msec))
{
log_time_granularity = granularity_msec;
tor_log_sigsafe_err_set_granularity(granularity_msec);
@@ -535,7 +532,7 @@ logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len,
if (m != msg_after_prefix) {
tor_free(m);
}
-#else /* !(defined(MAXLINE)) */
+#else /* !defined(MAXLINE) */
/* We have syslog but not MAXLINE. That's promising! */
syslog(severity, "%s", msg_after_prefix);
#endif /* defined(MAXLINE) */
@@ -587,8 +584,7 @@ logv,(int severity, log_domain_mask_t domain, const char *funcname,
/* check that severity is sane. Overrunning the masks array leads to
* interesting and hard to diagnose effects */
raw_assert(severity >= LOG_ERR && severity <= LOG_DEBUG);
- /* check that we've initialised the log mutex before we try to lock it */
- raw_assert(log_mutex_initialized);
+
LOCK_LOGS();
if ((! (domain & LD_NOCB)) && pending_cb_messages
@@ -632,6 +628,10 @@ void
tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
{
va_list ap;
+
+ /* check that domain is composed of known domains and flags */
+ raw_assert((domain & (LD_ALL_DOMAINS|LD_ALL_FLAGS)) == domain);
+
if (severity > log_global_min_severity_)
return;
va_start(ap,format);
@@ -665,18 +665,30 @@ tor_log_update_sigsafe_err_fds(void)
const logfile_t *lf;
int found_real_stderr = 0;
- int fds[TOR_SIGSAFE_LOG_MAX_FDS];
+ /* log_fds and err_fds contain matching entries: log_fds are the fds used by
+ * the log module, and err_fds are the fds used by the err module.
+ * For stdio logs, the log_fd and err_fd values are identical,
+ * and the err module closes the fd on shutdown.
+ * For file logs, the err_fd is a dup() of the log_fd,
+ * and the log and err modules both close their respective fds on shutdown.
+ * (Once all fds representing a file are closed, the underlying file is
+ * closed.)
+ */
+ int log_fds[TOR_SIGSAFE_LOG_MAX_FDS];
+ int err_fds[TOR_SIGSAFE_LOG_MAX_FDS];
int n_fds;
LOCK_LOGS();
/* Reserve the first one for stderr. This is safe because when we daemonize,
- * we dup2 /dev/null to stderr, */
- fds[0] = STDERR_FILENO;
+ * we dup2 /dev/null to stderr.
+ * For stderr, log_fds and err_fds are the same. */
+ log_fds[0] = err_fds[0] = STDERR_FILENO;
n_fds = 1;
for (lf = logfiles; lf; lf = lf->next) {
- /* Don't try callback to the control port, or syslogs: We can't
- * do them from a signal handler. Don't try stdout: we always do stderr.
+ /* Don't try callback to the control port, syslogs, android logs, or any
+ * other non-file descriptor log: We can't call arbitrary functions from a
+ * signal handler.
*/
if (lf->is_temporary || logfile_is_external(lf)
|| lf->seems_dead || lf->fd < 0)
@@ -685,25 +697,42 @@ tor_log_update_sigsafe_err_fds(void)
(LD_BUG|LD_GENERAL)) {
if (lf->fd == STDERR_FILENO)
found_real_stderr = 1;
- /* Avoid duplicates */
- if (int_array_contains(fds, n_fds, lf->fd))
+ /* Avoid duplicates by checking the log module fd against log_fds */
+ if (int_array_contains(log_fds, n_fds, lf->fd))
continue;
- fds[n_fds++] = lf->fd;
+ /* Update log_fds using the log module's fd */
+ log_fds[n_fds] = lf->fd;
+ if (lf->needs_close) {
+ /* File log fds are duplicated, because close_log() closes the log
+ * module's fd, and tor_log_close_sigsafe_err_fds() closes the err
+ * module's fd. Both refer to the same file. */
+ err_fds[n_fds] = dup(lf->fd);
+ } else {
+ /* stdio log fds are not closed by the log module.
+ * tor_log_close_sigsafe_err_fds() closes stdio logs. */
+ err_fds[n_fds] = lf->fd;
+ }
+ n_fds++;
if (n_fds == TOR_SIGSAFE_LOG_MAX_FDS)
break;
}
}
if (!found_real_stderr &&
- int_array_contains(fds, n_fds, STDOUT_FILENO)) {
- /* Don't use a virtual stderr when we're also logging to stdout. */
+ int_array_contains(log_fds, n_fds, STDOUT_FILENO)) {
+ /* Don't use a virtual stderr when we're also logging to stdout.
+ * If we reached max_fds logs, we'll now have (max_fds - 1) logs.
+ * That's ok, max_fds is large enough that most tor instances don't exceed
+ * it. */
raw_assert(n_fds >= 2); /* Don't tor_assert inside log fns */
- fds[0] = fds[--n_fds];
+ --n_fds;
+ log_fds[0] = log_fds[n_fds];
+ err_fds[0] = err_fds[n_fds];
}
UNLOCK_LOGS();
- tor_log_set_sigsafe_err_fds(fds, n_fds);
+ tor_log_set_sigsafe_err_fds(err_fds, n_fds);
}
/** Add to <b>out</b> a copy of every currently configured log file name. Used
@@ -729,7 +758,7 @@ tor_log_get_logfile_names(smartlist_t *out)
/** Implementation of the log_fn backend, used when we have
* variadic macros. All arguments are as for log_fn, except for
- * <b>fn</b>, which is the name of the calling functions. */
+ * <b>fn</b>, which is the name of the calling function. */
void
log_fn_(int severity, log_domain_mask_t domain, const char *fn,
const char *format, ...)
@@ -806,16 +835,40 @@ logs_free_all(void)
}
/* We _could_ destroy the log mutex here, but that would screw up any logs
- * that happened between here and the end of execution. */
+ * that happened between here and the end of execution.
+ * If tor is re-initialized, log_mutex_initialized will still be 1. So we
+ * won't trigger any undefined behaviour by trying to re-initialize the
+ * log mutex. */
+}
+
+/** Close signal-safe log files.
+ * Closing the log files makes the process and OS flush log buffers.
+ *
+ * This function is safe to call from a signal handler. It should only be
+ * called when shutting down the log or err modules. It is currenly called
+ * by the err module, when terminating the process on an abnormal condition.
+ */
+void
+logs_close_sigsafe(void)
+{
+ logfile_t *victim, *next;
+ /* We can't LOCK_LOGS() in a signal handler, because it may call
+ * signal-unsafe functions. And we can't deallocate memory, either. */
+ next = logfiles;
+ logfiles = NULL;
+ while (next) {
+ victim = next;
+ next = next->next;
+ if (victim->needs_close) {
+ close_log_sigsafe(victim);
+ }
+ }
}
/** Remove and free the log entry <b>victim</b> from the linked-list
* logfiles (it is probably present, but it might not be due to thread
* racing issues). After this function is called, the caller shouldn't
* refer to <b>victim</b> anymore.
- *
- * Long-term, we need to do something about races in the log subsystem
- * in general. See bug 222 for more details.
*/
static void
delete_log(logfile_t *victim)
@@ -835,13 +888,26 @@ delete_log(logfile_t *victim)
}
/** Helper: release system resources (but not memory) held by a single
- * logfile_t. */
+ * signal-safe logfile_t. If the log's resources can not be released in
+ * a signal handler, does nothing. */
static void
-close_log(logfile_t *victim)
+close_log_sigsafe(logfile_t *victim)
{
if (victim->needs_close && victim->fd >= 0) {
+ /* We can't do anything useful here if close() fails: we're shutting
+ * down logging, and the err module only does fatal errors. */
close(victim->fd);
victim->fd = -1;
+ }
+}
+
+/** Helper: release system resources (but not memory) held by a single
+ * logfile_t. */
+static void
+close_log(logfile_t *victim)
+{
+ if (victim->needs_close) {
+ close_log_sigsafe(victim);
} else if (victim->is_syslog) {
#ifdef HAVE_SYSLOG_H
if (--syslog_count == 0) {
@@ -865,15 +931,15 @@ set_log_severity_config(int loglevelMin, int loglevelMax,
raw_assert(loglevelMax >= LOG_ERR && loglevelMax <= LOG_DEBUG);
memset(severity_out, 0, sizeof(log_severity_list_t));
for (i = loglevelMin; i >= loglevelMax; --i) {
- severity_out->masks[SEVERITY_MASK_IDX(i)] = ~0u;
+ severity_out->masks[SEVERITY_MASK_IDX(i)] = LD_ALL_DOMAINS;
}
}
/** Add a log handler named <b>name</b> to send all messages in <b>severity</b>
* to <b>fd</b>. Copies <b>severity</b>. Helper: does no locking. */
-static void
-add_stream_log_impl(const log_severity_list_t *severity,
- const char *name, int fd)
+MOCK_IMPL(STATIC void,
+add_stream_log_impl,(const log_severity_list_t *severity,
+ const char *name, int fd))
{
logfile_t *lf;
lf = tor_malloc_zero(sizeof(logfile_t));
@@ -929,18 +995,16 @@ logs_set_domain_logging(int enabled)
UNLOCK_LOGS();
}
-/** Add a log handler to receive messages during startup (before the real
- * logs are initialized).
+/** Add a log handler to accept messages when no other log is configured.
*/
void
-add_temp_log(int min_severity)
+add_default_log(int min_severity)
{
log_severity_list_t *s = tor_malloc_zero(sizeof(log_severity_list_t));
set_log_severity_config(min_severity, LOG_ERR, s);
LOCK_LOGS();
- add_stream_log_impl(s, "<temp>", fileno(stdout));
+ add_stream_log_impl(s, "<default>", fileno(stdout));
tor_free(s);
- logfiles->is_temporary = 1;
UNLOCK_LOGS();
}
@@ -1083,8 +1147,7 @@ flush_log_messages_from_startup(void)
UNLOCK_LOGS();
}
-/** Close any log handlers added by add_temp_log() or marked by
- * mark_logs_temp(). */
+/** Close any log handlers marked by mark_logs_temp(). */
void
close_temp_logs(void)
{
@@ -1136,10 +1199,10 @@ mark_logs_temp(void)
* opening the logfile failed, -1 is returned and errno is set appropriately
* (by open(2)). Takes ownership of fd.
*/
-int
-add_file_log(const log_severity_list_t *severity,
- const char *filename,
- int fd)
+MOCK_IMPL(int,
+add_file_log,(const log_severity_list_t *severity,
+ const char *filename,
+ int fd))
{
logfile_t *lf;
@@ -1275,7 +1338,9 @@ static const char *domain_list[] = {
CTASSERT(ARRAY_LENGTH(domain_list) == N_LOGGING_DOMAINS + 1);
-CTASSERT((UINT64_C(1)<<(N_LOGGING_DOMAINS-1)) < LOWEST_RESERVED_LD_FLAG_);
+CTASSERT(HIGHEST_RESERVED_LD_DOMAIN_ < LD_ALL_DOMAINS);
+CTASSERT(LD_ALL_DOMAINS < LOWEST_RESERVED_LD_FLAG_);
+CTASSERT(LOWEST_RESERVED_LD_FLAG_ < LD_ALL_FLAGS);
/** Return a bitmask for the log domain for which <b>domain</b> is the name,
* or 0 if there is no such name. */
@@ -1357,7 +1422,7 @@ parse_log_severity_config(const char **cfg_ptr,
const char *dash, *space;
char *sev_lo, *sev_hi;
int low, high, i;
- log_domain_mask_t domains = ~0u;
+ log_domain_mask_t domains = LD_ALL_DOMAINS;
if (*cfg == '[') {
int err = 0;
@@ -1375,7 +1440,7 @@ parse_log_severity_config(const char **cfg_ptr,
tor_free(domains_str);
SMARTLIST_FOREACH_BEGIN(domains_list, const char *, domain) {
if (!strcmp(domain, "*")) {
- domains = ~0u;
+ domains = LD_ALL_DOMAINS;
} else {
log_domain_mask_t d;
int negate=0;
@@ -1471,7 +1536,7 @@ switch_logs_debug(void)
LOCK_LOGS();
for (lf = logfiles; lf; lf=lf->next) {
for (i = LOG_DEBUG; i >= LOG_ERR; --i)
- lf->severities->masks[SEVERITY_MASK_IDX(i)] = ~0u;
+ lf->severities->masks[SEVERITY_MASK_IDX(i)] = LD_ALL_DOMAINS;
}
log_global_min_severity_ = get_min_log_level();
UNLOCK_LOGS();
diff --git a/src/lib/log/log.h b/src/lib/log/log.h
index c2b20fc228..19cb9484d7 100644
--- a/src/lib/log/log.h
+++ b/src/lib/log/log.h
@@ -119,10 +119,21 @@
#define LD_BTRACK (UINT64_C(1)<<28)
/** Message-passing backend. */
#define LD_MESG (UINT64_C(1)<<29)
+
+/** The number of log domains. */
#define N_LOGGING_DOMAINS 30
+/** The highest log domain */
+#define HIGHEST_RESERVED_LD_DOMAIN_ (UINT64_C(1)<<(N_LOGGING_DOMAINS - 1))
+/** All log domains. */
+#define LD_ALL_DOMAINS ((~(UINT64_C(0)))>>(64 - N_LOGGING_DOMAINS))
+/** The number of log flags. */
+#define N_LOGGING_FLAGS 3
/** First bit that is reserved in log_domain_mask_t for non-domain flags. */
-#define LOWEST_RESERVED_LD_FLAG_ (UINT64_C(1)<<61)
+#define LOWEST_RESERVED_LD_FLAG_ (UINT64_C(1)<<(64 - N_LOGGING_FLAGS))
+/** All log flags. */
+#define LD_ALL_FLAGS ((~(UINT64_C(0)))<<(64 - N_LOGGING_FLAGS))
+
#ifdef TOR_UNIT_TESTS
/** This log message should not be intercepted by mock_saving_logv */
#define LD_NO_MOCK (UINT64_C(1)<<61)
@@ -154,11 +165,11 @@ int parse_log_severity_config(const char **cfg,
log_severity_list_t *severity_out);
void set_log_severity_config(int minSeverity, int maxSeverity,
log_severity_list_t *severity_out);
-void add_stream_log(const log_severity_list_t *severity, const char *name,
- int fd);
-int add_file_log(const log_severity_list_t *severity,
- const char *filename,
- int fd);
+void add_stream_log(const log_severity_list_t *severity,
+ const char *name, int fd);
+MOCK_DECL(int, add_file_log,(const log_severity_list_t *severity,
+ const char *filename,
+ int fd));
#ifdef HAVE_SYSLOG_H
int add_syslog_log(const log_severity_list_t *severity,
@@ -175,7 +186,8 @@ void logs_set_domain_logging(int enabled);
int get_min_log_level(void);
void switch_logs_debug(void);
void logs_free_all(void);
-void add_temp_log(int min_severity);
+void logs_close_sigsafe(void);
+void add_default_log(int min_severity);
void close_temp_logs(void);
void rollback_log_changes(void);
void mark_logs_temp(void);
@@ -184,7 +196,7 @@ void change_callback_log_severity(int loglevelMin, int loglevelMax,
void flush_pending_log_callbacks(void);
void flush_log_messages_from_startup(void);
void log_set_application_name(const char *name);
-void set_log_time_granularity(int granularity_msec);
+MOCK_DECL(void, set_log_time_granularity,(int granularity_msec));
void truncate_logs(void);
void tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
@@ -296,6 +308,14 @@ extern const log_domain_mask_t LD_GENERAL_;
MOCK_DECL(STATIC void, logv, (int severity, log_domain_mask_t domain,
const char *funcname, const char *suffix, const char *format,
va_list ap) CHECK_PRINTF(5,0));
+MOCK_DECL(STATIC void, add_stream_log_impl,(
+ const log_severity_list_t *severity, const char *name, int fd));
+#endif
+
+#if defined(LOG_PRIVATE) || defined(TOR_UNIT_TESTS)
+/** Given a severity, yields an index into log_severity_list_t.masks to use
+ * for that severity. */
+#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR)
#endif
#endif /* !defined(TOR_TORLOG_H) */
diff --git a/src/lib/log/util_bug.c b/src/lib/log/util_bug.c
index 76b97c1a08..72c614a3b2 100644
--- a/src/lib/log/util_bug.c
+++ b/src/lib/log/util_bug.c
@@ -11,6 +11,7 @@
#include "lib/log/util_bug.h"
#include "lib/log/log.h"
#include "lib/err/backtrace.h"
+#include "lib/err/torerr.h"
#ifdef TOR_UNIT_TESTS
#include "lib/smartlist_core/smartlist_core.h"
#include "lib/smartlist_core/smartlist_foreach.h"
@@ -63,7 +64,7 @@ tor_set_failed_assertion_callback(void (*fn)(void))
{
failed_assertion_cb = fn;
}
-#else /* !(defined(TOR_UNIT_TESTS)) */
+#else /* !defined(TOR_UNIT_TESTS) */
#define capturing_bugs() (0)
#define add_captured_bug(s) do { } while (0)
#endif /* defined(TOR_UNIT_TESTS) */
@@ -161,16 +162,18 @@ tor_bug_occurred_(const char *fname, unsigned int line,
}
/**
- * Call the abort() function to kill the current process with a fatal
- * error.
+ * Call the tor_raw_abort_() function to close raw logs, then kill the current
+ * process with a fatal error. But first, close the file-based log file
+ * descriptors, so error messages are written before process termination.
*
* (This is a separate function so that we declare it in util_bug.h without
- * including stdlib in all the users of util_bug.h)
+ * including torerr.h in all the users of util_bug.h)
**/
void
tor_abort_(void)
{
- abort();
+ logs_close_sigsafe();
+ tor_raw_abort_();
}
#ifdef _WIN32
diff --git a/src/lib/log/util_bug.h b/src/lib/log/util_bug.h
index 8c233da735..dcff5fd9c3 100644
--- a/src/lib/log/util_bug.h
+++ b/src/lib/log/util_bug.h
@@ -96,7 +96,7 @@
(void)(a); \
(void)(fmt); \
STMT_END
-#else /* !(defined(TOR_UNIT_TESTS) && ... */
+#else /* !(defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_T...)) */
/** Like assert(3), but send assertion failures to the log as well as to
* stderr. */
#define tor_assert(expr) tor_assertf(expr, NULL)
@@ -214,7 +214,7 @@
("!("#cond")"), 1, NULL); \
} \
bool_result; } ))
-#else /* !(defined(__GNUC__)) */
+#else /* !defined(__GNUC__) */
#define IF_BUG_ONCE__(cond,var) \
static int var = 0; \
if ((cond) ? \
diff --git a/src/lib/log/win32err.c b/src/lib/log/win32err.c
index dc45cb4c3d..03d5c9fad2 100644
--- a/src/lib/log/win32err.c
+++ b/src/lib/log/win32err.c
@@ -47,7 +47,7 @@ format_win32_error(DWORD err)
result = tor_malloc(len);
wcstombs(result,str,len);
result[len-1] = '\0';
-#else /* !(defined(UNICODE)) */
+#else /* !defined(UNICODE) */
result = tor_strdup(str);
#endif /* defined(UNICODE) */
} else {
diff --git a/src/lib/malloc/malloc.h b/src/lib/malloc/malloc.h
index 8c81d30dd5..39a45901a1 100644
--- a/src/lib/malloc/malloc.h
+++ b/src/lib/malloc/malloc.h
@@ -48,7 +48,7 @@ void tor_free_(void *mem);
raw_free(*tor_free__tmpvar); \
*tor_free__tmpvar=NULL; \
STMT_END
-#else /* !(defined(__GNUC__)) */
+#else /* !defined(__GNUC__) */
#define tor_free(p) STMT_BEGIN \
raw_free(p); \
(p)=NULL; \
diff --git a/src/lib/malloc/map_anon.c b/src/lib/malloc/map_anon.c
index 4e38cb642c..0e78521bd8 100644
--- a/src/lib/malloc/map_anon.c
+++ b/src/lib/malloc/map_anon.c
@@ -27,6 +27,9 @@
#include <windows.h>
#endif
+#include <string.h>
+#include <errno.h>
+
/**
* Macro to get the high bytes of a size_t, if there are high bytes.
* Windows needs this; other operating systems define a size_t that does
@@ -108,8 +111,18 @@ static int
nodump_mem(void *mem, size_t sz)
{
#if defined(MADV_DONTDUMP)
- return madvise(mem, sz, MADV_DONTDUMP);
-#else
+ int rv = madvise(mem, sz, MADV_DONTDUMP);
+ if (rv == 0) {
+ return 0;
+ } else if (errno == ENOSYS || errno == EINVAL) {
+ return 0; // syscall not supported, or flag not supported.
+ } else {
+ tor_log_err_sigsafe("Unexpected error from madvise: ",
+ strerror(errno),
+ NULL);
+ return -1;
+ }
+#else /* !defined(MADV_DONTDUMP) */
(void) mem;
(void) sz;
return 0;
@@ -136,18 +149,33 @@ noinherit_mem(void *mem, size_t sz, inherit_res_t *inherit_result_out)
return 0;
}
#endif /* defined(FLAG_ZERO) */
+
#ifdef FLAG_NOINHERIT
int r2 = MINHERIT(mem, sz, FLAG_NOINHERIT);
if (r2 == 0) {
*inherit_result_out = INHERIT_RES_DROP;
+ return 0;
}
- return r2;
-#else /* !(defined(FLAG_NOINHERIT)) */
+#endif /* defined(FLAG_NOINHERIT) */
+
+#if defined(FLAG_ZERO) || defined(FLAG_NOINHERIT)
+ /* At least one operation was tried, and neither succeeded. */
+
+ if (errno == ENOSYS || errno == EINVAL) {
+ /* Syscall not supported, or flag not supported. */
+ return 0;
+ } else {
+ tor_log_err_sigsafe("Unexpected error from minherit: ",
+ strerror(errno),
+ NULL);
+ return -1;
+ }
+#else /* !(defined(FLAG_ZERO) || defined(FLAG_NOINHERIT)) */
(void)inherit_result_out;
(void)mem;
(void)sz;
return 0;
-#endif /* defined(FLAG_NOINHERIT) */
+#endif /* defined(FLAG_ZERO) || defined(FLAG_NOINHERIT) */
}
/**
diff --git a/src/lib/math/fp.c b/src/lib/math/fp.c
index 52c57c1d7f..49a2a6a2ca 100644
--- a/src/lib/math/fp.c
+++ b/src/lib/math/fp.c
@@ -62,16 +62,20 @@ clamp_double_to_int64(double number)
{
int exponent;
-#if defined(MINGW_ANY) && GCC_VERSION >= 409
+#if (defined(MINGW_ANY)||defined(__FreeBSD__)) && GCC_VERSION >= 409
/*
Mingw's math.h uses gcc's __builtin_choose_expr() facility to declare
isnan, isfinite, and signbit. But as implemented in at least some
versions of gcc, __builtin_choose_expr() can generate type warnings
even from branches that are not taken. So, suppress those warnings.
+
+ FreeBSD's math.h uses an __fp_type_select() macro, which dispatches
+ based on sizeof -- again, this can generate type warnings from
+ branches that are not taken.
*/
#define PROBLEMATIC_FLOAT_CONVERSION_WARNING
DISABLE_GCC_WARNING(float-conversion)
-#endif /* defined(MINGW_ANY) && GCC_VERSION >= 409 */
+#endif /* (defined(MINGW_ANY)||defined(__FreeBSD__)) && GCC_VERSION >= 409 */
/*
With clang 4.0 we apparently run into "double promotion" warnings here,
@@ -123,16 +127,12 @@ int
tor_isinf(double x)
{
/* Same as above, work around the "double promotion" warnings */
-#if defined(MINGW_ANY) && GCC_VERSION >= 409
-#define PROBLEMATIC_FLOAT_CONVERSION_WARNING
+#ifdef PROBLEMATIC_FLOAT_CONVERSION_WARNING
DISABLE_GCC_WARNING(float-conversion)
-#endif /* defined(MINGW_ANY) && GCC_VERSION >= 409 */
-#if defined(__clang__)
-#if __has_warning("-Wdouble-promotion")
-#define PROBLEMATIC_DOUBLE_PROMOTION_WARNING
+#endif
+#ifdef PROBLEMATIC_DOUBLE_PROMOTION_WARNING
DISABLE_GCC_WARNING(double-promotion)
#endif
-#endif /* defined(__clang__) */
return isinf(x);
#ifdef PROBLEMATIC_DOUBLE_PROMOTION_WARNING
ENABLE_GCC_WARNING(double-promotion)
@@ -141,4 +141,3 @@ ENABLE_GCC_WARNING(double-promotion)
ENABLE_GCC_WARNING(float-conversion)
#endif
}
-
diff --git a/src/lib/math/prob_distr.c b/src/lib/math/prob_distr.c
index d44dc28265..f9d65073ff 100644
--- a/src/lib/math/prob_distr.c
+++ b/src/lib/math/prob_distr.c
@@ -178,8 +178,8 @@ clz32(uint32_t x)
/**
* Compute the logistic function: f(x) = 1/(1 + e^{-x}) = e^x/(1 + e^x).
- * Maps a log-odds-space probability in [-\infty, +\infty] into a direct-space
- * probability in [0,1]. Inverse of logit.
+ * Maps a log-odds-space probability in [-infinity, +infinity] into a
+ * direct-space probability in [0,1]. Inverse of logit.
*
* Ill-conditioned for large x; the identity logistic(-x) = 1 -
* logistic(x) and the function logistichalf(x) = logistic(x) - 1/2 may
@@ -266,7 +266,7 @@ logistic(double x)
/**
* Compute the logit function: log p/(1 - p). Defined on [0,1]. Maps
* a direct-space probability in [0,1] to a log-odds-space probability
- * in [-\infty, +\infty]. Inverse of logistic.
+ * in [-infinity, +infinity]. Inverse of logistic.
*
* Ill-conditioned near 1/2 and 1; the identity logit(1 - p) =
* -logit(p) and the function logithalf(p0) = logit(1/2 + p0) may help
@@ -488,7 +488,7 @@ random_uniform_01(void)
/* Functions for specific probability distributions start here: */
/*
- * Logistic(mu, sigma) distribution, supported on (-\infty,+\infty)
+ * Logistic(mu, sigma) distribution, supported on (-infinity,+infinity)
*
* This is the uniform distribution on [0,1] mapped into log-odds
* space, scaled by sigma and translated by mu.
@@ -546,7 +546,7 @@ isf_logistic(double p, double mu, double sigma)
}
/*
- * LogLogistic(alpha, beta) distribution, supported on (0, +\infty).
+ * LogLogistic(alpha, beta) distribution, supported on (0, +infinity).
*
* This is the uniform distribution on [0,1] mapped into odds space,
* scaled by positive alpha and shaped by positive beta.
@@ -687,7 +687,7 @@ isf_log_logistic(double p, double alpha, double beta)
}
/*
- * Weibull(lambda, k) distribution, supported on (0, +\infty).
+ * Weibull(lambda, k) distribution, supported on (0, +infinity).
*
* pdf(x) = (k/lambda) (x/lambda)^{k - 1} e^{-(x/lambda)^k}
* cdf(x) = 1 - e^{-(x/lambda)^k}
@@ -753,7 +753,7 @@ isf_weibull(double p, double lambda, double k)
}
/*
- * GeneralizedPareto(mu, sigma, xi), supported on (mu, +\infty) for
+ * GeneralizedPareto(mu, sigma, xi), supported on (mu, +infinity) for
* nonnegative xi, or (mu, mu - sigma/xi) for negative xi.
*
* Samples:
@@ -793,19 +793,19 @@ cdf_genpareto(double x, double mu, double sigma, double xi)
/*
* log(1 + xi x_0)/xi
- * = (-1/xi) \sum_{n=1}^\infty (-xi x_0)^n/n
- * = (-1/xi) (-xi x_0 + \sum_{n=2}^\infty (-xi x_0)^n/n)
- * = x_0 - (1/xi) \sum_{n=2}^\infty (-xi x_0)^n/n
- * = x_0 - x_0 \sum_{n=2}^\infty (-xi x_0)^{n-1}/n
+ * = (-1/xi) \sum_{n=1}^infinity (-xi x_0)^n/n
+ * = (-1/xi) (-xi x_0 + \sum_{n=2}^infinity (-xi x_0)^n/n)
+ * = x_0 - (1/xi) \sum_{n=2}^infinity (-xi x_0)^n/n
+ * = x_0 - x_0 \sum_{n=2}^infinity (-xi x_0)^{n-1}/n
* = x_0 (1 - d),
*
- * where d = \sum_{n=2}^\infty (-xi x_0)^{n-1}/n. If |xi| <
+ * where d = \sum_{n=2}^infinity (-xi x_0)^{n-1}/n. If |xi| <
* eps/4|x_0|, then
*
- * |d| <= \sum_{n=2}^\infty (eps/4)^{n-1}/n
- * <= \sum_{n=2}^\infty (eps/4)^{n-1}
- * = \sum_{n=1}^\infty (eps/4)^n
- * = (eps/4) \sum_{n=0}^\infty (eps/4)^n
+ * |d| <= \sum_{n=2}^infinity (eps/4)^{n-1}/n
+ * <= \sum_{n=2}^infinity (eps/4)^{n-1}
+ * = \sum_{n=1}^infinity (eps/4)^n
+ * = (eps/4) \sum_{n=0}^infinity (eps/4)^n
* = (eps/4)/(1 - eps/4)
* < eps/2
*
@@ -855,20 +855,20 @@ icdf_genpareto(double p, double mu, double sigma, double xi)
* for xi near zero (note f(xi) --> -log U as xi --> 0), write
* the absolutely convergent Taylor expansion
*
- * f(xi) = (1/xi)*(-xi log U + \sum_{n=2}^\infty (-xi log U)^n/n!
- * = -log U + (1/xi)*\sum_{n=2}^\infty (-xi log U)^n/n!
- * = -log U + \sum_{n=2}^\infty xi^{n-1} (-log U)^n/n!
- * = -log U - log U \sum_{n=2}^\infty (-xi log U)^{n-1}/n!
- * = -log U (1 + \sum_{n=2}^\infty (-xi log U)^{n-1}/n!).
+ * f(xi) = (1/xi)*(-xi log U + \sum_{n=2}^infinity (-xi log U)^n/n!
+ * = -log U + (1/xi)*\sum_{n=2}^infinity (-xi log U)^n/n!
+ * = -log U + \sum_{n=2}^infinity xi^{n-1} (-log U)^n/n!
+ * = -log U - log U \sum_{n=2}^infinity (-xi log U)^{n-1}/n!
+ * = -log U (1 + \sum_{n=2}^infinity (-xi log U)^{n-1}/n!).
*
- * Let d = \sum_{n=2}^\infty (-xi log U)^{n-1}/n!. What do we
+ * Let d = \sum_{n=2}^infinity (-xi log U)^{n-1}/n!. What do we
* lose if we discard it and use -log U as an approximation to
* f(xi)? If |xi| < eps/-4log U, then
*
- * |d| <= \sum_{n=2}^\infty |xi log U|^{n-1}/n!
- * <= \sum_{n=2}^\infty (eps/4)^{n-1}/n!
- * <= \sum_{n=1}^\infty (eps/4)^n
- * = (eps/4) \sum_{n=0}^\infty (eps/4)^n
+ * |d| <= \sum_{n=2}^infinity |xi log U|^{n-1}/n!
+ * <= \sum_{n=2}^infinity (eps/4)^{n-1}/n!
+ * <= \sum_{n=1}^infinity (eps/4)^n
+ * = (eps/4) \sum_{n=0}^infinity (eps/4)^n
* = (eps/4)/(1 - eps/4)
* < eps/2,
*
@@ -1098,10 +1098,10 @@ sample_logistic(uint32_t s, double t, double p0)
* We carve up the interval (0, 1) into subregions to compute
* the inverse CDF precisely:
*
- * A = (0, 1/(1 + e)] ---> (-\infty, -1]
+ * A = (0, 1/(1 + e)] ---> (-infinity, -1]
* B = [1/(1 + e), 1/2] ---> [-1, 0]
* C = [1/2, 1 - 1/(1 + e)] ---> [0, 1]
- * D = [1 - 1/(1 + e), 1) ---> [1, +\infty)
+ * D = [1 - 1/(1 + e), 1) ---> [1, +infinity)
*
* Cases D and C are mirror images of cases A and B,
* respectively, so we choose between them by the sign chosen
@@ -1234,19 +1234,19 @@ sample_genpareto(uint32_t s, double p0, double xi)
* Write f(xi) = (e^{xi x} - 1)/xi for xi near zero as the
* absolutely convergent Taylor series
*
- * f(x) = (1/xi) (xi x + \sum_{n=2}^\infty (xi x)^n/n!)
- * = x + (1/xi) \sum_{n=2}^\inty (xi x)^n/n!
- * = x + \sum_{n=2}^\infty xi^{n-1} x^n/n!
- * = x + x \sum_{n=2}^\infty (xi x)^{n-1}/n!
- * = x (1 + \sum_{n=2}^\infty (xi x)^{n-1}/n!).
+ * f(x) = (1/xi) (xi x + \sum_{n=2}^infinity (xi x)^n/n!)
+ * = x + (1/xi) \sum_{n=2}^infinity (xi x)^n/n!
+ * = x + \sum_{n=2}^infinity xi^{n-1} x^n/n!
+ * = x + x \sum_{n=2}^infinity (xi x)^{n-1}/n!
+ * = x (1 + \sum_{n=2}^infinity (xi x)^{n-1}/n!).
*
- * d = \sum_{n=2}^\infty (xi x)^{n-1}/n! is the relative error
+ * d = \sum_{n=2}^infinity (xi x)^{n-1}/n! is the relative error
* of f(x) from x. If |xi| < eps/4x, then
*
- * |d| <= \sum_{n=2}^\infty |xi x|^{n-1}/n!
- * <= \sum_{n=2}^\infty (eps/4)^{n-1}/n!
- * <= \sum_{n=1}^\infty (eps/4)
- * = (eps/4) \sum_{n=0}^\infty (eps/4)^n
+ * |d| <= \sum_{n=2}^infinity |xi x|^{n-1}/n!
+ * <= \sum_{n=2}^infinity (eps/4)^{n-1}/n!
+ * <= \sum_{n=1}^infinity (eps/4)
+ * = (eps/4) \sum_{n=0}^infinity (eps/4)^n
* = (eps/4)/(1 - eps/4)
* < eps/2,
*
diff --git a/src/lib/math/prob_distr.h b/src/lib/math/prob_distr.h
index 7254dc8623..a93d888950 100644
--- a/src/lib/math/prob_distr.h
+++ b/src/lib/math/prob_distr.h
@@ -66,41 +66,41 @@ struct dist {
* type-specific macro built out of it -- but if you did use this
* directly, it would be something like:
*
-* struct weibull mydist = {
-* DIST_BASE_TYPED(&weibull_ops, mydist, struct weibull),
-* .lambda = ...,
-* .k = ...,
-* };
+* struct weibull mydist = {
+* DIST_BASE_TYPED(&weibull_ops, mydist, struct weibull),
+* .lambda = ...,
+* .k = ...,
+* };
*
* If you want to define a distribution type, define a canonical set of
* operations and define a type-specific initializer element like so:
*
-* struct foo {
-* struct dist base;
-* int omega;
-* double tau;
-* double phi;
-* };
+* struct foo {
+* struct dist base;
+* int omega;
+* double tau;
+* double phi;
+* };
*
-* struct dist_ops foo_ops = ...;
+* struct dist_ops foo_ops = ...;
*
-* #define FOO(OBJ) DIST_BASE_TYPED(&foo_ops, OBJ, struct foo)
+* #define FOO(OBJ) DIST_BASE_TYPED(&foo_ops, OBJ, struct foo)
*
* Then users can do:
*
-* struct foo mydist = {
-* FOO(mydist),
-* .omega = ...,
-* .tau = ...,
-* .phi = ...,
-* };
+* struct foo mydist = {
+* FOO(mydist),
+* .omega = ...,
+* .tau = ...,
+* .phi = ...,
+* };
*
* If you accidentally write
*
-* struct bar mydist = {
-* FOO(mydist),
-* ...
-* };
+* struct bar mydist = {
+* FOO(mydist),
+* ...
+* };
*
* then the compiler will report a type mismatch in the sizeof
* expression, which otherwise evaporates at runtime.
diff --git a/src/lib/memarea/memarea.c b/src/lib/memarea/memarea.c
index 84c73b0b95..f3bb79a1e2 100644
--- a/src/lib/memarea/memarea.c
+++ b/src/lib/memarea/memarea.c
@@ -68,7 +68,7 @@
uint32_t sent_val = get_uint32(&(chunk)->U_MEM[chunk->mem_size]); \
tor_assert(sent_val == SENTINEL_VAL); \
STMT_END
-#else /* !(defined(USE_SENTINELS)) */
+#else /* !defined(USE_SENTINELS) */
#define SENTINEL_LEN 0
#define SET_SENTINEL(chunk) STMT_NIL
#define CHECK_SENTINEL(chunk) STMT_NIL
@@ -315,7 +315,7 @@ memarea_assert_ok(memarea_t *area)
}
}
-#else /* !(!defined(DISABLE_MEMORY_SENTINELS)) */
+#else /* defined(DISABLE_MEMORY_SENTINELS) */
struct memarea_t {
smartlist_t *pieces;
diff --git a/src/lib/meminfo/meminfo.c b/src/lib/meminfo/meminfo.c
index f4fa45167e..bff71c2f05 100644
--- a/src/lib/meminfo/meminfo.c
+++ b/src/lib/meminfo/meminfo.c
@@ -54,7 +54,7 @@ tor_log_mallinfo(int severity)
mi.arena, mi.ordblks, mi.smblks, mi.hblks,
mi.hblkhd, mi.usmblks, mi.fsmblks, mi.uordblks, mi.fordblks,
mi.keepcost);
-#else /* !(defined(HAVE_MALLINFO)) */
+#else /* !defined(HAVE_MALLINFO) */
(void)severity;
#endif /* defined(HAVE_MALLINFO) */
}
diff --git a/src/lib/net/address.c b/src/lib/net/address.c
index dd6a83974a..106e560a48 100644
--- a/src/lib/net/address.c
+++ b/src/lib/net/address.c
@@ -2001,7 +2001,7 @@ tor_addr_port_new(const tor_addr_t *addr, uint16_t port)
return ap;
}
-/** Return true iff <a>a</b> and <b>b</b> are the same address and port */
+/** Return true iff <b>a</b> and <b>b</b> are the same address and port */
int
tor_addr_port_eq(const tor_addr_port_t *a,
const tor_addr_port_t *b)
diff --git a/src/lib/net/nettypes.h b/src/lib/net/nettypes.h
index 0eb352c657..60039bac09 100644
--- a/src/lib/net/nettypes.h
+++ b/src/lib/net/nettypes.h
@@ -31,7 +31,7 @@ typedef int socklen_t;
#define TOR_SOCKET_T_FORMAT "%"PRIuPTR
#define SOCKET_OK(s) ((SOCKET)(s) != INVALID_SOCKET)
#define TOR_INVALID_SOCKET INVALID_SOCKET
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
/** Type used for a network socket. */
#define tor_socket_t int
#define TOR_SOCKET_T_FORMAT "%d"
diff --git a/src/lib/net/network_sys.h b/src/lib/net/network_sys.h
index 43e62592ca..34ac3d120c 100644
--- a/src/lib/net/network_sys.h
+++ b/src/lib/net/network_sys.h
@@ -2,7 +2,7 @@
/* See LICENSE for licensing information */
/**
- * \file log_network.h
+ * \file network_sys.h
* \brief Declare subsystem object for the network module.
**/
diff --git a/src/lib/net/resolve.c b/src/lib/net/resolve.c
index e8d7d0d94d..442bc4a6b3 100644
--- a/src/lib/net/resolve.c
+++ b/src/lib/net/resolve.c
@@ -8,6 +8,7 @@
* \brief Use the libc DNS resolver to convert hostnames into addresses.
**/
+#define RESOLVE_PRIVATE
#include "lib/net/resolve.h"
#include "lib/net/address.h"
@@ -70,10 +71,10 @@ tor_lookup_hostname,(const char *name, uint32_t *addr))
*
* See tor_addr_lookup() for details.
*/
-static int
-tor_addr_lookup_host_getaddrinfo(const char *name,
- uint16_t family,
- tor_addr_t *addr)
+MOCK_IMPL(STATIC int,
+tor_addr_lookup_host_impl,(const char *name,
+ uint16_t family,
+ tor_addr_t *addr))
{
int err;
struct addrinfo *res=NULL, *res_p;
@@ -118,17 +119,19 @@ tor_addr_lookup_host_getaddrinfo(const char *name,
return (err == EAI_AGAIN) ? 1 : -1;
}
-#else /* !(defined(HAVE_GETADDRINFO)) */
+#else /* !defined(HAVE_GETADDRINFO) */
-/* Host lookup helper for tor_addr_lookup(), which calls getaddrinfo().
- * Used when gethostbyname() is not available on this system.
+/* Host lookup helper for tor_addr_lookup(), which calls gethostbyname().
+ * Used when getaddrinfo() is not available on this system.
*
* See tor_addr_lookup() for details.
*/
-static int
-tor_addr_lookup_host_gethostbyname(const char *name,
- tor_addr_t *addr)
+MOCK_IMPL(STATIC int,
+tor_addr_lookup_host_impl,(const char *name,
+ uint16_t family,
+ tor_addr_t *addr))
{
+ (void) family;
struct hostent *ent;
int err;
#ifdef HAVE_GETHOSTBYNAME_R_6_ARG
@@ -170,7 +173,6 @@ tor_addr_lookup_host_gethostbyname(const char *name,
return (err == TRY_AGAIN) ? 1 : -1;
#endif
}
-
#endif /* defined(HAVE_GETADDRINFO) */
/** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set
@@ -215,13 +217,8 @@ tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr))
} else {
/* Clear the address after a failed tor_addr_parse(). */
memset(addr, 0, sizeof(tor_addr_t));
-#ifdef HAVE_GETADDRINFO
- result = tor_addr_lookup_host_getaddrinfo(name, family, addr);
+ result = tor_addr_lookup_host_impl(name, family, addr);
goto done;
-#else /* !(defined(HAVE_GETADDRINFO)) */
- result = tor_addr_lookup_host_gethostbyname(name, addr);
- goto done;
-#endif /* defined(HAVE_GETADDRINFO) */
}
/* If we weren't successful, and haven't already set the result,
@@ -506,7 +503,7 @@ tor_make_getaddrinfo_cache_active(void)
{
sandbox_getaddrinfo_is_active = 1;
}
-#else /* !(defined(USE_SANDBOX_GETADDRINFO)) */
+#else /* !defined(USE_SANDBOX_GETADDRINFO) */
void
sandbox_disable_getaddrinfo_cache(void)
{
diff --git a/src/lib/net/resolve.h b/src/lib/net/resolve.h
index d11c902a91..b979b2fb41 100644
--- a/src/lib/net/resolve.h
+++ b/src/lib/net/resolve.h
@@ -24,12 +24,18 @@
struct tor_addr_t;
+/*
+ * Primary lookup functions.
+ */
MOCK_DECL(int, tor_lookup_hostname,(const char *name, uint32_t *addr));
MOCK_DECL(int, tor_addr_lookup,(const char *name, uint16_t family,
struct tor_addr_t *addr_out));
int tor_addr_port_lookup(const char *s, struct tor_addr_t *addr_out,
uint16_t *port_out);
+/*
+ * Sandbox helpers
+ */
struct addrinfo;
#ifdef USE_SANDBOX_GETADDRINFO
/** Pre-calls getaddrinfo in order to pre-record result. */
@@ -42,7 +48,7 @@ int tor_getaddrinfo(const char *name, const char *servname,
struct addrinfo **res);
void tor_freeaddrinfo(struct addrinfo *addrinfo);
void tor_free_getaddrinfo_cache(void);
-#else /* !(defined(USE_SANDBOX_GETADDRINFO)) */
+#else /* !defined(USE_SANDBOX_GETADDRINFO) */
#define tor_getaddrinfo(name, servname, hints, res) \
getaddrinfo((name),(servname), (hints),(res))
#define tor_add_addrinfo(name) \
@@ -55,4 +61,13 @@ void tor_free_getaddrinfo_cache(void);
void sandbox_disable_getaddrinfo_cache(void);
void tor_make_getaddrinfo_cache_active(void);
+/*
+ * Internal resolver wrapper; exposed for mocking.
+ */
+#ifdef RESOLVE_PRIVATE
+MOCK_DECL(STATIC int, tor_addr_lookup_host_impl, (const char *name,
+ uint16_t family,
+ struct tor_addr_t *addr));
+#endif
+
#endif /* !defined(TOR_RESOLVE_H) */
diff --git a/src/lib/net/socket.c b/src/lib/net/socket.c
index e824a05045..e1b82251ed 100644
--- a/src/lib/net/socket.c
+++ b/src/lib/net/socket.c
@@ -84,7 +84,7 @@ check_network_configuration(bool server_mode)
"so your relay makes it harder to figure out how busy it is.");
}
}
-#else /* !(defined(__FreeBSD__)) */
+#else /* !defined(__FreeBSD__) */
(void) server_mode;
#endif /* defined(__FreeBSD__) */
}
@@ -206,7 +206,7 @@ mark_socket_closed(tor_socket_t s)
bitarray_clear(open_sockets, s);
}
}
-#else /* !(defined(DEBUG_SOCKET_COUNTING)) */
+#else /* !defined(DEBUG_SOCKET_COUNTING) */
#define mark_socket_open(s) ((void) (s))
#define mark_socket_closed(s) ((void) (s))
#endif /* defined(DEBUG_SOCKET_COUNTING) */
@@ -308,7 +308,7 @@ tor_open_socket_with_extensions(int domain, int type, int protocol,
return TOR_INVALID_SOCKET;
}
}
-#else /* !(defined(FD_CLOEXEC)) */
+#else /* !defined(FD_CLOEXEC) */
(void)cloexec;
#endif /* defined(FD_CLOEXEC) */
@@ -418,7 +418,7 @@ tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr,
return TOR_INVALID_SOCKET;
}
}
-#else /* !(defined(FD_CLOEXEC)) */
+#else /* !defined(FD_CLOEXEC) */
(void)cloexec;
#endif /* defined(FD_CLOEXEC) */
diff --git a/src/lib/net/socket.h b/src/lib/net/socket.h
index 193ad91e4c..53a9f1bb92 100644
--- a/src/lib/net/socket.h
+++ b/src/lib/net/socket.h
@@ -92,7 +92,7 @@ ssize_t read_all_from_socket(tor_socket_t fd, char *buf, size_t count);
#define ERRNO_IS_EINTR(e) ((e) == WSAEINTR || 0)
int tor_socket_errno(tor_socket_t sock);
const char *tor_socket_strerror(int e);
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
#define SOCK_ERRNO(e) e
#if EAGAIN == EWOULDBLOCK
/* || 0 is for -Wparentheses-equality (-Wall?) appeasement under clang */
diff --git a/src/lib/net/socketpair.c b/src/lib/net/socketpair.c
index 3be7b26f7f..f3a0c3770a 100644
--- a/src/lib/net/socketpair.c
+++ b/src/lib/net/socketpair.c
@@ -22,7 +22,7 @@
#include <windows.h>
#define socket_errno() (WSAGetLastError())
#define SOCKET_EPROTONOSUPPORT WSAEPROTONOSUPPORT
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
#define closesocket(x) close(x)
#define socket_errno() (errno)
#define SOCKET_EPROTONOSUPPORT EPROTONOSUPPORT
diff --git a/src/lib/osinfo/uname.c b/src/lib/osinfo/uname.c
index 2b37ff136c..34860c407a 100644
--- a/src/lib/osinfo/uname.c
+++ b/src/lib/osinfo/uname.c
@@ -137,7 +137,7 @@ get_uname,(void))
if (!is_server && !is_client) {
strlcat(uname_result, " [client or server]", sizeof(uname_result));
}
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
/* LCOV_EXCL_START -- can't provoke uname failure */
strlcpy(uname_result, "Unknown platform", sizeof(uname_result));
/* LCOV_EXCL_STOP */
diff --git a/src/lib/process/daemon.c b/src/lib/process/daemon.c
index 3b90bef671..ae34b5bcb8 100644
--- a/src/lib/process/daemon.c
+++ b/src/lib/process/daemon.c
@@ -165,7 +165,7 @@ finish_daemon(const char *desired_cwd)
return 0;
}
-#else /* !(!defined(_WIN32)) */
+#else /* defined(_WIN32) */
/* defined(_WIN32) */
int
start_daemon(void)
diff --git a/src/lib/process/env.c b/src/lib/process/env.c
index 0060200ba1..3912ade197 100644
--- a/src/lib/process/env.c
+++ b/src/lib/process/env.c
@@ -47,7 +47,7 @@ get_environment(void)
* when we do a mostly-static build on OSX 10.7, the resulting binary won't
* work on OSX 10.6. */
return *_NSGetEnviron();
-#else /* !(defined(HAVE__NSGETENVIRON)) */
+#else /* !defined(HAVE__NSGETENVIRON) */
return environ;
#endif /* defined(HAVE__NSGETENVIRON) */
}
diff --git a/src/lib/process/process.c b/src/lib/process/process.c
index 631c7169f1..b01c99992c 100644
--- a/src/lib/process/process.c
+++ b/src/lib/process/process.c
@@ -513,7 +513,7 @@ process_get_unix_process(const process_t *process)
tor_assert(process->unix_process);
return process->unix_process;
}
-#else /* !(!defined(_WIN32)) */
+#else /* defined(_WIN32) */
/** Get the internal handle for Windows backend. */
process_win32_t *
process_get_win32_process(const process_t *process)
@@ -550,6 +550,7 @@ process_vprintf(process_t *process,
char *data;
size = tor_vasprintf(&data, format, args);
+ tor_assert(data != NULL);
process_write(process, (uint8_t *)data, size);
tor_free(data);
}
diff --git a/src/lib/process/process.h b/src/lib/process/process.h
index 05c091a5bf..b28f55e0a1 100644
--- a/src/lib/process/process.h
+++ b/src/lib/process/process.h
@@ -35,8 +35,8 @@ typedef enum {
const char *process_status_to_string(process_status_t status);
typedef enum {
- /** Pass complete \n-terminated lines to the
- * callback (with the \n or \r\n removed). */
+ /** Pass complete newline-terminated lines to the
+ * callback (with the LF or CRLF removed). */
PROCESS_PROTOCOL_LINE,
/** Pass the raw response from read() to the callback. */
diff --git a/src/lib/process/process_unix.c b/src/lib/process/process_unix.c
index 17ade87463..8191bdc1f0 100644
--- a/src/lib/process/process_unix.c
+++ b/src/lib/process/process_unix.c
@@ -199,7 +199,7 @@ process_unix_exec(process_t *process)
"Cannot find maximum file descriptor, assuming: %d", max_fd);
}
}
-#else /* !(defined(_SC_OPEN_MAX)) */
+#else /* !defined(_SC_OPEN_MAX) */
max_fd = DEFAULT_MAX_FD;
#endif /* defined(_SC_OPEN_MAX) */
@@ -253,22 +253,15 @@ process_unix_exec(process_t *process)
process_environment_t *env = process_get_environment(process);
/* Call the requested program. */
- retval = execve(argv[0], argv, env->unixoid_environment_block);
+ execve(argv[0], argv, env->unixoid_environment_block);
/* If we made it here it is because execve failed :-( */
- if (-1 == retval)
- fprintf(stderr, "Call to execve() failed: %s", strerror(errno));
-
tor_free(argv);
process_environment_free(env);
- tor_assert_unreached();
-
error:
- /* LCOV_EXCL_START */
fprintf(stderr, "Error from child process: %s", strerror(errno));
_exit(1);
- /* LCOV_EXCL_STOP */
}
/* We are in the parent process. */
diff --git a/src/lib/process/process_win32.c b/src/lib/process/process_win32.c
index 624333d4a3..7e4082ad13 100644
--- a/src/lib/process/process_win32.c
+++ b/src/lib/process/process_win32.c
@@ -234,6 +234,24 @@ process_win32_exec(process_t *process)
CloseHandle(stdin_pipe_read);
CloseHandle(stdin_pipe_write);
+ /* In the Unix backend, we do not get an error in the Tor process when a
+ * child process fails to spawn its target executable since we need to
+ * first do the fork() call in the Tor process and then the child process
+ * is responsible for doing the call to execve().
+ *
+ * This means that the user of the process_exec() API must check for
+ * whether it returns PROCESS_STATUS_ERROR, which will rarely happen on
+ * Unix, but will happen for error cases on Windows where it does not
+ * happen on Unix. For example: when the target executable does not exist
+ * on the file system.
+ *
+ * To have somewhat feature compatibility between the Unix and the Windows
+ * backend, we here notify the process_t owner that the process have exited
+ * (even though it never managed to run) to ensure that the exit callback
+ * is executed.
+ */
+ process_notify_event_exit(process, 0);
+
return PROCESS_STATUS_ERROR;
}
diff --git a/src/lib/process/restrict.c b/src/lib/process/restrict.c
index 534b39d101..fda284f3d9 100644
--- a/src/lib/process/restrict.c
+++ b/src/lib/process/restrict.c
@@ -152,7 +152,7 @@ tor_mlockall(void)
"pages: %s", strerror(errno));
return -1;
}
-#else /* !(defined(HAVE_UNIX_MLOCKALL)) */
+#else /* !defined(HAVE_UNIX_MLOCKALL) */
log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?");
return -1;
#endif /* defined(HAVE_UNIX_MLOCKALL) */
@@ -214,7 +214,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
return -1;
}
limit = MAX_CONNECTIONS;
-#else /* !(!defined(HAVE_GETRLIMIT)) */
+#else /* defined(HAVE_GETRLIMIT) */
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
diff --git a/src/lib/process/setuid.c b/src/lib/process/setuid.c
index 6e8258f279..3c94ce4bac 100644
--- a/src/lib/process/setuid.c
+++ b/src/lib/process/setuid.c
@@ -72,7 +72,7 @@ log_credential_status(void)
"UID is %u (real), %u (effective), %u (saved)",
(unsigned)ruid, (unsigned)euid, (unsigned)suid);
}
-#else /* !(defined(HAVE_GETRESUID)) */
+#else /* !defined(HAVE_GETRESUID) */
/* getresuid is not present on MacOS X, so we can't get the saved (E)UID */
ruid = getuid();
euid = geteuid();
@@ -93,7 +93,7 @@ log_credential_status(void)
"GID is %u (real), %u (effective), %u (saved)",
(unsigned)rgid, (unsigned)egid, (unsigned)sgid);
}
-#else /* !(defined(HAVE_GETRESGID)) */
+#else /* !defined(HAVE_GETRESGID) */
/* getresgid is not present on MacOS X, so we can't get the saved (E)GID */
rgid = getgid();
egid = getegid();
@@ -154,7 +154,7 @@ have_capability_support(void)
return 0;
cap_free(caps);
return 1;
-#else /* !(defined(HAVE_LINUX_CAPABILITIES)) */
+#else /* !defined(HAVE_LINUX_CAPABILITIES) */
return 0;
#endif /* defined(HAVE_LINUX_CAPABILITIES) */
}
@@ -265,7 +265,7 @@ switch_id(const char *user, const unsigned flags)
if (drop_capabilities(1))
return -1;
}
-#else /* !(defined(HAVE_LINUX_CAPABILITIES)) */
+#else /* !defined(HAVE_LINUX_CAPABILITIES) */
(void) keep_bindlow;
if (warn_if_no_caps) {
log_warn(LD_CONFIG, "KeepBindCapabilities set, but no capability support "
@@ -376,7 +376,7 @@ switch_id(const char *user, const unsigned flags)
#endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && ... */
return 0;
-#else /* !(!defined(_WIN32)) */
+#else /* defined(_WIN32) */
(void)user;
(void)flags;
diff --git a/src/lib/process/winprocess_sys.c b/src/lib/process/winprocess_sys.c
index ff9bc1ba04..ad65886422 100644
--- a/src/lib/process/winprocess_sys.c
+++ b/src/lib/process/winprocess_sys.c
@@ -51,7 +51,7 @@ subsys_winprocess_initialize(void)
return 0;
}
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
#define WINPROCESS_SYS_ENABLED false
#define subsys_winprocess_initialize NULL
#endif /* defined(_WIN32) */
diff --git a/src/lib/sandbox/sandbox.c b/src/lib/sandbox/sandbox.c
index b652397f5a..0b316e9c6a 100644
--- a/src/lib/sandbox/sandbox.c
+++ b/src/lib/sandbox/sandbox.c
@@ -294,6 +294,7 @@ sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
unsigned i;
int rc;
int param[] = { SIGINT, SIGTERM, SIGPIPE, SIGUSR1, SIGUSR2, SIGHUP, SIGCHLD,
+ SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS, SIGIO,
#ifdef SIGXFSZ
SIGXFSZ
#endif
@@ -443,7 +444,7 @@ libc_uses_openat_for_everything(void)
return 1;
else
return 0;
-#else /* !(defined(CHECK_LIBC_VERSION)) */
+#else /* !defined(CHECK_LIBC_VERSION) */
return 0;
#endif /* defined(CHECK_LIBC_VERSION) */
}
diff --git a/src/lib/sandbox/sandbox.h b/src/lib/sandbox/sandbox.h
index 5bec09a36a..b4ae6e5c07 100644
--- a/src/lib/sandbox/sandbox.h
+++ b/src/lib/sandbox/sandbox.h
@@ -108,7 +108,7 @@ typedef struct {
* it matches the parameter.
*/
const char* sandbox_intern_string(const char *param);
-#else /* !(defined(USE_LIBSECCOMP)) */
+#else /* !defined(USE_LIBSECCOMP) */
#define sandbox_intern_string(s) (s)
#endif /* defined(USE_LIBSECCOMP) */
diff --git a/src/lib/smartlist_core/smartlist_core.h b/src/lib/smartlist_core/smartlist_core.h
index 795741c447..36f23e2009 100644
--- a/src/lib/smartlist_core/smartlist_core.h
+++ b/src/lib/smartlist_core/smartlist_core.h
@@ -77,7 +77,7 @@ static inline void smartlist_set(smartlist_t *sl, int idx, void *val) {
raw_assert(sl->num_used > idx);
sl->list[idx] = val;
}
-#else /* !(defined(DEBUG_SMARTLIST)) */
+#else /* !defined(DEBUG_SMARTLIST) */
#define smartlist_len(sl) ((sl)->num_used)
#define smartlist_get(sl, idx) ((sl)->list[idx])
#define smartlist_set(sl, idx, val) ((sl)->list[idx] = (val))
diff --git a/src/lib/testsupport/testsupport.h b/src/lib/testsupport/testsupport.h
index 631ec0228c..90b7c43b19 100644
--- a/src/lib/testsupport/testsupport.h
+++ b/src/lib/testsupport/testsupport.h
@@ -21,7 +21,7 @@
* tests. */
#define STATIC
#define EXTERN(type, name) extern type name;
-#else /* !(defined(TOR_UNIT_TESTS)) */
+#else /* !defined(TOR_UNIT_TESTS) */
#define STATIC static
#define EXTERN(type, name)
#endif /* defined(TOR_UNIT_TESTS) */
@@ -90,7 +90,7 @@
do { \
func = func ##__real; \
} while (0)
-#else /* !(defined(TOR_UNIT_TESTS)) */
+#else /* !defined(TOR_UNIT_TESTS) */
#define MOCK_DECL(rv, funcname, arglist) \
rv funcname arglist
#define MOCK_DECL_ATTR(rv, funcname, arglist, attr) \
diff --git a/src/lib/thread/compat_threads.c b/src/lib/thread/compat_threads.c
index 1c4a5c4e3f..5c8ffa55c6 100644
--- a/src/lib/thread/compat_threads.c
+++ b/src/lib/thread/compat_threads.c
@@ -67,7 +67,15 @@ atomic_counter_init(atomic_counter_t *counter)
memset(counter, 0, sizeof(*counter));
tor_mutex_init_nonrecursive(&counter->mutex);
}
-/** Clean up all resources held by an atomic counter. */
+/** Clean up all resources held by an atomic counter.
+ *
+ * Destroying a locked mutex is undefined behaviour. Global mutexes may be
+ * locked when they are passed to this function, because multiple threads can
+ * still access them. So we can either:
+ * - destroy on shutdown, and re-initialise when tor re-initialises, or
+ * - skip destroying and re-initialisation, using a sentinel variable.
+ * See #31735 for details.
+ */
void
atomic_counter_destroy(atomic_counter_t *counter)
{
diff --git a/src/lib/thread/thread_sys.h b/src/lib/thread/thread_sys.h
index c0daf2b5e9..ef27134a32 100644
--- a/src/lib/thread/thread_sys.h
+++ b/src/lib/thread/thread_sys.h
@@ -2,7 +2,7 @@
/* See LICENSE for licensing information */
/**
- * \file threads_sys.h
+ * \file thread_sys.h
* \brief Declare subsystem object for threads library
**/
diff --git a/src/lib/thread/threads.h b/src/lib/thread/threads.h
index fcce651936..2e63dac5d9 100644
--- a/src/lib/thread/threads.h
+++ b/src/lib/thread/threads.h
@@ -133,7 +133,17 @@ atomic_counter_init(atomic_counter_t *counter)
{
atomic_init(&counter->val, 0);
}
-/** Clean up all resources held by an atomic counter. */
+/** Clean up all resources held by an atomic counter.
+ *
+ * This usage note applies to the compat_threads implementation of
+ * atomic_counter_destroy():
+ * Destroying a locked mutex is undefined behaviour. Global mutexes may be
+ * locked when they are passed to this function, because multiple threads can
+ * still access them. So we can either:
+ * - destroy on shutdown, and re-initialise when tor re-initialises, or
+ * - skip destroying and re-initialisation, using a sentinel variable.
+ * See #31735 for details.
+ */
static inline void
atomic_counter_destroy(atomic_counter_t *counter)
{
@@ -164,7 +174,7 @@ atomic_counter_exchange(atomic_counter_t *counter, size_t newval)
return atomic_exchange(&counter->val, newval);
}
-#else /* !(defined(HAVE_WORKING_STDATOMIC)) */
+#else /* !defined(HAVE_WORKING_STDATOMIC) */
#endif /* defined(HAVE_WORKING_STDATOMIC) */
#endif /* !defined(TOR_COMPAT_THREADS_H) */
diff --git a/src/lib/time/compat_time.c b/src/lib/time/compat_time.c
index 3f41500f3a..ab45224a7f 100644
--- a/src/lib/time/compat_time.c
+++ b/src/lib/time/compat_time.c
@@ -833,7 +833,7 @@ monotime_coarse_absolute_msec(void)
{
return monotime_coarse_absolute_nsec() / ONE_MILLION;
}
-#else /* !(defined(MONOTIME_COARSE_FN_IS_DIFFERENT)) */
+#else /* !defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */
#define initialized_at_coarse initialized_at
#endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */
@@ -865,7 +865,7 @@ monotime_msec_to_approx_coarse_stamp_units(uint64_t msec)
mach_time_info.numer;
return abstime_val >> monotime_shift;
}
-#else /* !(defined(__APPLE__)) */
+#else /* !defined(__APPLE__) */
uint64_t
monotime_coarse_stamp_units_to_approx_msec(uint64_t units)
{
diff --git a/src/lib/time/compat_time.h b/src/lib/time/compat_time.h
index 8c7661d7cb..4d16effd29 100644
--- a/src/lib/time/compat_time.h
+++ b/src/lib/time/compat_time.h
@@ -259,7 +259,7 @@ void monotime_coarse_get(monotime_coarse_t *out);
uint64_t monotime_coarse_absolute_nsec(void);
uint64_t monotime_coarse_absolute_usec(void);
uint64_t monotime_coarse_absolute_msec(void);
-#else /* !(defined(MONOTIME_COARSE_FN_IS_DIFFERENT)) */
+#else /* !defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */
#define monotime_coarse_get monotime_get
#define monotime_coarse_absolute_nsec monotime_absolute_nsec
#define monotime_coarse_absolute_usec monotime_absolute_usec
@@ -304,7 +304,7 @@ void monotime_coarse_zero(monotime_coarse_t *out);
int monotime_coarse_is_zero(const monotime_coarse_t *val);
void monotime_coarse_add_msec(monotime_coarse_t *out,
const monotime_coarse_t *val, uint32_t msec);
-#else /* !(defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)) */
+#else /* !defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */
#define monotime_coarse_diff_nsec monotime_diff_nsec
#define monotime_coarse_diff_usec monotime_diff_usec
#define monotime_coarse_diff_msec monotime_diff_msec
diff --git a/src/lib/tls/buffers_tls.c b/src/lib/tls/buffers_tls.c
index 3c18cc7e43..bf03b61459 100644
--- a/src/lib/tls/buffers_tls.c
+++ b/src/lib/tls/buffers_tls.c
@@ -68,9 +68,9 @@ buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most)
check_no_tls_errors();
- if (BUG(buf->datalen >= INT_MAX))
+ IF_BUG_ONCE(buf->datalen >= INT_MAX)
return -1;
- if (BUG(buf->datalen >= INT_MAX - at_most))
+ IF_BUG_ONCE(buf->datalen >= INT_MAX - at_most)
return -1;
while (at_most > total_read) {
diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h
index 9e195c6af2..799bd6aaeb 100644
--- a/src/lib/tls/tortls.h
+++ b/src/lib/tls/tortls.h
@@ -25,7 +25,7 @@ struct ssl_ctx_st;
struct ssl_session_st;
typedef struct ssl_ctx_st tor_tls_context_impl_t;
typedef struct ssl_st tor_tls_impl_t;
-#else /* !(defined(ENABLE_OPENSSL)) */
+#else /* !defined(ENABLE_OPENSSL) */
struct PRFileDesc;
typedef struct PRFileDesc tor_tls_context_impl_t;
typedef struct PRFileDesc tor_tls_impl_t;
@@ -144,7 +144,7 @@ void check_no_tls_errors_(const char *fname, int line);
void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
int severity, int domain, const char *doing);
-#else /* !(defined(ENABLE_OPENSSL)) */
+#else /* !defined(ENABLE_OPENSSL) */
#define check_no_tls_errors() STMT_NIL
#endif /* defined(ENABLE_OPENSSL) */
diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c
index 42db05460e..9184cafd60 100644
--- a/src/lib/tls/tortls_openssl.c
+++ b/src/lib/tls/tortls_openssl.c
@@ -659,7 +659,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
if (r < 0)
goto error;
}
-#else /* !(defined(SSL_CTX_set1_groups_list) || ...) */
+#else /* !(defined(SSL_CTX_set1_groups_list) || defined(HAVE_SSL_CTX_SE...)) */
if (! is_client) {
int nid;
EC_KEY *ec_key;
@@ -675,7 +675,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
SSL_CTX_set_tmp_ecdh(result->ctx, ec_key);
EC_KEY_free(ec_key);
}
-#endif /* defined(SSL_CTX_set1_groups_list) || ...) */
+#endif /* defined(SSL_CTX_set1_groups_list) || defined(HAVE_SSL_CTX_SET1...) */
SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
always_accept_verify_cb);
/* let us realloc bufs that we're writing from */
@@ -766,7 +766,7 @@ find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher);
return c != NULL;
}
-#else /* !(defined(HAVE_SSL_CIPHER_FIND)) */
+#else /* !defined(HAVE_SSL_CIPHER_FIND) */
# if defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR)
if (m && m->get_cipher_by_char) {
diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c
index 03f65049cf..7724288279 100644
--- a/src/lib/tls/x509_openssl.c
+++ b/src/lib/tls/x509_openssl.c
@@ -59,7 +59,7 @@ ENABLE_GCC_WARNING(redundant-decls)
#define X509_get_notAfter(cert) \
X509_getm_notAfter(cert)
#endif
-#else /* !(defined(OPENSSL_1_1_API)) */
+#else /* !defined(OPENSSL_1_1_API) */
#define X509_get_notBefore_const(cert) \
((const ASN1_TIME*) X509_get_notBefore((X509 *)cert))
#define X509_get_notAfter_const(cert) \
diff --git a/src/lib/trace/events.h b/src/lib/trace/events.h
index 0674f7d501..9de86d63f2 100644
--- a/src/lib/trace/events.h
+++ b/src/lib/trace/events.h
@@ -34,7 +34,7 @@
#include "lib/trace/debug.h"
#endif
-#else /* !(defined(TOR_EVENT_TRACING_ENABLED)) */
+#else /* !defined(TOR_EVENT_TRACING_ENABLED) */
/* Reaching this point, we NOP every event declaration because event tracing
* is not been enabled at compile time. */