diff: fix xpalloc-related signed integer overflow

Problem reported by Gisle Vanem <https://bugs.gnu.org/64316>.
* src/io.c (find_and_hash_each_line):
Rely on xpalloc to check for integer overflow instead
of trying to do it ourselves incorrectly, with old code
that predated the use of xpalloc.
* src/system.h: Verify that LIN_MAX == IDX_MAX,
since the code now relies on this.
* tests/Makefile.am (TESTS): Add bug-64316.
* tests/bug-64316: New file
This commit is contained in:
Paul Eggert 2023-06-28 14:08:14 -07:00
parent bdc8608705
commit 359b8c3ef2
5 changed files with 40 additions and 14 deletions

View File

@ -288,10 +288,10 @@ struct file_data {
/* Array of pointers to lines in the file. */
char const **linbuf;
/* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines.
linebuf[linbuf_base ... buffered_lines - 1] are possibly differing.
linebuf[linbuf_base ... valid_lines - 1] contain valid data.
linebuf[linbuf_base ... alloc_lines - 1] are allocated. */
/* linbuf_base <= 0 <= buffered_lines <= valid_lines <= alloc_lines.
linbuf[0 ... buffered_lines - 1] are possibly differing.
linbuf[linbuf_base ... valid_lines - 1] contain valid data.
linbuf[linbuf_base ... alloc_lines - 1] are allocated. */
lin linbuf_base, buffered_lines, valid_lines, alloc_lines;
/* Pointer to end of prefix of this file to ignore when hashing. */

View File

@ -381,9 +381,7 @@ find_and_hash_each_line (struct file_data *current)
/* Create a new equivalence class in this bucket. */
i = eqs_index++;
if (i == eqs_alloc)
eqs = xpalloc (eqs, &eqs_alloc, 1,
LIN_MAX < PTRDIFF_MAX ? LIN_MAX : -1,
sizeof *eqs);
eqs = xpalloc (eqs, &eqs_alloc, 1, -1, sizeof *eqs);
eqs[i].next = *bucket;
eqs[i].hash = h;
eqs[i].line = ip;
@ -417,13 +415,10 @@ find_and_hash_each_line (struct file_data *current)
/* Maybe increase the size of the line table. */
if (line == alloc_lines)
{
idx_t eqs_max = MIN (LIN_MAX, IDX_MAX / sizeof *cureqs);
/* Grow (alloc_lines - linbuf_base) by adding to alloc_lines. */
idx_t n = alloc_lines - linbuf_base;
linbuf += linbuf_base;
linbuf = xpalloc (linbuf, &n, 1, eqs_max - linbuf_base,
sizeof *linbuf);
linbuf = xpalloc (linbuf, &n, 1, -1, sizeof *linbuf);
linbuf -= linbuf_base;
alloc_lines = linbuf_base + n;
cureqs = xirealloc (cureqs, alloc_lines * sizeof *cureqs);
@ -445,8 +440,7 @@ find_and_hash_each_line (struct file_data *current)
/* Grow (alloc_lines - linbuf_base) by adding to alloc_lines. */
idx_t n = alloc_lines - linbuf_base;
linbuf += linbuf_base;
linbuf = xpalloc (linbuf, &n, 1, MAX (0, IDX_MAX - linbuf_base),
sizeof *linbuf);
linbuf = xpalloc (linbuf, &n, 1, -1, sizeof *linbuf);
linbuf -= linbuf_base;
alloc_lines = n - linbuf_base;
}

View File

@ -129,7 +129,7 @@ typedef struct incomplete *word;
typedef ptrdiff_t lin;
#define LIN_MAX PTRDIFF_MAX
#define pI "t"
verify (LIN_MAX <= IDX_MAX);
verify (LIN_MAX == IDX_MAX);
/* This section contains POSIX-compliant defaults for macros
that are meant to be overridden by hand in config.h as needed. */

View File

@ -5,6 +5,7 @@ TESTS = \
bignum \
binary \
brief-vs-stat-zero-kernel-lies \
bug-64316 \
cmp \
colliding-file-names \
diff3 \

31
tests/bug-64316 Executable file
View File

@ -0,0 +1,31 @@
#!/bin/sh
# Test for signed integer overflow bug within diff.
# Bug reported by Gisele Vanem <http://bugs.gnu.org/64316>.
# Compile with gcc -fsanitize=undefined to test for this bug.
. "${srcdir=.}/init.sh"; path_prepend_ ../src
fail=0
for f in a b; do
printf 'c\nd\ne\nf\ng\n%s\nh\ni\nj\nk\nl\n' $f >$f || framework_failure_
done
cat >exp <<'EOF' || framework_failure_
@@ -3,7 +3,7 @@
e
f
g
-a
+b
h
i
j
EOF
returns_ 1 diff -u a b >out 2>err || fail=1
sed '1,2d' out >out1 || framework_failure_
compare exp out1 || fail=1
compare /dev/null err || fail=1
Exit $fail