regex: use regexec_buf()

The new regexec_buf() function operates on buffers with an explicitly specified length, rather than NUL-terminated strings. We need to use this function whenever the buffer we want to pass to regexec(3) may have been mmap(2)ed (and is hence not NUL-terminated). Note: the original motivation for this patch was to fix a bug where `git diff -G <regex>` would crash. This patch converts more callers, though, some of which allocated to construct NUL-terminated strings, or worse, modified buffers to temporarily insert NULs while calling regexec(3). By converting them to use regexec_buf(), the code has become much cleaner. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Sep 21, 2016 at 20:24 UTC b7d36ffca02c23f545d6e098d78180e6e72dfd8d
5 files changed +17 -33
diff.c
+2 -1
@@ -941,7 +941,8 @@ static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
941 {
942 if (word_regex && *begin < buffer->size) {
943 regmatch_t match[1];
944 - if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
944 + if (!regexec_buf(word_regex, buffer->ptr + *begin,
945 + buffer->size - *begin, 1, match, 0)) {
946 char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
947 '\n', match[0].rm_eo - match[0].rm_so);
948 *end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
diffcore-pickaxe.c
+8 -10
@@ -21,7 +21,6 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
21 {
22 struct diffgrep_cb *data = priv;
23 regmatch_t regmatch;
24 - int hold;
24
25 if (line[0] != '+' && line[0] != '-')
26 return;
@@ -31,11 +30,8 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
30 * caller early.
31 */
32 return;
34 - /* Yuck -- line ought to be "const char *"! */
35 - hold = line[len];
36 - line[len] = '\0';
37 - data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
38 - line[len] = hold;
33 + data->hit = !regexec_buf(data->regexp, line + 1, len - 1, 1,
34 + &regmatch, 0);
35 }
36
37 static int diff_grep(mmfile_t *one, mmfile_t *two,
@@ -48,9 +44,11 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
44 xdemitconf_t xecfg;
45
46 if (!one)
51 - return !regexec(regexp, two->ptr, 1, &regmatch, 0);
47 + return !regexec_buf(regexp, two->ptr, two->size,
48 + 1, &regmatch, 0);
49 if (!two)
53 - return !regexec(regexp, one->ptr, 1, &regmatch, 0);
50 + return !regexec_buf(regexp, one->ptr, one->size,
51 + 1, &regmatch, 0);
52
53 /*
54 * We have both sides; need to run textual diff and see if
@@ -81,8 +79,8 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
79 regmatch_t regmatch;
80 int flags = 0;
81
84 - assert(data[sz] == '\0');
85 - while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
82 + while (*data &&
83 + !regexec_buf(regexp, data, sz, 1, &regmatch, flags)) {
84 flags |= REG_NOTBOL;
85 data += regmatch.rm_eo;
86 if (*data && regmatch.rm_so == regmatch.rm_eo)
grep.c
+2 -12
@@ -848,17 +848,6 @@ static int fixmatch(struct grep_pat *p, char *line, char *eol,
848 }
849 }
850
851 -static int regmatch(const regex_t *preg, char *line, char *eol,
852 - regmatch_t *match, int eflags)
853 -{
854 -#ifdef REG_STARTEND
855 - match->rm_so = 0;
856 - match->rm_eo = eol - line;
857 - eflags |= REG_STARTEND;
858 -#endif
859 - return regexec(preg, line, 1, match, eflags);
860 -}
861 -
851 static int patmatch(struct grep_pat *p, char *line, char *eol,
852 regmatch_t *match, int eflags)
853 {
@@ -869,7 +858,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol,
858 else if (p->pcre_regexp)
859 hit = !pcrematch(p, line, eol, match, eflags);
860 else
872 - hit = !regmatch(&p->regexp, line, eol, match, eflags);
861 + hit = !regexec_buf(&p->regexp, line, eol - line, 1, match,
862 + eflags);
863
864 return hit;
865 }
t/t4062-diff-pickaxe.sh
+1 -1
@@ -14,7 +14,7 @@ test_expect_success setup '
14 test_tick &&
15 git commit -m "A 4k file"
16 '
17 -test_expect_failure '-G matches' '
17 +test_expect_success '-G matches' '
18 git diff --name-only -G "^0{4096}$" HEAD^ >out &&
19 test 4096-zeroes.txt = "$(cat out)"
20 '
xdiff-interface.c
+4 -9
@@ -216,11 +216,10 @@ struct ff_regs {
216 static long ff_regexp(const char *line, long len,
217 char *buffer, long buffer_size, void *priv)
218 {
219 - char *line_buffer;
219 struct ff_regs *regs = priv;
220 regmatch_t pmatch[2];
221 int i;
223 - int result = -1;
222 + int result;
223
224 /* Exclude terminating newline (and cr) from matching */
225 if (len > 0 && line[len-1] == '\n') {
@@ -230,18 +229,16 @@ static long ff_regexp(const char *line, long len,
229 len--;
230 }
231
233 - line_buffer = xstrndup(line, len); /* make NUL terminated */
234 -
232 for (i = 0; i < regs->nr; i++) {
233 struct ff_reg *reg = regs->array + i;
237 - if (!regexec(&reg->re, line_buffer, 2, pmatch, 0)) {
234 + if (!regexec_buf(&reg->re, line, len, 2, pmatch, 0)) {
235 if (reg->negate)
239 - goto fail;
236 + return -1;
237 break;
238 }
239 }
240 if (regs->nr <= i)
244 - goto fail;
241 + return -1;
242 i = pmatch[1].rm_so >= 0 ? 1 : 0;
243 line += pmatch[i].rm_so;
244 result = pmatch[i].rm_eo - pmatch[i].rm_so;
@@ -250,8 +247,6 @@ static long ff_regexp(const char *line, long len,
247 while (result > 0 && (isspace(line[result - 1])))
248 result--;
249 memcpy(buffer, line, result);
253 - fail:
254 - free(line_buffer);
250 return result;
251 }
252