[PATCH v2 00/25] Address a couple of issues identified by Coverity
- From
Johannes Schindelin <johannes.schindelin@gmx.de>
- Date
- Apr 28, 2017, 13:49 UTC
- Message-ID
- <cover.1493387231.git.johannes.schindelin@gmx.de>
- In-Reply-To
- <cover.1493237937.git.johannes.schindelin@gmx.de>
I recently registered the git-for-windows fork with Coverity to ensure that even the Windows-specific patches get some static analysis love.
While at it, I squashed a couple of obvious issues in the part that is not Windows-specific.
Note: while this patch series squashes some of those issues, the remaining issues are not necessarily all false positives (e.g. Coverity getting fooled by our FLEX_ARRAY trick into believing that the last member of the struct is indeed a 0 or 1 size array) or intentional (e.g. builtins not releasing memory because exit() is called right after returning from the function that leaks memory).
Notable examples of the remaining issues are:
- a couple of callers of shorten_unambiguous_ref() assume that they do not have to release the memory returned from that function, often assigning the pointer to a `const` variable that typically does not hold a pointer that needs to be free()d. My hunch is that we will want to convert that function to have a fixed number of static buffers and use those in a round robin fashion à la sha1_to_hex().
- pack-redundant.c seems to have hard-to-reason-about code paths that may eventually leak memory. Essentially, the custody of the allocated memory is not clear at all.
- fast-import.c calls strbuf_detach() on the command_buf without any obvious rationale. Turns out that *some* code paths assign command_buf.buf to a `recent_command` which releases the buffer later. However, from a cursory look it appears to me as if there are some other code paths that skip that assignment, effectively causing a memory leak once strbuf_detach() is called.
Sadly, I lack the time to pursue those remaining issues further.
Changes since v1:
- clarified in the commit messages for the setup_*git_dir() functions that we are not really plugging a memory leak, but marking singletons as `static` to help Coverity not to complain about this again
- dropped the patch to http-backend, as it is supposedly a one-shot program using exit() as "garbage collector".
- the difftool patch does a more thorough job of fixing leaks now
- reworded the commit subject of the mailinfo/mailsplit patch to sport a prefix indicating the overall area of the fix
- changed the mailinfo/mailsplit patch to *really* handle EOF correctly
- simplified the patch to get_mail_commit_oid(), and now it even returns the correct error value!
- adjusted the comment in builtin/checkout.c that talked about leaking the cache_entry (which is not leaked anymore)
- add forgotten free(buf) in fast-export.c in an early return
- line-log data is now released properly
- a couple more memory leaks in add_reflog_for_walk() have been found and addressed (this one *really* needs a couple more eyeballs, though, it is just a much bigger change than I originally anticipated)
Johannes Schindelin (25): mingw: avoid memory leak when splitting PATH winansi: avoid use of uninitialized value winansi: avoid buffer overrun add_commit_patch_id(): avoid allocating memory unnecessarily git_config_rename_section_in_file(): avoid resource leak get_mail_commit_oid(): avoid resource leak difftool: address a couple of resource/memory leaks status: close file descriptor after reading git-rebase-todo mailinfo & mailsplit: check for EOF while parsing cat-file: fix memory leak checkout: fix memory leak split_commit_in_progress(): fix memory leak setup_bare_git_dir(): help static analysis setup_discovered_git_dir(): help static analysis pack-redundant: plug memory leak mktree: plug memory leaks reported by Coverity fast-export: avoid leaking memory in handle_tag() receive-pack: plug memory leak in update() line-log: avoid memory leak shallow: avoid memory leak add_reflog_for_walk: avoid memory leak remote: plug memory leak in match_explicit() name-rev: avoid leaking memory in the `deref` case show_worktree(): plug memory leak submodule_uses_worktrees(): plug memory leak
builtin/am.c | 15 ++++++--------- builtin/cat-file.c | 1 + builtin/checkout.c | 17 +++++++++-------- builtin/difftool.c | 33 +++++++++++++++++++++++---------- builtin/fast-export.c | 2 ++ builtin/mailsplit.c | 3 ++- builtin/mktree.c | 5 +++-- builtin/name-rev.c | 7 +++++-- builtin/pack-redundant.c | 1 + builtin/receive-pack.c | 4 +++- builtin/worktree.c | 8 +++++--- compat/mingw.c | 4 +++- compat/winansi.c | 7 +++++++ config.c | 5 ++++- line-log.c | 1 + mailinfo.c | 15 +++++++++++---- patch-ids.c | 3 ++- reflog-walk.c | 20 +++++++++++++++++--- remote.c | 5 +++-- setup.c | 11 ++++++++--- shallow.c | 8 ++++++-- worktree.c | 2 +- wt-status.c | 8 +++++++- 23 files changed, 130 insertions(+), 55 deletions(-)
base-commit: 027a3b943b444a3e3a76f9a89803fc10245b858f Published-As: https://github.com/dscho/git/releases/tag/coverity-v2 Fetch-It-Via: git fetch https://github.com/dscho/git coverity-v2
Interdiff vs v1:
diff --git a/builtin/am.c b/builtin/am.c
index 067dd4fc20d..9c5c2c778e2 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1353,18 +1353,14 @@ static int get_mail_commit_oid(struct object_id *commit_id, const char *mail)
const char *x;
int ret = 0;
- if (strbuf_getline_lf(&sb, fp))
- ret = -1;
-
- if (!ret && !skip_prefix(sb.buf, "From ", &x))
- ret = -1;
-
- if (!ret && get_oid_hex(x, commit_id) < 0)
+ if (strbuf_getline_lf(&sb, fp) ||
+ !skip_prefix(sb.buf, "From ", &x) ||
+ get_oid_hex(x, commit_id) < 0)
ret = -1;
strbuf_release(&sb);
fclose(fp);
- return 0;
+ return ret;
}
/**
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 98f98256608..5faea3a05fa 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -235,14 +235,14 @@ static int checkout_merged(int pos, const struct checkout *state)
/*
* NEEDSWORK:
* There is absolutely no reason to write this as a blob object
- * and create a phony cache entry just to leak. This hack is
- * primarily to get to the write_entry() machinery that massages
- * the contents to work-tree format and writes out which only
- * allows it for a cache entry. The code in write_entry() needs
- * to be refactored to allow us to feed a <buffer, size, mode>
- * instead of a cache entry. Such a refactoring would help
- * merge_recursive as well (it also writes the merge result to the
- * object database even when it may contain conflicts).
+ * and create a phony cache entry. This hack is primarily to get
+ * to the write_entry() machinery that massages the contents to
+ * work-tree format and writes out which only allows it for a
+ * cache entry. The code in write_entry() needs to be refactored
+ * to allow us to feed a <buffer, size, mode> instead of a cache
+ * entry. Such a refactoring would help merge_recursive as well
+ * (it also writes the merge result to the object database even
+ * when it may contain conflicts).
*/
if (write_sha1_file(result_buf.ptr, result_buf.size,
blob_type, oid.hash))
diff --git a/builtin/difftool.c b/builtin/difftool.c
index a4f1d117ef6..b9a892f2693 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -440,8 +440,10 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
}
if (lmode && status != 'C') {
- if (checkout_path(lmode, &loid, src_path, &lstate))
- return error("could not write '%s'", src_path);
+ if (checkout_path(lmode, &loid, src_path, &lstate)) {
+ ret = error("could not write '%s'", src_path);
+ goto finish;
+ }
}
if (rmode && !S_ISLNK(rmode)) {
@@ -457,9 +459,12 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
hashmap_add(&working_tree_dups, entry);
if (!use_wt_file(workdir, dst_path, &roid)) {
- if (checkout_path(rmode, &roid, dst_path, &rstate))
- return error("could not write '%s'",
- dst_path);
+ if (checkout_path(rmode, &roid, dst_path,
+ &rstate)) {
+ ret = error("could not write '%s'",
+ dst_path);
+ goto finish;
+ }
} else if (!is_null_oid(&roid)) {
/*
* Changes in the working tree need special
@@ -474,10 +479,12 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
ADD_CACHE_JUST_APPEND);
add_path(&rdir, rdir_len, dst_path);
- if (ensure_leading_directories(rdir.buf))
- return error("could not create "
- "directory for '%s'",
- dst_path);
+ if (ensure_leading_directories(rdir.buf)) {
+ ret = error("could not create "
+ "directory for '%s'",
+ dst_path);
+ goto finish;
+ }
add_path(&wtdir, wtdir_len, dst_path);
if (symlinks) {
if (symlink(wtdir.buf, rdir.buf)) {
@@ -499,13 +506,14 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
}
fclose(fp);
+ fp = NULL;
if (finish_command(&child)) {
ret = error("error occurred running diff --raw");
goto finish;
}
if (!i)
- return 0;
+ goto finish;
/*
* Changes to submodules require special treatment.This loop writes a
@@ -628,6 +636,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
exit_cleanup(tmpdir, rc);
finish:
+ if (fp)
+ fclose(fp);
+
free(lbase_dir);
free(rbase_dir);
strbuf_release(&ldir);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 828d41c0c11..64617ad8e36 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -734,6 +734,7 @@ static void handle_tag(const char *name, struct tag *tag)
oid_to_hex(&tag->object.oid));
case DROP:
/* Ignore this tag altogether */
+ free(buf);
return;
case REWRITE:
if (tagged->type != OBJ_COMMIT) {
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index c0d88f97512..9b3efc8e987 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -232,8 +232,9 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
do {
peek = fgetc(f);
- } while (peek >= 0 && isspace(peek));
- ungetc(peek, f);
+ } while (isspace(peek));
+ if (peek != EOF)
+ ungetc(peek, f);
if (strbuf_getwholeline(&buf, f, '\n')) {
/* empty stdin is OK */
diff --git a/http-backend.c b/http-backend.c
index d12572fda10..eef0a361f4f 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -681,10 +681,8 @@ int cmd_main(int argc, const char **argv)
if (!regexec(&re, dir, 1, out, 0)) {
size_t n;
- if (strcmp(method, c->method)) {
- free(dir);
+ if (strcmp(method, c->method))
return bad_request(&hdr, c);
- }
cmd = c;
n = out[0].rm_eo - out[0].rm_so;
@@ -710,7 +708,5 @@ int cmd_main(int argc, const char **argv)
max_request_buffer);
cmd->imp(&hdr, cmd_arg);
- free(dir);
- free(cmd_arg);
return 0;
}
diff --git a/line-log.c b/line-log.c
index 19d46e9ea2c..b9087814b8c 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1125,7 +1125,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c
changed = process_all_files(&parent_range, rev, &queue, range);
if (parent)
add_line_range(rev, parent, parent_range);
- free(parent_range);
+ free_line_log_data(parent_range);
return changed;
}
diff --git a/mailinfo.c b/mailinfo.c
index 60dcad7b714..a319911b510 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -882,7 +882,10 @@ static int read_one_header_line(struct strbuf *line, FILE *in)
for (;;) {
int peek;
- peek = fgetc(in); ungetc(peek, in);
+ peek = fgetc(in);
+ if (peek == EOF)
+ break;
+ ungetc(peek, in);
if (peek != ' ' && peek != '\t')
break;
if (strbuf_getline_lf(&continuation, in))
@@ -1094,14 +1097,18 @@ int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
return -1;
}
- mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
- mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
-
do {
peek = fgetc(mi->input);
- } while (peek >= 0 && isspace(peek));
+ if (peek == EOF) {
+ fclose(cmitmsg);
+ return error("empty patch: '%s'", patch);
+ }
+ } while (isspace(peek));
ungetc(peek, mi->input);
+ mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
+ mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
+
/* process the email header */
while (read_one_header_line(&line, mi->input))
check_header(mi, &line, mi->p_hdr_data, 1);
diff --git a/reflog-walk.c b/reflog-walk.c
index ec66f2b16e6..c63eb1a3fd7 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -197,17 +197,27 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
reflogs = read_complete_reflog(branch);
}
}
- if (!reflogs || reflogs->nr == 0)
+ if (!reflogs || reflogs->nr == 0) {
+ if (reflogs) {
+ free(reflogs->ref);
+ free(reflogs);
+ }
+ free(branch);
return -1;
+ }
string_list_insert(&info->complete_reflogs, branch)->util
= reflogs;
}
+ free(branch);
commit_reflog = xcalloc(1, sizeof(struct commit_reflog));
if (recno < 0) {
commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
if (commit_reflog->recno < 0) {
- free(branch);
+ if (reflogs) {
+ free(reflogs->ref);
+ free(reflogs);
+ }
free(commit_reflog);
return -1;
}-- 2.12.2.windows.2.800.gede8f145e06