Merge branch 'bc/fetch-pack-clear-alternate-shallow'
authorJunio C Hamano <gitster@pobox.com>
Thu, 7 Feb 2019 06:05:30 +0000 (22:05 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 7 Feb 2019 06:05:30 +0000 (22:05 -0800)
"git fetch" over protocol v2 that needs to make a second connection
to backfill tags did not clear a variable that holds shallow
repository information correctly, leading to an access of freed
piece of memory.

* bc/fetch-pack-clear-alternate-shallow:
fetch-pack: clear alternate shallow in one more place
fetch-pack: clear alternate shallow when complete

1  2 
fetch-pack.c
diff --combined fetch-pack.c
index 08b3b356faeabdeeff90918142f7225ce5460e8c,a92621a388303cd5fa4a56e2b3a7cddc3d96e236..812be15d7e568522973227bb939343d94c008ceb
@@@ -135,42 -135,38 +135,42 @@@ enum ack_type 
        ACK_ready
  };
  
 -static void consume_shallow_list(struct fetch_pack_args *args, int fd)
 +static void consume_shallow_list(struct fetch_pack_args *args,
 +                               struct packet_reader *reader)
  {
        if (args->stateless_rpc && args->deepen) {
                /* If we sent a depth we will get back "duplicate"
                 * shallow and unshallow commands every time there
                 * is a block of have lines exchanged.
                 */
 -              char *line;
 -              while ((line = packet_read_line(fd, NULL))) {
 -                      if (starts_with(line, "shallow "))
 +              while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
 +                      if (starts_with(reader->line, "shallow "))
                                continue;
 -                      if (starts_with(line, "unshallow "))
 +                      if (starts_with(reader->line, "unshallow "))
                                continue;
                        die(_("git fetch-pack: expected shallow list"));
                }
 +              if (reader->status != PACKET_READ_FLUSH)
 +                      die(_("git fetch-pack: expected a flush packet after shallow list"));
        }
  }
  
 -static enum ack_type get_ack(int fd, struct object_id *result_oid)
 +static enum ack_type get_ack(struct packet_reader *reader,
 +                           struct object_id *result_oid)
  {
        int len;
 -      char *line = packet_read_line(fd, &len);
        const char *arg;
  
 -      if (!line)
 +      if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                die(_("git fetch-pack: expected ACK/NAK, got a flush packet"));
 -      if (!strcmp(line, "NAK"))
 +      len = reader->pktlen;
 +
 +      if (!strcmp(reader->line, "NAK"))
                return NAK;
 -      if (skip_prefix(line, "ACK ", &arg)) {
 +      if (skip_prefix(reader->line, "ACK ", &arg)) {
                if (!get_oid_hex(arg, result_oid)) {
                        arg += 40;
 -                      len -= arg - line;
 +                      len -= arg - reader->line;
                        if (len < 1)
                                return ACK;
                        if (strstr(arg, "continue"))
                        return ACK;
                }
        }
 -      if (skip_prefix(line, "ERR ", &arg))
 -              die(_("remote error: %s"), arg);
 -      die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line);
 +      die(_("git fetch-pack: expected ACK/NAK, got '%s'"), reader->line);
  }
  
  static void send_request(struct fetch_pack_args *args,
@@@ -250,15 -248,10 +250,15 @@@ static int find_common(struct fetch_neg
        int got_ready = 0;
        struct strbuf req_buf = STRBUF_INIT;
        size_t state_len = 0;
 +      struct packet_reader reader;
  
        if (args->stateless_rpc && multi_ack == 1)
                die(_("--stateless-rpc requires multi_ack_detailed"));
  
 +      packet_reader_init(&reader, fd[0], NULL, 0,
 +                         PACKET_READ_CHOMP_NEWLINE |
 +                         PACKET_READ_DIE_ON_ERR_PACKET);
 +
        if (!args->no_dependents) {
                mark_tips(negotiator, args->negotiation_tips);
                for_each_cached_alternate(negotiator, insert_one_alternate_object);
                        packet_buf_write(&req_buf, "deepen-not %s", s->string);
                }
        }
 -      if (server_supports_filtering && args->filter_options.choice)
 +      if (server_supports_filtering && args->filter_options.choice) {
 +              struct strbuf expanded_filter_spec = STRBUF_INIT;
 +              expand_list_objects_filter_spec(&args->filter_options,
 +                                              &expanded_filter_spec);
                packet_buf_write(&req_buf, "filter %s",
 -                               args->filter_options.filter_spec);
 +                               expanded_filter_spec.buf);
 +              strbuf_release(&expanded_filter_spec);
 +      }
        packet_buf_flush(&req_buf);
        state_len = req_buf.len;
  
        if (args->deepen) {
 -              char *line;
                const char *arg;
                struct object_id oid;
  
                send_request(args, fd[1], &req_buf);
 -              while ((line = packet_read_line(fd[0], NULL))) {
 -                      if (skip_prefix(line, "shallow ", &arg)) {
 +              while (packet_reader_read(&reader) == PACKET_READ_NORMAL) {
 +                      if (skip_prefix(reader.line, "shallow ", &arg)) {
                                if (get_oid_hex(arg, &oid))
 -                                      die(_("invalid shallow line: %s"), line);
 +                                      die(_("invalid shallow line: %s"), reader.line);
                                register_shallow(the_repository, &oid);
                                continue;
                        }
 -                      if (skip_prefix(line, "unshallow ", &arg)) {
 +                      if (skip_prefix(reader.line, "unshallow ", &arg)) {
                                if (get_oid_hex(arg, &oid))
 -                                      die(_("invalid unshallow line: %s"), line);
 +                                      die(_("invalid unshallow line: %s"), reader.line);
                                if (!lookup_object(the_repository, oid.hash))
 -                                      die(_("object not found: %s"), line);
 +                                      die(_("object not found: %s"), reader.line);
                                /* make sure that it is parsed as shallow */
                                if (!parse_object(the_repository, &oid))
 -                                      die(_("error in object: %s"), line);
 +                                      die(_("error in object: %s"), reader.line);
                                if (unregister_shallow(&oid))
 -                                      die(_("no shallow found: %s"), line);
 +                                      die(_("no shallow found: %s"), reader.line);
                                continue;
                        }
 -                      die(_("expected shallow/unshallow, got %s"), line);
 +                      die(_("expected shallow/unshallow, got %s"), reader.line);
                }
        } else if (!args->stateless_rpc)
                send_request(args, fd[1], &req_buf);
                        if (!args->stateless_rpc && count == INITIAL_FLUSH)
                                continue;
  
 -                      consume_shallow_list(args, fd[0]);
 +                      consume_shallow_list(args, &reader);
                        do {
 -                              ack = get_ack(fd[0], result_oid);
 +                              ack = get_ack(&reader, result_oid);
                                if (ack)
                                        print_verbose(args, _("got %s %d %s"), "ack",
                                                      ack, oid_to_hex(result_oid));
@@@ -480,9 -469,9 +480,9 @@@ done
        strbuf_release(&req_buf);
  
        if (!got_ready || !no_done)
 -              consume_shallow_list(args, fd[0]);
 +              consume_shallow_list(args, &reader);
        while (flushes || multi_ack) {
 -              int ack = get_ack(fd[0], result_oid);
 +              int ack = get_ack(&reader, result_oid);
                if (ack) {
                        print_verbose(args, _("got %s (%d) %s"), "ack",
                                      ack, oid_to_hex(result_oid));
@@@ -1097,8 -1086,7 +1097,8 @@@ static int add_haves(struct fetch_negot
  static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
                              const struct fetch_pack_args *args,
                              const struct ref *wants, struct oidset *common,
 -                            int *haves_to_send, int *in_vain)
 +                            int *haves_to_send, int *in_vain,
 +                            int sideband_all)
  {
        int ret = 0;
        struct strbuf req_buf = STRBUF_INIT;
                packet_buf_write(&req_buf, "include-tag");
        if (prefer_ofs_delta)
                packet_buf_write(&req_buf, "ofs-delta");
 +      if (sideband_all)
 +              packet_buf_write(&req_buf, "sideband-all");
  
        /* Add shallow-info and deepen request */
        if (server_supports_feature("fetch", "shallow", 0))
        /* Add filter */
        if (server_supports_feature("fetch", "filter", 0) &&
            args->filter_options.choice) {
 +              struct strbuf expanded_filter_spec = STRBUF_INIT;
                print_verbose(args, _("Server supports filter"));
 +              expand_list_objects_filter_spec(&args->filter_options,
 +                                              &expanded_filter_spec);
                packet_buf_write(&req_buf, "filter %s",
 -                               args->filter_options.filter_spec);
 +                               expanded_filter_spec.buf);
 +              strbuf_release(&expanded_filter_spec);
        } else if (args->filter_options.choice) {
                warning("filtering not recognized by server, ignoring");
        }
@@@ -1290,6 -1272,8 +1290,8 @@@ static void receive_shallow_info(struc
                setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
                                        NULL);
                args->deepen = 1;
+       } else {
+               alternate_shallow_file = NULL;
        }
  }
  
@@@ -1343,13 -1327,7 +1345,13 @@@ static struct ref *do_fetch_pack_v2(str
        struct fetch_negotiator negotiator;
        fetch_negotiator_init(&negotiator, negotiation_algorithm);
        packet_reader_init(&reader, fd[0], NULL, 0,
 -                         PACKET_READ_CHOMP_NEWLINE);
 +                         PACKET_READ_CHOMP_NEWLINE |
 +                         PACKET_READ_DIE_ON_ERR_PACKET);
 +      if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 1) &&
 +          server_supports_feature("fetch", "sideband-all", 0)) {
 +              reader.use_sideband = 1;
 +              reader.me = "fetch-pack";
 +      }
  
        while (state != FETCH_DONE) {
                switch (state) {
                case FETCH_SEND_REQUEST:
                        if (send_fetch_request(&negotiator, fd[1], args, ref,
                                               &common,
 -                                             &haves_to_send, &in_vain))
 +                                             &haves_to_send, &in_vain,
 +                                             reader.use_sideband))
                                state = FETCH_GET_PACK;
                        else
                                state = FETCH_PROCESS_ACKS;
@@@ -1514,6 -1491,7 +1516,7 @@@ static void update_shallow(struct fetch
                        rollback_lock_file(&shallow_lock);
                } else
                        commit_lock_file(&shallow_lock);
+               alternate_shallow_file = NULL;
                return;
        }
  
                                                &alternate_shallow_file,
                                                &extra);
                        commit_lock_file(&shallow_lock);
+                       alternate_shallow_file = NULL;
                }
                oid_array_clear(&extra);
                return;
                commit_lock_file(&shallow_lock);
                oid_array_clear(&extra);
                oid_array_clear(&ref);
+               alternate_shallow_file = NULL;
                return;
        }