Merge branch 'jk/detect-truncated-zlib-input'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Nov 2018 13:37:17 +0000 (22:37 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Nov 2018 13:37:17 +0000 (22:37 +0900)
A regression in Git 2.12 era made "git fsck" fall into an infinite
loop while processing truncated loose objects.

* jk/detect-truncated-zlib-input:
cat-file: handle streaming failures consistently
check_stream_sha1(): handle input underflow
t1450: check large blob in trailing-garbage test

1  2 
builtin/cat-file.c
sha1-file.c
diff --combined builtin/cat-file.c
index 8d97c84725cd0f7f5347e8ca808072bd227d4d1c,0520cecc9a1d963841b384de6173d752e6d78fb7..0d403eb77d37d3b409d7d8ef030c12b91fe63da6
@@@ -50,6 -50,13 +50,13 @@@ static int filter_object(const char *pa
        return 0;
  }
  
+ static int stream_blob(const struct object_id *oid)
+ {
+       if (stream_blob_to_fd(1, oid, NULL, 0))
+               die("unable to stream %s to stdout", oid_to_hex(oid));
+       return 0;
+ }
  static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                        int unknown_type)
  {
                        die("git cat-file --textconv %s: <object> must be <sha1:path>",
                            obj_name);
  
 -              if (textconv_object(path, obj_context.mode, &oid, 1, &buf, &size))
 +              if (textconv_object(the_repository, path, obj_context.mode,
 +                                  &oid, 1, &buf, &size))
                        break;
                /* else fallthrough */
  
                }
  
                if (type == OBJ_BLOB)
-                       return stream_blob_to_fd(1, &oid, NULL, 0);
+                       return stream_blob(&oid);
                buf = read_object_file(&oid, &type, &size);
                if (!buf)
                        die("Cannot read object %s", obj_name);
                                oidcpy(&blob_oid, &oid);
  
                        if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB)
-                               return stream_blob_to_fd(1, &blob_oid, NULL, 0);
+                               return stream_blob(&blob_oid);
                        /*
                         * we attempted to dereference a tag to a blob
                         * and failed; there may be new dereference
@@@ -306,8 -312,7 +313,8 @@@ static void print_object_or_die(struct 
                                            oid_to_hex(oid), data->rest);
                        } else if (opt->cmdmode == 'c') {
                                enum object_type type;
 -                              if (!textconv_object(data->rest, 0100644, oid,
 +                              if (!textconv_object(the_repository,
 +                                                   data->rest, 0100644, oid,
                                                     1, &contents, &size))
                                        contents = read_object_file(oid,
                                                                    &type,
                                BUG("invalid cmdmode: %c", opt->cmdmode);
                        batch_write(opt, contents, size);
                        free(contents);
-               } else if (stream_blob_to_fd(1, oid, NULL, 0) < 0)
-                       die("unable to stream %s to stdout", oid_to_hex(oid));
+               } else {
+                       stream_blob(oid);
+               }
        }
        else {
                enum object_type type;
diff --combined sha1-file.c
index dd0b6aa873a95433906b52abf605a43acc924d4a,6d6c3af6598c33623d10a930a39afaf9de74db1a..2daf7d9935d4aea4db13067f1134bc851c8ab166
@@@ -149,10 -149,10 +149,10 @@@ static struct cached_object *find_cache
        struct cached_object *co = cached_objects;
  
        for (i = 0; i < cached_object_nr; i++, co++) {
 -              if (!oidcmp(&co->oid, oid))
 +              if (oideq(&co->oid, oid))
                        return co;
        }
 -      if (!oidcmp(oid, the_hash_algo->empty_tree))
 +      if (oideq(oid, the_hash_algo->empty_tree))
                return &empty_tree;
        return NULL;
  }
@@@ -825,7 -825,7 +825,7 @@@ int check_object_signature(const struc
  
        if (map) {
                hash_object_file(map, size, type, &real_oid);
 -              return oidcmp(oid, &real_oid) ? -1 : 0;
 +              return !oideq(oid, &real_oid) ? -1 : 0;
        }
  
        st = open_istream(oid, &obj_type, &size, NULL);
        }
        the_hash_algo->final_fn(real_oid.hash, &c);
        close_istream(st);
 -      return oidcmp(oid, &real_oid) ? -1 : 0;
 +      return !oideq(oid, &real_oid) ? -1 : 0;
  }
  
  int git_open_cloexec(const char *name, int flags)
@@@ -1317,7 -1317,7 +1317,7 @@@ int oid_object_info_extended(struct rep
                         * TODO Pass a repository struct through fetch_object,
                         * such that arbitrary repositories work.
                         */
 -                      fetch_object(repository_format_partial_clone, real->hash);
 +                      fetch_objects(repository_format_partial_clone, real, 1);
                        already_retried = 1;
                        continue;
                }
@@@ -1671,7 -1671,7 +1671,7 @@@ static int write_loose_object(const str
                die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid),
                    ret);
        the_hash_algo->final_fn(parano_oid.hash, &c);
 -      if (oidcmp(oid, &parano_oid) != 0)
 +      if (!oideq(oid, &parano_oid))
                die(_("confused by unstable object source data for %s"),
                    oid_to_hex(oid));
  
@@@ -1813,8 -1813,7 +1813,8 @@@ static void check_tag(const void *buf, 
                die(_("corrupt tag"));
  }
  
 -static int index_mem(struct object_id *oid, void *buf, size_t size,
 +static int index_mem(struct index_state *istate,
 +                   struct object_id *oid, void *buf, size_t size,
                     enum object_type type,
                     const char *path, unsigned flags)
  {
         */
        if ((type == OBJ_BLOB) && path) {
                struct strbuf nbuf = STRBUF_INIT;
 -              if (convert_to_git(&the_index, path, buf, size, &nbuf,
 +              if (convert_to_git(istate, path, buf, size, &nbuf,
                                   get_conv_flags(flags))) {
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
        return ret;
  }
  
 -static int index_stream_convert_blob(struct object_id *oid, int fd,
 -                                   const char *path, unsigned flags)
 +static int index_stream_convert_blob(struct index_state *istate,
 +                                   struct object_id *oid,
 +                                   int fd,
 +                                   const char *path,
 +                                   unsigned flags)
  {
        int ret;
        const int write_object = flags & HASH_WRITE_OBJECT;
        struct strbuf sbuf = STRBUF_INIT;
  
        assert(path);
 -      assert(would_convert_to_git_filter_fd(&the_index, path));
 +      assert(would_convert_to_git_filter_fd(istate, path));
  
 -      convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
 +      convert_to_git_filter_fd(istate, path, fd, &sbuf,
                                 get_conv_flags(flags));
  
        if (write_object)
        return ret;
  }
  
 -static int index_pipe(struct object_id *oid, int fd, enum object_type type,
 +static int index_pipe(struct index_state *istate, struct object_id *oid,
 +                    int fd, enum object_type type,
                      const char *path, unsigned flags)
  {
        struct strbuf sbuf = STRBUF_INIT;
        int ret;
  
        if (strbuf_read(&sbuf, fd, 4096) >= 0)
 -              ret = index_mem(oid, sbuf.buf, sbuf.len, type, path, flags);
 +              ret = index_mem(istate, oid, sbuf.buf, sbuf.len, type, path, flags);
        else
                ret = -1;
        strbuf_release(&sbuf);
  
  #define SMALL_FILE_SIZE (32*1024)
  
 -static int index_core(struct object_id *oid, int fd, size_t size,
 +static int index_core(struct index_state *istate,
 +                    struct object_id *oid, int fd, size_t size,
                      enum object_type type, const char *path,
                      unsigned flags)
  {
        int ret;
  
        if (!size) {
 -              ret = index_mem(oid, "", size, type, path, flags);
 +              ret = index_mem(istate, oid, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                ssize_t read_result = read_in_full(fd, buf, size);
                        ret = error(_("short read while indexing %s"),
                                    path ? path : "<unknown>");
                else
 -                      ret = index_mem(oid, buf, size, type, path, flags);
 +                      ret = index_mem(istate, oid, buf, size, type, path, flags);
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 -              ret = index_mem(oid, buf, size, type, path, flags);
 +              ret = index_mem(istate, oid, buf, size, type, path, flags);
                munmap(buf, size);
        }
        return ret;
@@@ -1947,8 -1941,7 +1947,8 @@@ static int index_stream(struct object_i
        return index_bulk_checkin(oid, fd, size, type, path, flags);
  }
  
 -int index_fd(struct object_id *oid, int fd, struct stat *st,
 +int index_fd(struct index_state *istate, struct object_id *oid,
 +           int fd, struct stat *st,
             enum object_type type, const char *path, unsigned flags)
  {
        int ret;
         * Call xsize_t() only when needed to avoid potentially unnecessary
         * die() for large files.
         */
 -      if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(&the_index, path))
 -              ret = index_stream_convert_blob(oid, fd, path, flags);
 +      if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(istate, path))
 +              ret = index_stream_convert_blob(istate, oid, fd, path, flags);
        else if (!S_ISREG(st->st_mode))
 -              ret = index_pipe(oid, fd, type, path, flags);
 +              ret = index_pipe(istate, oid, fd, type, path, flags);
        else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
 -               (path && would_convert_to_git(&the_index, path)))
 -              ret = index_core(oid, fd, xsize_t(st->st_size), type, path,
 -                               flags);
 +               (path && would_convert_to_git(istate, path)))
 +              ret = index_core(istate, oid, fd, xsize_t(st->st_size),
 +                               type, path, flags);
        else
                ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
                                   flags);
        return ret;
  }
  
 -int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags)
 +int index_path(struct index_state *istate, struct object_id *oid,
 +             const char *path, struct stat *st, unsigned flags)
  {
        int fd;
        struct strbuf sb = STRBUF_INIT;
                fd = open(path, O_RDONLY);
                if (fd < 0)
                        return error_errno("open(\"%s\")", path);
 -              if (index_fd(oid, fd, st, OBJ_BLOB, path, flags) < 0)
 +              if (index_fd(istate, oid, fd, st, OBJ_BLOB, path, flags) < 0)
                        return error(_("%s: failed to insert into database"),
                                     path);
                break;
@@@ -2199,7 -2191,8 +2199,8 @@@ static int check_stream_sha1(git_zstrea
         * see the comment in unpack_sha1_rest for details.
         */
        while (total_read <= size &&
-              (status == Z_OK || status == Z_BUF_ERROR)) {
+              (status == Z_OK ||
+               (status == Z_BUF_ERROR && !stream->avail_out))) {
                stream->next_out = buf;
                stream->avail_out = sizeof(buf);
                if (size - total_read < stream->avail_out)
        }
  
        the_hash_algo->final_fn(real_sha1, &c);
 -      if (hashcmp(expected_sha1, real_sha1)) {
 +      if (!hasheq(expected_sha1, real_sha1)) {
                error(_("sha1 mismatch for %s (expected %s)"), path,
                      sha1_to_hex(expected_sha1));
                return -1;