summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Meyering <meyering@fb.com>2016-08-12 21:40:29 -0700
committerJim Meyering <meyering@fb.com>2016-08-13 21:56:39 -0700
commitedd942ca27d570a33d612b12eecaa33a76640e46 (patch)
tree982758dcaf484390dc7f1114b77b21bff7080875
parentb3def738f3b435cbe6f2a8406bae5a71175a0b80 (diff)
downloaddiffutils-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.c56
-rw-r--r--tests/diff366
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