]> git.ipfire.org Git - thirdparty/git.git/commitdiff
promisor-remote: fix segfault when remote URL is missing
authorChristian Couder <christian.couder@gmail.com>
Tue, 18 Mar 2025 11:00:06 +0000 (12:00 +0100)
committerJunio C Hamano <gitster@pobox.com>
Tue, 18 Mar 2025 19:22:33 +0000 (12:22 -0700)
Using strvec_push() to push `NULL` into a 'strvec' results in a
segfault, because `xstrdup(NULL)` crashes.

So when an URL is missing from the config, let's not push the remote
name and URL into the 'strvec's.

While at it, let's also not push them in case the URL is empty. It's
just not worth the trouble and it's consistent with how Git otherwise
treats missing and empty URLs in the same way.

Note that in case of missing or empty URL, Git uses the remote name to
fetch, which can work if the remote is on the same filesystem. So
configurations where the client, server and remote are all on the same
filesystem may need URLs to be configured even if they are the same as
the remote names. But this is a rare case, and the work around is easy
enough.

We leave improving the strvec API and/or xstrdup() for a future
separate effort.

While at it, let's also use git_config_get_string_tmp() instead of
git_config_get_string() to simplify memory management.

Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
promisor-remote.c
t/t5710-promisor-remote-capability.sh

index 6a0a61382f40c01cf5645844e2f1e2d8bd0925cc..ba80240f120dc6543e389536fa2f7b55344580ea 100644 (file)
@@ -323,13 +323,15 @@ static void promisor_info_vecs(struct repository *repo,
        promisor_remote_init(repo);
 
        for (r = repo->promisor_remote_config->promisors; r; r = r->next) {
-               char *url;
+               const char *url;
                char *url_key = xstrfmt("remote.%s.url", r->name);
 
-               strvec_push(names, r->name);
-               strvec_push(urls, git_config_get_string(url_key, &url) ? NULL : url);
+               /* Only add remotes with a non empty URL */
+               if (!git_config_get_string_tmp(url_key, &url) && *url) {
+                       strvec_push(names, r->name);
+                       strvec_push(urls, url);
+               }
 
-               free(url);
                free(url_key);
        }
 }
@@ -356,10 +358,8 @@ char *promisor_remote_info(struct repository *repo)
                        strbuf_addch(&sb, ';');
                strbuf_addstr(&sb, "name=");
                strbuf_addstr_urlencode(&sb, names.v[i], allow_unsanitized);
-               if (urls.v[i]) {
-                       strbuf_addstr(&sb, ",url=");
-                       strbuf_addstr_urlencode(&sb, urls.v[i], allow_unsanitized);
-               }
+               strbuf_addstr(&sb, ",url=");
+               strbuf_addstr_urlencode(&sb, urls.v[i], allow_unsanitized);
        }
 
        strvec_clear(&names);
index e26a97f588fd5bda26a92996fb8d3355da6e50ac..b35b7742356fe1d39f4ae70d94ff3974a6bdcb5f 100755 (executable)
@@ -193,6 +193,25 @@ test_expect_success "clone with 'KnownName' and different remote names" '
        initialize_server 1 "$oid"
 '
 
+test_expect_success "clone with 'KnownName' and missing URL in the config" '
+       git -C server config promisor.advertise true &&
+       test_when_finished "rm -rf client" &&
+
+       # Clone from server to create a client
+       # Lazy fetching by the client from the LOP will fail because of the
+       # missing URL in the client config, so the server will have to lazy
+       # fetch from the LOP.
+       GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+               -c promisor.acceptfromserver=KnownName \
+               --no-local --filter="blob:limit=5k" server client &&
+
+       # Check that the largest object is not missing on the server
+       check_missing_objects server 0 "" &&
+
+       # Reinitialize server so that the largest object is missing again
+       initialize_server 1 "$oid"
+'
+
 test_expect_success "clone with promisor.acceptfromserver set to 'KnownUrl'" '
        git -C server config promisor.advertise true &&
        test_when_finished "rm -rf client" &&
@@ -228,6 +247,48 @@ test_expect_success "clone with 'KnownUrl' and different remote urls" '
        initialize_server 1 "$oid"
 '
 
+test_expect_success "clone with 'KnownUrl' and url not configured on the server" '
+       git -C server config promisor.advertise true &&
+       test_when_finished "rm -rf client" &&
+
+       test_when_finished "git -C server config set remote.lop.url \"file://$(pwd)/lop\"" &&
+       git -C server config unset remote.lop.url &&
+
+       # Clone from server to create a client
+       # It should fail because the client will reject the LOP as URLs are
+       # different, and the server cannot lazy fetch as the LOP URL is
+       # missing, so the remote name will be used instead which will fail.
+       test_must_fail env GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+               -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+               -c remote.lop.url="file://$(pwd)/lop" \
+               -c promisor.acceptfromserver=KnownUrl \
+               --no-local --filter="blob:limit=5k" server client &&
+
+       # Check that the largest object is still missing on the server
+       check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownUrl' and empty url, so not advertised" '
+       git -C server config promisor.advertise true &&
+       test_when_finished "rm -rf client" &&
+
+       test_when_finished "git -C server config set remote.lop.url \"file://$(pwd)/lop\"" &&
+       git -C server config set remote.lop.url "" &&
+
+       # Clone from server to create a client
+       # It should fail because the client will reject the LOP as an empty URL is
+       # not advertised, and the server cannot lazy fetch as the LOP URL is empty,
+       # so the remote name will be used instead which will fail.
+       test_must_fail env GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+               -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+               -c remote.lop.url="file://$(pwd)/lop" \
+               -c promisor.acceptfromserver=KnownUrl \
+               --no-local --filter="blob:limit=5k" server client &&
+
+       # Check that the largest object is still missing on the server
+       check_missing_objects server 1 "$oid"
+'
+
 test_expect_success "clone with promisor.advertise set to 'true' but don't delete the client" '
        git -C server config promisor.advertise true &&