From 2b6a9b13ca4a687aeb0cad5f32e49711b3e67aaa Mon Sep 17 00:00:00 2001 From: Thomas Gummerer Date: Tue, 8 Oct 2019 18:38:43 +0100 Subject: range-diff: don't segfault with mode-only changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ef283b3699 ("apply: make parse_git_diff_header public", 2019-07-11) the 'parse_git_diff_header' function was made public and useable by callers outside of apply.c. However it was missed that its (then) only caller, 'find_header' did some error handling, and completing 'struct patch' appropriately. range-diff then started using this function, and tried to handle this appropriately itself, but fell short in some cases. This in turn would lead to range-diff segfaulting when there are mode-only changes in a range. Move the error handling and completing of the struct into the 'parse_git_diff_header' function, so other callers can take advantage of it. This fixes the segfault in 'git range-diff'. Reported-by: Uwe Kleine-König Signed-off-by: Thomas Gummerer Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- apply.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'apply.c') diff --git a/apply.c b/apply.c index cde95369bb..5f06a1460c 100644 --- a/apply.c +++ b/apply.c @@ -1361,11 +1361,32 @@ int parse_git_diff_header(struct strbuf *root, if (check_header_line(*linenr, patch)) return -1; if (res > 0) - return offset; + goto done; break; } } +done: + if (!patch->old_name && !patch->new_name) { + if (!patch->def_name) { + error(Q_("git diff header lacks filename information when removing " + "%d leading pathname component (line %d)", + "git diff header lacks filename information when removing " + "%d leading pathname components (line %d)", + parse_hdr_state.p_value), + parse_hdr_state.p_value, *linenr); + return -128; + } + patch->old_name = xstrdup(patch->def_name); + patch->new_name = xstrdup(patch->def_name); + } + if ((!patch->new_name && !patch->is_delete) || + (!patch->old_name && !patch->is_new)) { + error(_("git diff header lacks filename information " + "(line %d)"), *linenr); + return -128; + } + patch->is_toplevel_relative = 1; return offset; } @@ -1546,26 +1567,6 @@ static int find_header(struct apply_state *state, return -128; if (git_hdr_len <= len) continue; - if (!patch->old_name && !patch->new_name) { - if (!patch->def_name) { - error(Q_("git diff header lacks filename information when removing " - "%d leading pathname component (line %d)", - "git diff header lacks filename information when removing " - "%d leading pathname components (line %d)", - state->p_value), - state->p_value, state->linenr); - return -128; - } - patch->old_name = xstrdup(patch->def_name); - patch->new_name = xstrdup(patch->def_name); - } - if ((!patch->new_name && !patch->is_delete) || - (!patch->old_name && !patch->is_new)) { - error(_("git diff header lacks filename information " - "(line %d)"), state->linenr); - return -128; - } - patch->is_toplevel_relative = 1; *hdrsize = git_hdr_len; return offset; } -- cgit v1.2.3