diff --git a/banned-die.h b/banned-die.h new file mode 100644 index 00000000000000..3dc521f6b01e5c --- /dev/null +++ b/banned-die.h @@ -0,0 +1,32 @@ +#ifndef BANNED_DIE_H +#define BANNED_DIE_H + +#include "banned.h" + +/* + * This header lists functions that must not be used by low-level APIs + * because they can cause Git to terminate. + */ + +#undef die +#define die banned(die) + +#undef xsnprintf +#define xsnprintf(...) BANNED(xsnprintf) + +#undef xstrdup +#define xstrdup(str) BANNED(xstrdup) + +#undef xcalloc +#define xcalloc(nmemb, size) BANNED(xcalloc) + +#undef xstrfmt +#define xstrfmt(...) BANNED(xstrfmt) + +#undef ALLOC_ARRAY +#define ALLOC_ARRAY(x, alloc) BANNED(ALLOC_ARRAY) + +#undef ALLOC_GROW +#define ALLOC_GROW(x, nr, alloc) BANNED(ALLOC_GROW) + +#endif /* BANNED_DIE_H */ diff --git a/trace2.c b/trace2.c index c23c0a227b7032..c37f783fa032a2 100644 --- a/trace2.c +++ b/trace2.c @@ -17,6 +17,7 @@ #include "trace2/tr2_tgt.h" #include "trace2/tr2_tls.h" #include "trace2/tr2_tmr.h" +#include "banned-die.h" static int trace2_enabled; static int trace2_redact = 1; @@ -259,7 +260,10 @@ int trace2_is_enabled(void) static const char *redact_arg(const char *arg) { const char *p, *colon; + const char *redact = ":"; + char *redacted; size_t at; + size_t prefix_len, suffix_len, redacted_len, redact_len; if (!trace2_redact || (!skip_prefix(arg, "https://", &p) && @@ -274,7 +278,25 @@ static const char *redact_arg(const char *arg) if (!colon) return arg; - return xstrfmt("%.*s:%s", (int)(colon - arg), arg, p + at); + redact_len = strlen(redact); + prefix_len = colon - arg; + suffix_len = strlen(p + at); + + if (unsigned_add_overflows(prefix_len, suffix_len) || + unsigned_add_overflows(prefix_len + suffix_len, redact_len)) + return NULL; + + redacted_len = prefix_len + suffix_len + redact_len; + + redacted = malloc(redacted_len); + if (!redacted) + return NULL; + + memcpy(redacted, arg, prefix_len); + memcpy(redacted + prefix_len, redact, redact_len - 1); + memcpy(redacted + prefix_len + redact_len - 1, p + at, + suffix_len + 1); + return redacted; } /* @@ -299,11 +321,17 @@ static const char **redact_argv(const char **argv) if (!argv[i]) return argv; + if (!redacted) + return NULL; for (j = 0; argv[j]; j++) ; /* keep counting */ - ALLOC_ARRAY(ret, j + 1); + ret = calloc(j + 1, sizeof(*ret)); + if (!ret) { + free((char *)redacted); + return NULL; + } ret[j] = NULL; for (j = 0; j < i; j++) @@ -311,7 +339,14 @@ static const char **redact_argv(const char **argv) ret[i] = redacted; for (++i; argv[i]; i++) { redacted = redact_arg(argv[i]); - ret[i] = redacted ? redacted : argv[i]; + if (!redacted) { + for (j = 0; j < i; j++) + if (ret[j] != argv[j]) + free((void *)ret[j]); + free(ret); + return NULL; + } + ret[i] = redacted; } return ret; @@ -344,6 +379,8 @@ void trace2_cmd_start_fl(const char *file, int line, const char **argv) us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); redacted = redact_argv(argv); + if (!redacted) + return; for_each_wanted_builtin (j, tgt_j) if (tgt_j->pfn_start_fl) @@ -512,6 +549,7 @@ void trace2_child_start_fl(const char *file, int line, uint64_t us_now; uint64_t us_elapsed_absolute; const char **orig_argv = cmd->args.v; + const char **redacted; if (!trace2_enabled) return; @@ -529,7 +567,10 @@ void trace2_child_start_fl(const char *file, int line, * temporarily replace the original argv (inside the `strvec`) * with a possibly redacted version. */ - cmd->args.v = redact_argv(orig_argv); + redacted = redact_argv(orig_argv); + if (!redacted) + return; + cmd->args.v = redacted; for_each_wanted_builtin (j, tgt_j) if (tgt_j->pfn_child_start_fl) @@ -621,6 +662,8 @@ int trace2_exec_fl(const char *file, int line, const char *exe, exec_id = tr2tls_locked_increment(&tr2_next_exec_id); redacted = redact_argv(argv); + if (!redacted) + return exec_id; for_each_wanted_builtin (j, tgt_j) if (tgt_j->pfn_exec_fl) diff --git a/trace2/tr2_cfg.c b/trace2/tr2_cfg.c index bbcfeda60af4de..06912a3cebc041 100644 --- a/trace2/tr2_cfg.c +++ b/trace2/tr2_cfg.c @@ -7,6 +7,7 @@ #include "trace2/tr2_cfg.h" #include "trace2/tr2_sysenv.h" #include "wildmatch.h" +#include "banned-die.h" static struct string_list tr2_cfg_patterns = STRING_LIST_INIT_DUP; static int tr2_cfg_loaded; diff --git a/trace2/tr2_cmd_name.c b/trace2/tr2_cmd_name.c index b7b5a869b74bcd..88f24e8781f879 100644 --- a/trace2/tr2_cmd_name.c +++ b/trace2/tr2_cmd_name.c @@ -1,6 +1,7 @@ #include "git-compat-util.h" #include "strbuf.h" #include "trace2/tr2_cmd_name.h" +#include "banned-die.h" #define TR2_ENVVAR_PARENT_NAME "GIT_TRACE2_PARENT_NAME" diff --git a/trace2/tr2_ctr.c b/trace2/tr2_ctr.c index ee17bfa86b401b..5283946e08aef8 100644 --- a/trace2/tr2_ctr.c +++ b/trace2/tr2_ctr.c @@ -2,6 +2,7 @@ #include "trace2/tr2_tgt.h" #include "trace2/tr2_tls.h" #include "trace2/tr2_ctr.h" +#include "banned-die.h" /* * A global counter block to aggregate values from the partial sums @@ -53,7 +54,11 @@ static struct tr2_counter_metadata tr2_counter_metadata[TRACE2_NUMBER_OF_COUNTER void tr2_counter_increment(enum trace2_counter_id cid, uint64_t value) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); - struct tr2_counter *c = &ctx->counter_block.counter[cid]; + struct tr2_counter *c; + + if (tr2tls_is_fallback(ctx)) + return; + c = &ctx->counter_block.counter[cid]; c->value += value; @@ -67,6 +72,8 @@ void tr2_update_final_counters(void) struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); enum trace2_counter_id cid; + if (tr2tls_is_fallback(ctx)) + return; if (!ctx->used_any_counter) return; @@ -88,6 +95,8 @@ void tr2_emit_per_thread_counters(tr2_tgt_evt_counter_t *fn_apply) struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); enum trace2_counter_id cid; + if (tr2tls_is_fallback(ctx)) + return; if (!ctx->used_any_per_thread_counter) return; diff --git a/trace2/tr2_dst.c b/trace2/tr2_dst.c index 5be892cd5cdefa..686a3e42fcd835 100644 --- a/trace2/tr2_dst.c +++ b/trace2/tr2_dst.c @@ -5,6 +5,7 @@ #include "trace2/tr2_dst.h" #include "trace2/tr2_sid.h" #include "trace2/tr2_sysenv.h" +#include "banned-die.h" /* * How many attempts we will make at creating an automatically-named trace file. diff --git a/trace2/tr2_sid.c b/trace2/tr2_sid.c index 1c1d27b0eee935..358f61b301695b 100644 --- a/trace2/tr2_sid.c +++ b/trace2/tr2_sid.c @@ -3,6 +3,7 @@ #include "strbuf.h" #include "trace2/tr2_tbuf.h" #include "trace2/tr2_sid.h" +#include "banned-die.h" #define TR2_ENVVAR_PARENT_SID "GIT_TRACE2_PARENT_SID" diff --git a/trace2/tr2_sysenv.c b/trace2/tr2_sysenv.c index 4abc218514fdbc..4ee273a4aedd11 100644 --- a/trace2/tr2_sysenv.c +++ b/trace2/tr2_sysenv.c @@ -4,6 +4,7 @@ #include "config.h" #include "dir.h" #include "tr2_sysenv.h" +#include "banned-die.h" /* * Each entry represents a trace2 setting. @@ -73,7 +74,9 @@ static int tr2_sysenv_cb(const char *key, const char *value, if (!value) return config_error_nonbool(key); free(tr2_sysenv_settings[k].value); - tr2_sysenv_settings[k].value = xstrdup(value); + tr2_sysenv_settings[k].value = strdup(value); + if (!tr2_sysenv_settings[k].value) + return -1; return 0; } } @@ -109,7 +112,7 @@ const char *tr2_sysenv_get(enum tr2_sysenv_variable var) const char *v = getenv(tr2_sysenv_settings[var].env_var_name); if (v && *v) { free(tr2_sysenv_settings[var].value); - tr2_sysenv_settings[var].value = xstrdup(v); + tr2_sysenv_settings[var].value = strdup(v); } tr2_sysenv_settings[var].getenv_called = 1; } diff --git a/trace2/tr2_tbuf.c b/trace2/tr2_tbuf.c index c3b3822ed7e4af..fff345cb99f9e7 100644 --- a/trace2/tr2_tbuf.c +++ b/trace2/tr2_tbuf.c @@ -1,47 +1,67 @@ #include "git-compat-util.h" #include "tr2_tbuf.h" +#include "banned-die.h" void tr2_tbuf_local_time(struct tr2_tbuf *tb) { - struct timeval tv; - struct tm tm; + struct timeval tv = { 0 }; + struct tm tm = { 0 }; time_t secs; + int len; gettimeofday(&tv, NULL); secs = tv.tv_sec; localtime_r(&secs, &tm); - xsnprintf(tb->buf, sizeof(tb->buf), "%02d:%02d:%02d.%06ld", tm.tm_hour, - tm.tm_min, tm.tm_sec, (long)tv.tv_usec); + len = snprintf(tb->buf, sizeof(tb->buf), "%02d:%02d:%02d.%06ld", + tm.tm_hour, tm.tm_min, tm.tm_sec, (long)tv.tv_usec); + + if (len < 0 || (size_t)len >= sizeof(tb->buf)) { + const char *blank = "00:00:00.000000"; + strlcpy(tb->buf, blank, sizeof(tb->buf)); + } } void tr2_tbuf_utc_datetime_extended(struct tr2_tbuf *tb) { - struct timeval tv; - struct tm tm; + struct timeval tv = { 0 }; + struct tm tm = { 0 }; time_t secs; + int len; gettimeofday(&tv, NULL); secs = tv.tv_sec; gmtime_r(&secs, &tm); - xsnprintf(tb->buf, sizeof(tb->buf), - "%4d-%02d-%02dT%02d:%02d:%02d.%06ldZ", tm.tm_year + 1900, - tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, - (long)tv.tv_usec); + len = snprintf(tb->buf, sizeof(tb->buf), + "%4d-%02d-%02dT%02d:%02d:%02d.%06ldZ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, (long)tv.tv_usec); + + if (len < 0 || (size_t)len >= sizeof(tb->buf)) { + const char *blank = "1900-00-00T00:00:00.000000Z"; + strlcpy(tb->buf, blank, sizeof(tb->buf)); + } } void tr2_tbuf_utc_datetime(struct tr2_tbuf *tb) { - struct timeval tv; - struct tm tm; + struct timeval tv = { 0 }; + struct tm tm = { 0 }; time_t secs; + int len; gettimeofday(&tv, NULL); secs = tv.tv_sec; gmtime_r(&secs, &tm); - xsnprintf(tb->buf, sizeof(tb->buf), "%4d%02d%02dT%02d%02d%02d.%06ldZ", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, - tm.tm_min, tm.tm_sec, (long)tv.tv_usec); + len = snprintf(tb->buf, sizeof(tb->buf), + "%4d%02d%02dT%02d%02d%02d.%06ldZ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, (long)tv.tv_usec); + + if (len < 0 || (size_t)len >= sizeof(tb->buf)) { + const char *blank = "19000000T000000.000000Z"; + strlcpy(tb->buf, blank, sizeof(tb->buf)); + } } diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c index 5a0381791f7eb4..a055e19bace9c8 100644 --- a/trace2/tr2_tgt_event.c +++ b/trace2/tr2_tgt_event.c @@ -13,6 +13,7 @@ #include "trace2/tr2_tgt.h" #include "trace2/tr2_tls.h" #include "trace2/tr2_tmr.h" +#include "banned-die.h" static struct tr2_dst tr2dst_event = { .sysenv_var = TR2_SYSENV_EVENT, diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c index 924736ab36093b..97d4c5d2023089 100644 --- a/trace2/tr2_tgt_normal.c +++ b/trace2/tr2_tgt_normal.c @@ -11,6 +11,7 @@ #include "trace2/tr2_tgt.h" #include "trace2/tr2_tls.h" #include "trace2/tr2_tmr.h" +#include "banned-die.h" static struct tr2_dst tr2dst_normal = { .sysenv_var = TR2_SYSENV_NORMAL, diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c index 4eb9289f950505..1f49d9f9221bba 100644 --- a/trace2/tr2_tgt_perf.c +++ b/trace2/tr2_tgt_perf.c @@ -14,6 +14,7 @@ #include "trace2/tr2_tgt.h" #include "trace2/tr2_tls.h" #include "trace2/tr2_tmr.h" +#include "banned-die.h" static struct tr2_dst tr2dst_perf = { .sysenv_var = TR2_SYSENV_PERF, diff --git a/trace2/tr2_tls.c b/trace2/tr2_tls.c index 7b023c1bfc65fa..2c6aaed5049e73 100644 --- a/trace2/tr2_tls.c +++ b/trace2/tr2_tls.c @@ -3,6 +3,7 @@ #include "thread-utils.h" #include "trace.h" #include "trace2/tr2_tls.h" +#include "banned-die.h" /* * Initialize size of the thread stack for nested regions. @@ -12,6 +13,9 @@ #define TR2_REGION_NESTING_INITIAL_SIZE (100) static struct tr2tls_thread_ctx *tr2tls_thread_main; +static struct tr2tls_thread_ctx tr2tls_thread_fallback = { + .thread_name = "unknown", +}; static uint64_t tr2tls_us_start_process; static pthread_mutex_t tr2tls_mutex; @@ -36,16 +40,23 @@ void tr2tls_start_process_clock(void) struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_base_name, uint64_t us_thread_start) { - struct tr2tls_thread_ctx *ctx = xcalloc(1, sizeof(*ctx)); + struct tr2tls_thread_ctx *ctx = calloc(1, sizeof(*ctx)); struct strbuf buf = STRBUF_INIT; + if (!ctx) + goto fallback; + /* * Implicitly "tr2tls_push_self()" to capture the thread's start * time in array_us_start[0]. For the main thread this gives us the * application run time. */ ctx->alloc = TR2_REGION_NESTING_INITIAL_SIZE; - ctx->array_us_start = (uint64_t *)xcalloc(ctx->alloc, sizeof(uint64_t)); + ctx->array_us_start = calloc(ctx->alloc, sizeof(uint64_t)); + if (!ctx->array_us_start) { + free(ctx); + goto fallback; + } ctx->array_us_start[ctx->nr_open_regions++] = us_thread_start; ctx->thread_id = tr2tls_locked_increment(&tr2_next_thread_id); @@ -61,6 +72,10 @@ struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_base_name, pthread_setspecific(tr2tls_key, ctx); return ctx; + +fallback: + pthread_setspecific(tr2tls_key, &tr2tls_thread_fallback); + return &tr2tls_thread_fallback; } struct tr2tls_thread_ctx *tr2tls_get_self(void) @@ -83,6 +98,11 @@ struct tr2tls_thread_ctx *tr2tls_get_self(void) return ctx; } +int tr2tls_is_fallback(const struct tr2tls_thread_ctx *ctx) +{ + return ctx == &tr2tls_thread_fallback; +} + int tr2tls_is_main_thread(void) { if (!HAVE_THREADS) @@ -99,6 +119,9 @@ void tr2tls_unset_self(void) pthread_setspecific(tr2tls_key, NULL); + if (tr2tls_is_fallback(ctx)) + return; + free((char *)ctx->thread_name); free(ctx->array_us_start); free(ctx); @@ -107,8 +130,36 @@ void tr2tls_unset_self(void) void tr2tls_push_self(uint64_t us_now) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); + uint64_t *new_array; + size_t new_alloc; + + if (tr2tls_is_fallback(ctx)) + return; + + if (ctx->nr_skipped_regions) { + ctx->nr_skipped_regions++; + return; + } + + if (ctx->nr_open_regions >= ctx->alloc) { + if (ctx->alloc > + SIZE_MAX / (2 * sizeof(*ctx->array_us_start))) { + ctx->nr_skipped_regions++; + return; + } + new_alloc = ctx->alloc * 2; + + new_array = realloc(ctx->array_us_start, + new_alloc * sizeof(*ctx->array_us_start)); + if (!new_array) { + ctx->nr_skipped_regions++; + return; + } + + ctx->array_us_start = new_array; + ctx->alloc = new_alloc; + } - ALLOC_GROW(ctx->array_us_start, ctx->nr_open_regions + 1, ctx->alloc); ctx->array_us_start[ctx->nr_open_regions++] = us_now; } @@ -116,6 +167,14 @@ void tr2tls_pop_self(void) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); + if (tr2tls_is_fallback(ctx)) + return; + + if (ctx->nr_skipped_regions) { + ctx->nr_skipped_regions--; + return; + } + if (!ctx->nr_open_regions) BUG("no open regions in thread '%s'", ctx->thread_name); @@ -126,6 +185,9 @@ void tr2tls_pop_unwind_self(void) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); + if (tr2tls_is_fallback(ctx)) + return; + while (ctx->nr_open_regions > 1) tr2tls_pop_self(); } @@ -136,6 +198,10 @@ uint64_t tr2tls_region_elasped_self(uint64_t us) uint64_t us_start; ctx = tr2tls_get_self(); + if (tr2tls_is_fallback(ctx)) + return 0; + if (ctx->nr_skipped_regions) + return 0; if (!ctx->nr_open_regions) return 0; @@ -155,6 +221,10 @@ uint64_t tr2tls_absolute_elapsed(uint64_t us) static void tr2tls_key_destructor(void *payload) { struct tr2tls_thread_ctx *ctx = payload; + + if (tr2tls_is_fallback(ctx)) + return; + free((char *)ctx->thread_name); free(ctx->array_us_start); free(ctx); diff --git a/trace2/tr2_tls.h b/trace2/tr2_tls.h index 3bdbf4d2754b40..4a0969c014a0a9 100644 --- a/trace2/tr2_tls.h +++ b/trace2/tr2_tls.h @@ -20,6 +20,7 @@ struct tr2tls_thread_ctx { uint64_t *array_us_start; size_t alloc; size_t nr_open_regions; /* plays role of "nr" in ALLOC_GROW */ + size_t nr_skipped_regions; int thread_id; struct tr2_timer_block timer_block; struct tr2_counter_block counter_block; @@ -53,6 +54,12 @@ struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_base_name, */ struct tr2tls_thread_ctx *tr2tls_get_self(void); +/* + * Return true if the context is the non-allocating fallback used after an + * allocation failure. Callers must not modify a fallback context. + */ +int tr2tls_is_fallback(const struct tr2tls_thread_ctx *ctx); + /* * return true if the current thread is the main thread. */ diff --git a/trace2/tr2_tmr.c b/trace2/tr2_tmr.c index 038181ad9be05b..b3d26e2b316796 100644 --- a/trace2/tr2_tmr.c +++ b/trace2/tr2_tmr.c @@ -3,6 +3,7 @@ #include "trace2/tr2_tls.h" #include "trace2/tr2_tmr.h" #include "trace.h" +#include "banned-die.h" #define MY_MAX(a, b) ((a) > (b) ? (a) : (b)) #define MY_MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -37,8 +38,11 @@ static struct tr2_timer_metadata tr2_timer_metadata[TRACE2_NUMBER_OF_TIMERS] = { void tr2_start_timer(enum trace2_timer_id tid) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); - struct tr2_timer *t = &ctx->timer_block.timer[tid]; + struct tr2_timer *t; + if (tr2tls_is_fallback(ctx)) + return; + t = &ctx->timer_block.timer[tid]; t->recursion_count++; if (t->recursion_count > 1) return; /* ignore recursive starts */ @@ -49,10 +53,13 @@ void tr2_start_timer(enum trace2_timer_id tid) void tr2_stop_timer(enum trace2_timer_id tid) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); - struct tr2_timer *t = &ctx->timer_block.timer[tid]; + struct tr2_timer *t; uint64_t ns_now; uint64_t ns_interval; + if (tr2tls_is_fallback(ctx)) + return; + t = &ctx->timer_block.timer[tid]; assert(t->recursion_count > 0); t->recursion_count--; @@ -90,6 +97,8 @@ void tr2_update_final_timers(void) struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); enum trace2_timer_id tid; + if (tr2tls_is_fallback(ctx)) + return; if (!ctx->used_any_timer) return; @@ -136,6 +145,8 @@ void tr2_emit_per_thread_timers(tr2_tgt_evt_timer_t *fn_apply) struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); enum trace2_timer_id tid; + if (tr2tls_is_fallback(ctx)) + return; if (!ctx->used_any_per_thread_timer) return;