diff options
Diffstat (limited to 'src/lib')
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->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. */ |