diff options
author | Jim Meyering <meyering@fb.com> | 2016-08-12 21:40:29 -0700 |
---|---|---|
committer | Jim Meyering <meyering@fb.com> | 2016-08-13 21:56:39 -0700 |
commit | edd942ca27d570a33d612b12eecaa33a76640e46 (patch) | |
tree | 982758dcaf484390dc7f1114b77b21bff7080875 | |
parent | b3def738f3b435cbe6f2a8406bae5a71175a0b80 (diff) | |
download | diffutils-edd942ca27d570a33d612b12eecaa33a76640e46.tar.gz |
diff3: fix leaks, for real
* src/diff3.c (struct diff_block)[lint]: Add member, n2.
(free_diff_block, next_to_n2): New functions.
* tests/diff3: Add more test coverage.
-rw-r--r-- | src/diff3.c | 56 | ||||
-rw-r--r-- | tests/diff3 | 66 |
2 files changed, 118 insertions, 4 deletions
diff --git a/src/diff3.c b/src/diff3.c index 0eb643e..b80aeb3 100644 --- a/src/diff3.c +++ b/src/diff3.c @@ -78,6 +78,9 @@ struct diff_block { char **lines[2]; /* The actual lines (may contain nulls) */ size_t *lengths[2]; /* Line lengths (including newlines, if any) */ struct diff_block *next; +#ifdef lint + struct diff_block *n2; /* Used only when freeing. */ +#endif }; /* Three way diff */ @@ -176,7 +179,7 @@ static struct diff3_block *create_diff3_block (lin, lin, lin, lin, lin, lin); static struct diff3_block *make_3way_diff (struct diff_block *, struct diff_block *); static struct diff3_block *reverse_diff3_blocklist (struct diff3_block *); static struct diff3_block *using_to_diff3_block (struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *); -static struct diff_block *process_diff (char const *, char const *, struct diff_block **); +static struct diff_block *process_diff (char const *, char const *, struct diff_block **, char **); static void check_stdout (void); static void fatal (char const *) __attribute__((noreturn)); static void output_diff3 (FILE *, struct diff3_block *, int const[3], int const[3]); @@ -212,6 +215,38 @@ static struct option const longopts[] = {0, 0, 0, 0} }; +static void +free_diff_block (struct diff_block *p) +{ +#ifndef lint + (void)p; +#else + while (p) + { + free (p->lines[0]); + free (p->lines[1]); + free (p->lengths[0]); + free (p->lengths[1]); + struct diff_block *next = p->n2; + free (p); + p = next; + } +#endif +} + +/* Copy each next pointer to n2, since make_3way_diff would clobber the former, + yet we will still need something to free these buffers. */ +static void +next_to_n2 (struct diff_block *p) +{ +#ifndef lint + (void)p; +#else + while (p) + p = p->n2 = p->next; +#endif +} + int main (int argc, char **argv) { @@ -377,10 +412,19 @@ main (int argc, char **argv) /* Invoke diff twice on two pairs of input files, combine the two diffs, and output them. */ + char *b0, *b1; commonname = file[rev_mapping[FILEC]]; - thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block); - thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block); + thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block, &b1); + thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block, &b0); + + next_to_n2 (thread0); + next_to_n2 (thread1); + diff3 = make_3way_diff (thread0, thread1); + + free_diff_block (thread0); + free_diff_block (thread1); + if (edscript) conflicts_found = output_diff3_edscript (stdout, diff3, mapping, rev_mapping, @@ -400,6 +444,8 @@ main (int argc, char **argv) conflicts_found = false; } + free (b0); + free (b1); check_stdout (); exit (conflicts_found); } @@ -938,7 +984,8 @@ compare_line_list (char * const list1[], size_t const lengths1[], static struct diff_block * process_diff (char const *filea, char const *fileb, - struct diff_block **last_block) + struct diff_block **last_block, + char **buf_to_free) { char *diff_contents; char *diff_limit; @@ -953,6 +1000,7 @@ process_diff (char const *filea, sizeof *bptr->lengths[1])); diff_limit = read_diff (filea, fileb, &diff_contents); + *buf_to_free = diff_contents; scan_diff = diff_contents; while (scan_diff < diff_limit) diff --git a/tests/diff3 b/tests/diff3 index a1fc287..a40631b 100644 --- a/tests/diff3 +++ b/tests/diff3 @@ -27,4 +27,70 @@ diff3 a a a > out 2> err || fail=1 compare /dev/null out || fail=1 compare /dev/null err || fail=1 +# This would have provoked a nontrivial leak prior to diffutils-3.5, +# due to the nontrivial list of diff_block structs. +seq 10 40|sed 's/1$/x/' > d || framework_failure_ +seq 10 40|sed 's/5$/y/' > e || framework_failure_ +seq 10 40|sed 's/8$/z/' > f || framework_failure_ +cat <<'EOF' > exp40 || framework_failure_ +====1 +1:2c + 1x +2:2c +3:2c + 11 +====2 +1:6c +3:6c + 15 +2:6c + 1y +====3 +1:9c +2:9c + 18 +3:9c + 1z +====1 +1:12c + 2x +2:12c +3:12c + 21 +====2 +1:16c +3:16c + 25 +2:16c + 2y +====3 +1:19c +2:19c + 28 +3:19c + 2z +====1 +1:22c + 3x +2:22c +3:22c + 31 +====2 +1:26c +3:26c + 35 +2:26c + 3y +====3 +1:29c +2:29c + 38 +3:29c + 3z +EOF + +diff3 d e f > out 2> err +compare exp40 out || fail=1 +compare /dev/null err || fail=1 + Exit $fail |