Merge branch 'tr/unpack-entry-use-after-free-fix'
authorJunio C Hamano <gitster@pobox.com>
Fri, 3 May 2013 22:18:04 +0000 (15:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 3 May 2013 22:18:04 +0000 (15:18 -0700)
* tr/unpack-entry-use-after-free-fix:
unpack_entry: avoid freeing objects in base cache

1  2 
sha1_file.c
diff --combined sha1_file.c
index 64228a26d0be873040ebde37e95e62d20f5ba957,a016a699f92b0f9ba9b1b8e408ed4b9bc27b4eef..67e815b2db00e4d406693f82e96b11bb701509a5
@@@ -21,7 -21,6 +21,7 @@@
  #include "sha1-lookup.h"
  #include "bulk-checkin.h"
  #include "streaming.h"
 +#include "dir.h"
  
  #ifndef O_NOATIME
  #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@@ -124,13 -123,8 +124,13 @@@ int safe_create_leading_directories(cha
                        }
                }
                else if (mkdir(path, 0777)) {
 -                      *pos = '/';
 -                      return -1;
 +                      if (errno == EEXIST &&
 +                          !stat(path, &st) && S_ISDIR(st.st_mode)) {
 +                              ; /* somebody created it since we checked */
 +                      } else {
 +                              *pos = '/';
 +                              return -1;
 +                      }
                }
                else if (adjust_shared_perm(path)) {
                        *pos = '/';
@@@ -1006,63 -1000,6 +1006,63 @@@ void install_packed_git(struct packed_g
        packed_git = pack;
  }
  
 +void (*report_garbage)(const char *desc, const char *path);
 +
 +static void report_helper(const struct string_list *list,
 +                        int seen_bits, int first, int last)
 +{
 +      const char *msg;
 +      switch (seen_bits) {
 +      case 0:
 +              msg = "no corresponding .idx nor .pack";
 +              break;
 +      case 1:
 +              msg = "no corresponding .idx";
 +              break;
 +      case 2:
 +              msg = "no corresponding .pack";
 +              break;
 +      default:
 +              return;
 +      }
 +      for (; first < last; first++)
 +              report_garbage(msg, list->items[first].string);
 +}
 +
 +static void report_pack_garbage(struct string_list *list)
 +{
 +      int i, baselen = -1, first = 0, seen_bits = 0;
 +
 +      if (!report_garbage)
 +              return;
 +
 +      sort_string_list(list);
 +
 +      for (i = 0; i < list->nr; i++) {
 +              const char *path = list->items[i].string;
 +              if (baselen != -1 &&
 +                  strncmp(path, list->items[first].string, baselen)) {
 +                      report_helper(list, seen_bits, first, i);
 +                      baselen = -1;
 +                      seen_bits = 0;
 +              }
 +              if (baselen == -1) {
 +                      const char *dot = strrchr(path, '.');
 +                      if (!dot) {
 +                              report_garbage("garbage found", path);
 +                              continue;
 +                      }
 +                      baselen = dot - path + 1;
 +                      first = i;
 +              }
 +              if (!strcmp(path + baselen, "pack"))
 +                      seen_bits |= 1;
 +              else if (!strcmp(path + baselen, "idx"))
 +                      seen_bits |= 2;
 +      }
 +      report_helper(list, seen_bits, first, list->nr);
 +}
 +
  static void prepare_packed_git_one(char *objdir, int local)
  {
        /* Ensure that this buffer is large enough so that we can
        int len;
        DIR *dir;
        struct dirent *de;
 +      struct string_list garbage = STRING_LIST_INIT_DUP;
  
        sprintf(path, "%s/pack", objdir);
        len = strlen(path);
                int namelen = strlen(de->d_name);
                struct packed_git *p;
  
 -              if (!has_extension(de->d_name, ".idx"))
 +              if (len + namelen + 1 > sizeof(path)) {
 +                      if (report_garbage) {
 +                              struct strbuf sb = STRBUF_INIT;
 +                              strbuf_addf(&sb, "%.*s/%s", len - 1, path, de->d_name);
 +                              report_garbage("path too long", sb.buf);
 +                              strbuf_release(&sb);
 +                      }
                        continue;
 +              }
  
 -              if (len + namelen + 1 > sizeof(path))
 +              if (is_dot_or_dotdot(de->d_name))
                        continue;
  
 -              /* Don't reopen a pack we already have. */
                strcpy(path + len, de->d_name);
 -              for (p = packed_git; p; p = p->next) {
 -                      if (!memcmp(path, p->pack_name, len + namelen - 4))
 -                              break;
 +
 +              if (has_extension(de->d_name, ".idx")) {
 +                      /* Don't reopen a pack we already have. */
 +                      for (p = packed_git; p; p = p->next) {
 +                              if (!memcmp(path, p->pack_name, len + namelen - 4))
 +                                      break;
 +                      }
 +                      if (p == NULL &&
 +                          /*
 +                           * See if it really is a valid .idx file with
 +                           * corresponding .pack file that we can map.
 +                           */
 +                          (p = add_packed_git(path, len + namelen, local)) != NULL)
 +                              install_packed_git(p);
                }
 -              if (p)
 -                      continue;
 -              /* See if it really is a valid .idx file with corresponding
 -               * .pack file that we can map.
 -               */
 -              p = add_packed_git(path, len + namelen, local);
 -              if (!p)
 +
 +              if (!report_garbage)
                        continue;
 -              install_packed_git(p);
 +
 +              if (has_extension(de->d_name, ".idx") ||
 +                  has_extension(de->d_name, ".pack") ||
 +                  has_extension(de->d_name, ".keep"))
 +                      string_list_append(&garbage, path);
 +              else
 +                      report_garbage("garbage found", path);
        }
        closedir(dir);
 +      report_pack_garbage(&garbage);
 +      string_list_clear(&garbage, 0);
  }
  
  static int sort_pack(const void *a_, const void *b_)
@@@ -1271,10 -1187,6 +1271,10 @@@ int check_sha1_signature(const unsigne
                char buf[1024 * 16];
                ssize_t readlen = read_istream(st, buf, sizeof(buf));
  
 +              if (readlen < 0) {
 +                      close_istream(st);
 +                      return -1;
 +              }
                if (!readlen)
                        break;
                git_SHA1_Update(&c, buf, readlen);
@@@ -2128,7 -2040,6 +2128,6 @@@ void *unpack_entry(struct packed_git *p
                        error("failed to unpack compressed delta "
                              "at offset %"PRIuMAX" from %s",
                              (uintmax_t)curpos, p->pack_name);
-                       free(base);
                        data = NULL;
                        continue;
                }