cat-file: fix a common "struct object_context" memory leak

Fix a memory leak where "cat-file" will leak the "path" member. See
e5fba602e5 (textconv: support for cat_file, 2010-06-15) for the code
that introduced the offending get_oid_with_context() call (called
get_sha1_with_context() at the time).

As a result we can mark several tests as passing with SANITIZE=leak
using "TEST_PASSES_SANITIZE_LEAK=true".

As noted in dc944b65f1 (get_sha1_with_context: dynamically allocate
oc->path, 2017-05-19) callers must free the "path" member. That same
commit added the relevant free() to this function, but we weren't
catching cases where we'd return early.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Ævar Arnfjörð Bjarmason 2022-07-01 12:42:59 +02:00 committed by Junio C Hamano
parent 55916bba0f
commit 27472b5195
13 changed files with 37 additions and 14 deletions

View File

@ -71,6 +71,7 @@ static int stream_blob(const struct object_id *oid)
static int cat_one_file(int opt, const char *exp_type, const char *obj_name, static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
int unknown_type) int unknown_type)
{ {
int ret;
struct object_id oid; struct object_id oid;
enum object_type type; enum object_type type;
char *buf; char *buf;
@ -106,7 +107,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (sb.len) { if (sb.len) {
printf("%s\n", sb.buf); printf("%s\n", sb.buf);
strbuf_release(&sb); strbuf_release(&sb);
return 0; ret = 0;
goto cleanup;
} }
break; break;
@ -115,7 +117,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0) if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0)
die("git cat-file: could not get object info"); die("git cat-file: could not get object info");
printf("%"PRIuMAX"\n", (uintmax_t)size); printf("%"PRIuMAX"\n", (uintmax_t)size);
return 0; ret = 0;
goto cleanup;
case 'e': case 'e':
return !has_object_file(&oid); return !has_object_file(&oid);
@ -123,8 +126,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
case 'w': case 'w':
if (filter_object(path, obj_context.mode, if (filter_object(path, obj_context.mode,
&oid, &buf, &size)) &oid, &buf, &size)) {
return -1; ret = -1;
goto cleanup;
}
break; break;
case 'c': case 'c':
@ -143,11 +148,14 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
const char *ls_args[3] = { NULL }; const char *ls_args[3] = { NULL };
ls_args[0] = "ls-tree"; ls_args[0] = "ls-tree";
ls_args[1] = obj_name; ls_args[1] = obj_name;
return cmd_ls_tree(2, ls_args, NULL); ret = cmd_ls_tree(2, ls_args, NULL);
goto cleanup;
} }
if (type == OBJ_BLOB) if (type == OBJ_BLOB) {
return stream_blob(&oid); ret = stream_blob(&oid);
goto cleanup;
}
buf = read_object_file(&oid, &type, &size); buf = read_object_file(&oid, &type, &size);
if (!buf) if (!buf)
die("Cannot read object %s", obj_name); die("Cannot read object %s", obj_name);
@ -172,8 +180,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
} else } else
oidcpy(&blob_oid, &oid); oidcpy(&blob_oid, &oid);
if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB) if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB) {
return stream_blob(&blob_oid); ret = stream_blob(&blob_oid);
goto cleanup;
}
/* /*
* we attempted to dereference a tag to a blob * we attempted to dereference a tag to a blob
* and failed; there may be new dereference * and failed; there may be new dereference
@ -193,9 +203,11 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
die("git cat-file %s: bad file", obj_name); die("git cat-file %s: bad file", obj_name);
write_or_die(1, buf, size); write_or_die(1, buf, size);
ret = 0;
cleanup:
free(buf); free(buf);
free(obj_context.path); free(obj_context.path);
return 0; return ret;
} }
struct expand_data { struct expand_data {

View File

@ -5,6 +5,7 @@ test_description='working-tree-encoding conversion via gitattributes'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
. "$TEST_DIRECTORY/lib-encoding.sh" . "$TEST_DIRECTORY/lib-encoding.sh"

View File

@ -1,6 +1,8 @@
#!/bin/sh #!/bin/sh
test_description='test conversion filters on large files' test_description='test conversion filters on large files'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
set_attr() { set_attr() {

View File

@ -5,6 +5,7 @@ test_description='Test notes trees that also contain non-notes'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
number_of_commits=100 number_of_commits=100

View File

@ -1,6 +1,8 @@
#!/bin/sh #!/bin/sh
test_description='test unique sha1 abbreviation on "index from..to" line' test_description='test unique sha1 abbreviation on "index from..to" line'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
test_expect_success 'setup' ' test_expect_success 'setup' '

View File

@ -2,6 +2,7 @@
test_description='git apply of i-t-a file' test_description='git apply of i-t-a file'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
test_expect_success setup ' test_expect_success setup '

View File

@ -49,10 +49,10 @@ Then no matter which order we start looking at the packs in, we know that we
will always find a delta for "file", because its lookup will always come will always find a delta for "file", because its lookup will always come
immediately after the lookup for "dummy". immediately after the lookup for "dummy".
' '
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
# Create a pack containing the tree $1 and blob $1:file, with # Create a pack containing the tree $1 and blob $1:file, with
# the latter stored as a delta against $2:file. # the latter stored as a delta against $2:file.
# #

View File

@ -6,6 +6,7 @@ test_description="recursive merge corner cases w/ renames but not criss-crosses"
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh . "$TEST_DIRECTORY"/lib-merge.sh

View File

@ -1,6 +1,8 @@
#!/bin/sh #!/bin/sh
test_description='git cat-file textconv support' test_description='git cat-file textconv support'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
cat >helper <<'EOF' cat >helper <<'EOF'

View File

@ -1,6 +1,8 @@
#!/bin/sh #!/bin/sh
test_description='git cat-file filters support' test_description='git cat-file filters support'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
test_expect_success 'setup ' ' test_expect_success 'setup ' '

View File

@ -5,7 +5,6 @@
test_description='git svn fetching' test_description='git svn fetching'
TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh . ./lib-git-svn.sh
test_expect_success 'initialize repo' ' test_expect_success 'initialize repo' '

View File

@ -2,7 +2,6 @@
test_description='test that git handles an svn repository with empty symlinks' test_description='test that git handles an svn repository with empty symlinks'
TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh . ./lib-git-svn.sh
test_expect_success 'load svn dumpfile' ' test_expect_success 'load svn dumpfile' '
svnadmin load "$rawsvnrepo" <<EOF svnadmin load "$rawsvnrepo" <<EOF

View File

@ -7,6 +7,7 @@ test_description='test git fast-import of notes objects'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh