Skip to content

Commit 6a67695

Browse files
committed
Merge branch 'js/regexec-buf'
Some codepaths in "git diff" used regexec(3) on a buffer that was mmap(2)ed, which may not have a terminating NUL, leading to a read beyond the end of the mapped region. This was fixed by introducing a regexec_buf() helper that takes a <ptr,len> pair with REG_STARTEND extension. * js/regexec-buf: regex: use regexec_buf() regex: add regexec_buf() that can work on a non NUL-terminated string regex: -G<pattern> feeds a non NUL-terminated string to regexec() and fails
2 parents 31b83f3 + b7d36ff commit 6a67695

7 files changed

+53
-33
lines changed

Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,8 @@ all::
301301
# crashes due to allocation and free working on different 'heaps'.
302302
# It's defined automatically if USE_NED_ALLOCATOR is set.
303303
#
304-
# Define NO_REGEX if you have no or inferior regex support in your C library.
304+
# Define NO_REGEX if your C library lacks regex support with REG_STARTEND
305+
# feature.
305306
#
306307
# Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
307308
# user.

diff.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,8 @@ static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
971971
{
972972
if (word_regex && *begin < buffer->size) {
973973
regmatch_t match[1];
974-
if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
974+
if (!regexec_buf(word_regex, buffer->ptr + *begin,
975+
buffer->size - *begin, 1, match, 0)) {
975976
char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
976977
'\n', match[0].rm_eo - match[0].rm_so);
977978
*end = p ? p - buffer->ptr : match[0].rm_eo + *begin;

diffcore-pickaxe.c

+8-10
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
2323
{
2424
struct diffgrep_cb *data = priv;
2525
regmatch_t regmatch;
26-
int hold;
2726

2827
if (line[0] != '+' && line[0] != '-')
2928
return;
@@ -33,11 +32,8 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
3332
* caller early.
3433
*/
3534
return;
36-
/* Yuck -- line ought to be "const char *"! */
37-
hold = line[len];
38-
line[len] = '\0';
39-
data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
40-
line[len] = hold;
35+
data->hit = !regexec_buf(data->regexp, line + 1, len - 1, 1,
36+
&regmatch, 0);
4137
}
4238

4339
static int diff_grep(mmfile_t *one, mmfile_t *two,
@@ -50,9 +46,11 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
5046
xdemitconf_t xecfg;
5147

5248
if (!one)
53-
return !regexec(regexp, two->ptr, 1, &regmatch, 0);
49+
return !regexec_buf(regexp, two->ptr, two->size,
50+
1, &regmatch, 0);
5451
if (!two)
55-
return !regexec(regexp, one->ptr, 1, &regmatch, 0);
52+
return !regexec_buf(regexp, one->ptr, one->size,
53+
1, &regmatch, 0);
5654

5755
/*
5856
* We have both sides; need to run textual diff and see if
@@ -83,8 +81,8 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
8381
regmatch_t regmatch;
8482
int flags = 0;
8583

86-
assert(data[sz] == '\0');
87-
while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
84+
while (*data &&
85+
!regexec_buf(regexp, data, sz, 1, &regmatch, flags)) {
8886
flags |= REG_NOTBOL;
8987
data += regmatch.rm_eo;
9088
if (*data && regmatch.rm_so == regmatch.rm_eo)

git-compat-util.h

+13
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,19 @@ void git_qsort(void *base, size_t nmemb, size_t size,
977977
#define qsort git_qsort
978978
#endif
979979

980+
#ifndef REG_STARTEND
981+
#error "Git requires REG_STARTEND support. Compile with NO_REGEX=NeedsStartEnd"
982+
#endif
983+
984+
static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,
985+
size_t nmatch, regmatch_t pmatch[], int eflags)
986+
{
987+
assert(nmatch > 0 && pmatch);
988+
pmatch[0].rm_so = 0;
989+
pmatch[0].rm_eo = size;
990+
return regexec(preg, buf, nmatch, pmatch, eflags | REG_STARTEND);
991+
}
992+
980993
#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
981994
# define FORCE_DIR_SET_GID S_ISGID
982995
#else

grep.c

+2-12
Original file line numberDiff line numberDiff line change
@@ -898,17 +898,6 @@ static int fixmatch(struct grep_pat *p, char *line, char *eol,
898898
}
899899
}
900900

901-
static int regmatch(const regex_t *preg, char *line, char *eol,
902-
regmatch_t *match, int eflags)
903-
{
904-
#ifdef REG_STARTEND
905-
match->rm_so = 0;
906-
match->rm_eo = eol - line;
907-
eflags |= REG_STARTEND;
908-
#endif
909-
return regexec(preg, line, 1, match, eflags);
910-
}
911-
912901
static int patmatch(struct grep_pat *p, char *line, char *eol,
913902
regmatch_t *match, int eflags)
914903
{
@@ -919,7 +908,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol,
919908
else if (p->pcre_regexp)
920909
hit = !pcrematch(p, line, eol, match, eflags);
921910
else
922-
hit = !regmatch(&p->regexp, line, eol, match, eflags);
911+
hit = !regexec_buf(&p->regexp, line, eol - line, 1, match,
912+
eflags);
923913

924914
return hit;
925915
}

t/t4062-diff-pickaxe.sh

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (c) 2016 Johannes Schindelin
4+
#
5+
6+
test_description='Pickaxe options'
7+
8+
. ./test-lib.sh
9+
10+
test_expect_success setup '
11+
test_commit initial &&
12+
printf "%04096d" 0 >4096-zeroes.txt &&
13+
git add 4096-zeroes.txt &&
14+
test_tick &&
15+
git commit -m "A 4k file"
16+
'
17+
test_expect_success '-G matches' '
18+
git diff --name-only -G "^0{4096}$" HEAD^ >out &&
19+
test 4096-zeroes.txt = "$(cat out)"
20+
'
21+
22+
test_done

xdiff-interface.c

+4-9
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,10 @@ struct ff_regs {
214214
static long ff_regexp(const char *line, long len,
215215
char *buffer, long buffer_size, void *priv)
216216
{
217-
char *line_buffer;
218217
struct ff_regs *regs = priv;
219218
regmatch_t pmatch[2];
220219
int i;
221-
int result = -1;
220+
int result;
222221

223222
/* Exclude terminating newline (and cr) from matching */
224223
if (len > 0 && line[len-1] == '\n') {
@@ -228,18 +227,16 @@ static long ff_regexp(const char *line, long len,
228227
len--;
229228
}
230229

231-
line_buffer = xstrndup(line, len); /* make NUL terminated */
232-
233230
for (i = 0; i < regs->nr; i++) {
234231
struct ff_reg *reg = regs->array + i;
235-
if (!regexec(&reg->re, line_buffer, 2, pmatch, 0)) {
232+
if (!regexec_buf(&reg->re, line, len, 2, pmatch, 0)) {
236233
if (reg->negate)
237-
goto fail;
234+
return -1;
238235
break;
239236
}
240237
}
241238
if (regs->nr <= i)
242-
goto fail;
239+
return -1;
243240
i = pmatch[1].rm_so >= 0 ? 1 : 0;
244241
line += pmatch[i].rm_so;
245242
result = pmatch[i].rm_eo - pmatch[i].rm_so;
@@ -248,8 +245,6 @@ static long ff_regexp(const char *line, long len,
248245
while (result > 0 && (isspace(line[result - 1])))
249246
result--;
250247
memcpy(buffer, line, result);
251-
fail:
252-
free(line_buffer);
253248
return result;
254249
}
255250

0 commit comments

Comments
 (0)