]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'rs/name-rev-fix-free-after-use'
authorJunio C Hamano <gitster@pobox.com>
Thu, 28 Apr 2022 17:46:04 +0000 (10:46 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 28 Apr 2022 17:46:04 +0000 (10:46 -0700)
Regression fix for 2.36 where "git name-rev" started to sometimes
reference strings after they are freed.

* rs/name-rev-fix-free-after-use:
  Revert "name-rev: release unused name strings"

1  2 
builtin/name-rev.c

diff --combined builtin/name-rev.c
index c59b5699fe80ece6bab54173222a265e6ee5f6d9,228eab439829589c5dbe64b9b6264f6d3f09f90d..02ea9d16330f650502e1722a7b5fb95fa7f52cb1
@@@ -9,7 -9,6 +9,7 @@@
  #include "prio-queue.h"
  #include "hash-lookup.h"
  #include "commit-slab.h"
 +#include "commit-graph.h"
  
  /*
   * One day.  See the 'name a rev shortly after epoch' test in t6120 when
@@@ -18,7 -17,7 +18,7 @@@
  #define CUTOFF_DATE_SLOP 86400
  
  struct rev_name {
-       char *tip_name;
+       const char *tip_name;
        timestamp_t taggerdate;
        int generation;
        int distance;
  
  define_commit_slab(commit_rev_name, struct rev_name);
  
 +static timestamp_t generation_cutoff = GENERATION_NUMBER_INFINITY;
  static timestamp_t cutoff = TIME_MAX;
  static struct commit_rev_name rev_names;
  
 +/* Disable the cutoff checks entirely */
 +static void disable_cutoff(void)
 +{
 +      generation_cutoff = 0;
 +      cutoff = 0;
 +}
 +
 +/* Cutoff searching any commits older than this one */
 +static void set_commit_cutoff(struct commit *commit)
 +{
 +
 +      if (cutoff > commit->date)
 +              cutoff = commit->date;
 +
 +      if (generation_cutoff) {
 +              timestamp_t generation = commit_graph_generation(commit);
 +
 +              if (generation_cutoff > generation)
 +                      generation_cutoff = generation;
 +      }
 +}
 +
 +/* adjust the commit date cutoff with a slop to allow for slightly incorrect
 + * commit timestamps in case of clock skew.
 + */
 +static void adjust_cutoff_timestamp_for_slop(void)
 +{
 +      if (cutoff) {
 +              /* check for undeflow */
 +              if (cutoff > TIME_MIN + CUTOFF_DATE_SLOP)
 +                      cutoff = cutoff - CUTOFF_DATE_SLOP;
 +              else
 +                      cutoff = TIME_MIN;
 +      }
 +}
 +
 +/* Check if a commit is before the cutoff. Prioritize generation numbers
 + * first, but use the commit timestamp if we lack generation data.
 + */
 +static int commit_is_before_cutoff(struct commit *commit)
 +{
 +      if (generation_cutoff < GENERATION_NUMBER_INFINITY)
 +              return generation_cutoff &&
 +                      commit_graph_generation(commit) < generation_cutoff;
 +
 +      return commit->date < cutoff;
 +}
 +
  /* How many generations are maximally preferred over _one_ merge traversal? */
  #define MERGE_TRAVERSAL_WEIGHT 65535
  
  static int is_valid_rev_name(const struct rev_name *name)
  {
-       return name && (name->generation || name->tip_name);
+       return name && name->tip_name;
  }
  
  static struct rev_name *get_commit_rev_name(const struct commit *commit)
@@@ -146,20 -96,9 +146,9 @@@ static struct rev_name *create_or_updat
  {
        struct rev_name *name = commit_rev_name_at(&rev_names, commit);
  
-       if (is_valid_rev_name(name)) {
-               if (!is_better_name(name, taggerdate, generation, distance, from_tag))
-                       return NULL;
-               /*
-                * This string might still be shared with ancestors
-                * (generation > 0).  We can release it here regardless,
-                * because the new name that has just won will be better
-                * for them as well, so name_rev() will replace these
-                * stale pointers when it processes the parents.
-                */
-               if (!name->generation)
-                       free(name->tip_name);
-       }
+       if (is_valid_rev_name(name) &&
+           !is_better_name(name, taggerdate, generation, distance, from_tag))
+               return NULL;
  
        name->taggerdate = taggerdate;
        name->generation = generation;
@@@ -201,7 -140,7 +190,7 @@@ static void name_rev(struct commit *sta
        struct rev_name *start_name;
  
        parse_commit(start_commit);
 -      if (start_commit->date < cutoff)
 +      if (commit_is_before_cutoff(start_commit))
                return;
  
        start_name = create_or_update_name(start_commit, taggerdate, 0, 0,
                        int generation, distance;
  
                        parse_commit(parent);
 -                      if (parent->date < cutoff)
 +                      if (commit_is_before_cutoff(parent))
                                continue;
  
                        if (parent_number > 1) {
@@@ -523,7 -462,7 +512,7 @@@ static void show_name(const struct obje
  static char const * const name_rev_usage[] = {
        N_("git name-rev [<options>] <commit>..."),
        N_("git name-rev [<options>] --all"),
 -      N_("git name-rev [<options>] --stdin"),
 +      N_("git name-rev [<options>] --annotate-stdin"),
        NULL
  };
  
@@@ -577,7 -516,7 +566,7 @@@ static void name_rev_line(char *p, stru
  int cmd_name_rev(int argc, const char **argv, const char *prefix)
  {
        struct object_array revs = OBJECT_ARRAY_INIT;
 -      int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
 +      int all = 0, annotate_stdin = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
        struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
        struct option opts[] = {
                OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
                                   N_("ignore refs matching <pattern>")),
                OPT_GROUP(""),
                OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
 -              OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")),
 +              OPT_BOOL(0, "stdin", &transform_stdin, N_("deprecated: use annotate-stdin instead")),
 +              OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
                OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
                OPT_BOOL(0, "always",     &always,
                           N_("show abbreviated commit object as fallback")),
        init_commit_rev_name(&rev_names);
        git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
 -      if (all + transform_stdin + !!argc > 1) {
 +
 +      if (transform_stdin) {
 +              warning("--stdin is deprecated. Please use --annotate-stdin instead, "
 +                                      "which is functionally equivalent.\n"
 +                                      "This option will be removed in a future release.");
 +              annotate_stdin = 1;
 +      }
 +
 +      if (all + annotate_stdin + !!argc > 1) {
                error("Specify either a list, or --all, not both!");
                usage_with_options(name_rev_usage, opts);
        }
 -      if (all || transform_stdin)
 -              cutoff = 0;
 +      if (all || annotate_stdin)
 +              disable_cutoff();
  
        for (; argc; argc--, argv++) {
                struct object_id oid;
                        continue;
                }
  
 -              if (commit) {
 -                      if (cutoff > commit->date)
 -                              cutoff = commit->date;
 -              }
 +              if (commit)
 +                      set_commit_cutoff(commit);
  
                if (peel_tag) {
                        if (!commit) {
                add_object_array(object, *argv, &revs);
        }
  
 -      if (cutoff) {
 -              /* check for undeflow */
 -              if (cutoff > TIME_MIN + CUTOFF_DATE_SLOP)
 -                      cutoff = cutoff - CUTOFF_DATE_SLOP;
 -              else
 -                      cutoff = TIME_MIN;
 -      }
 +      adjust_cutoff_timestamp_for_slop();
 +
        for_each_ref(name_ref, &data);
        name_tips();
  
 -      if (transform_stdin) {
 -              char buffer[2048];
 +      if (annotate_stdin) {
 +              struct strbuf sb = STRBUF_INIT;
  
 -              while (!feof(stdin)) {
 -                      char *p = fgets(buffer, sizeof(buffer), stdin);
 -                      if (!p)
 -                              break;
 -                      name_rev_line(p, &data);
 +              while (strbuf_getline(&sb, stdin) != EOF) {
 +                      strbuf_addch(&sb, '\n');
 +                      name_rev_line(sb.buf, &data);
                }
 +              strbuf_release(&sb);
        } else if (all) {
                int i, max;