hash-object: fix buffer reuse with --path in a subdirectory

The hash-object command uses prefix_filename() without duplicating its return value. Since that function returns a static buffer, the value is overwritten by subsequent calls. This can cause incorrect results when we use --path along with hashing a file by its relative path, both of which need to call prefix_filename(). We overwrite the filename computed for --path, effectively ignoring it. We can fix this by calling xstrdup on the return value. Note that we don't bother freeing the "vpath" instance, as it remains valid until the program exit. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Mar 20, 2017 at 21:20 UTC a1be47e4ca65718ec239e4b86a44e45220237aee
2 files changed +15 -2
builtin/hash-object.c
+5 -2
@@ -115,7 +115,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
115
116 prefix_length = prefix ? strlen(prefix) : 0;
117 if (vpath && prefix)
118 - vpath = prefix_filename(prefix, prefix_length, vpath);
118 + vpath = xstrdup(prefix_filename(prefix, prefix_length, vpath));
119
120 git_config(git_default_config, NULL);
121
@@ -144,11 +144,14 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
144
145 for (i = 0 ; i < argc; i++) {
146 const char *arg = argv[i];
147 + char *to_free = NULL;
148
149 if (0 <= prefix_length)
149 - arg = prefix_filename(prefix, prefix_length, arg);
150 + arg = to_free =
151 + xstrdup(prefix_filename(prefix, prefix_length, arg));
152 hash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,
153 flags, literally);
154 + free(to_free);
155 }
156
157 if (stdin_paths)
t/t1007-hash-object.sh
+10
@@ -134,6 +134,16 @@ test_expect_success 'gitattributes also work in a subdirectory' '
134 )
135 '
136
137 +test_expect_success '--path works in a subdirectory' '
138 + (
139 + cd subdir &&
140 + path1_sha=$(git hash-object --path=../file1 ../file0) &&
141 + path0_sha=$(git hash-object --path=../file0 ../file1) &&
142 + test "$file0_sha" = "$path0_sha" &&
143 + test "$file1_sha" = "$path1_sha"
144 + )
145 +'
146 +
147 test_expect_success 'check that --no-filters option works' '
148 nofilters_file1=$(git hash-object --no-filters file1) &&
149 test "$file0_sha" = "$nofilters_file1" &&