Merge branch 'js/regexec-buf'
authorJunio C Hamano <gitster@pobox.com>
Mon, 26 Sep 2016 23:09:19 +0000 (16:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 26 Sep 2016 23:09:19 +0000 (16:09 -0700)
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

Makefile
diff.c
diffcore-pickaxe.c
git-compat-util.h
grep.c
t/t4062-diff-pickaxe.sh [new file with mode: 0755]
xdiff-interface.c
index e8b060a235ab47e5ef91862d0d2ebcca416728d3..1aad150b34f7f2e84bea104538033443644af3a2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -301,7 +301,8 @@ all::
 # crashes due to allocation and free working on different 'heaps'.
 # It's defined automatically if USE_NED_ALLOCATOR is set.
 #
-# Define NO_REGEX if you have no or inferior regex support in your C library.
+# Define NO_REGEX if your C library lacks regex support with REG_STARTEND
+# feature.
 #
 # Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
 # user.
diff --git a/diff.c b/diff.c
index 2cc3a2e29b804c1ae58aa7d0c6d162b084ddeed8..a178ed39bc77988dcc6282d252002f5446305628 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -971,7 +971,8 @@ static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
 {
        if (word_regex && *begin < buffer->size) {
                regmatch_t match[1];
-               if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
+               if (!regexec_buf(word_regex, buffer->ptr + *begin,
+                                buffer->size - *begin, 1, match, 0)) {
                        char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
                                        '\n', match[0].rm_eo - match[0].rm_so);
                        *end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
index 55067cab6c2dbfb6abbfffa0aa9d7259502041cb..9795ca1c159a5177b1b7031a87c8f8bd5e5be3d5 100644 (file)
@@ -23,7 +23,6 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
 {
        struct diffgrep_cb *data = priv;
        regmatch_t regmatch;
-       int hold;
 
        if (line[0] != '+' && line[0] != '-')
                return;
@@ -33,11 +32,8 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
                 * caller early.
                 */
                return;
-       /* Yuck -- line ought to be "const char *"! */
-       hold = line[len];
-       line[len] = '\0';
-       data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
-       line[len] = hold;
+       data->hit = !regexec_buf(data->regexp, line + 1, len - 1, 1,
+                                &regmatch, 0);
 }
 
 static int diff_grep(mmfile_t *one, mmfile_t *two,
@@ -50,9 +46,11 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
        xdemitconf_t xecfg;
 
        if (!one)
-               return !regexec(regexp, two->ptr, 1, &regmatch, 0);
+               return !regexec_buf(regexp, two->ptr, two->size,
+                                   1, &regmatch, 0);
        if (!two)
-               return !regexec(regexp, one->ptr, 1, &regmatch, 0);
+               return !regexec_buf(regexp, one->ptr, one->size,
+                                   1, &regmatch, 0);
 
        /*
         * 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)
                regmatch_t regmatch;
                int flags = 0;
 
-               assert(data[sz] == '\0');
-               while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
+               while (*data &&
+                      !regexec_buf(regexp, data, sz, 1, &regmatch, flags)) {
                        flags |= REG_NOTBOL;
                        data += regmatch.rm_eo;
                        if (*data && regmatch.rm_so == regmatch.rm_eo)
index 37cce0746f460ae6c839e2556c526e0fd41cdf65..8aab0c30474764b3e71d4e6c0bc66c52218b3d5e 100644 (file)
@@ -977,6 +977,19 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #define qsort git_qsort
 #endif
 
+#ifndef REG_STARTEND
+#error "Git requires REG_STARTEND support. Compile with NO_REGEX=NeedsStartEnd"
+#endif
+
+static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,
+                             size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+       assert(nmatch > 0 && pmatch);
+       pmatch[0].rm_so = 0;
+       pmatch[0].rm_eo = size;
+       return regexec(preg, buf, nmatch, pmatch, eflags | REG_STARTEND);
+}
+
 #ifndef DIR_HAS_BSD_GROUP_SEMANTICS
 # define FORCE_DIR_SET_GID S_ISGID
 #else
diff --git a/grep.c b/grep.c
index d7d00b87cb2a28332ccad4ea4ff676e3f2bc2611..1194d35b5d06d7960d4464884952e755914e2519 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -898,17 +898,6 @@ static int fixmatch(struct grep_pat *p, char *line, char *eol,
        }
 }
 
-static int regmatch(const regex_t *preg, char *line, char *eol,
-                   regmatch_t *match, int eflags)
-{
-#ifdef REG_STARTEND
-       match->rm_so = 0;
-       match->rm_eo = eol - line;
-       eflags |= REG_STARTEND;
-#endif
-       return regexec(preg, line, 1, match, eflags);
-}
-
 static int patmatch(struct grep_pat *p, char *line, char *eol,
                    regmatch_t *match, int eflags)
 {
@@ -919,7 +908,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol,
        else if (p->pcre_regexp)
                hit = !pcrematch(p, line, eol, match, eflags);
        else
-               hit = !regmatch(&p->regexp, line, eol, match, eflags);
+               hit = !regexec_buf(&p->regexp, line, eol - line, 1, match,
+                                  eflags);
 
        return hit;
 }
diff --git a/t/t4062-diff-pickaxe.sh b/t/t4062-diff-pickaxe.sh
new file mode 100755 (executable)
index 0000000..f0bf50b
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 Johannes Schindelin
+#
+
+test_description='Pickaxe options'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       test_commit initial &&
+       printf "%04096d" 0 >4096-zeroes.txt &&
+       git add 4096-zeroes.txt &&
+       test_tick &&
+       git commit -m "A 4k file"
+'
+test_expect_success '-G matches' '
+       git diff --name-only -G "^0{4096}$" HEAD^ >out &&
+       test 4096-zeroes.txt = "$(cat out)"
+'
+
+test_done
index 3bfc69cade6e543f69164ed6d989473de40b7b3c..060038c2d6b92512e6625d0f5d94ed5439cd249c 100644 (file)
@@ -214,11 +214,10 @@ struct ff_regs {
 static long ff_regexp(const char *line, long len,
                char *buffer, long buffer_size, void *priv)
 {
-       char *line_buffer;
        struct ff_regs *regs = priv;
        regmatch_t pmatch[2];
        int i;
-       int result = -1;
+       int result;
 
        /* Exclude terminating newline (and cr) from matching */
        if (len > 0 && line[len-1] == '\n') {
@@ -228,18 +227,16 @@ static long ff_regexp(const char *line, long len,
                        len--;
        }
 
-       line_buffer = xstrndup(line, len); /* make NUL terminated */
-
        for (i = 0; i < regs->nr; i++) {
                struct ff_reg *reg = regs->array + i;
-               if (!regexec(&reg->re, line_buffer, 2, pmatch, 0)) {
+               if (!regexec_buf(&reg->re, line, len, 2, pmatch, 0)) {
                        if (reg->negate)
-                               goto fail;
+                               return -1;
                        break;
                }
        }
        if (regs->nr <= i)
-               goto fail;
+               return -1;
        i = pmatch[1].rm_so >= 0 ? 1 : 0;
        line += pmatch[i].rm_so;
        result = pmatch[i].rm_eo - pmatch[i].rm_so;
@@ -248,8 +245,6 @@ static long ff_regexp(const char *line, long len,
        while (result > 0 && (isspace(line[result - 1])))
                result--;
        memcpy(buffer, line, result);
- fail:
-       free(line_buffer);
        return result;
 }