[patch] buffer overflow in q_parser.y

Hi Dave,

The patch below corrects a buffer overflow bug in q_parser.y. Since it
is triggered by excessively long query strings, I believe that this bug
could be exploited to allow arbitrary code execution if a query string
supplied by a user is passed in directly to Ferret and not truncatated.
If I’m right, you should consider a new release asap.

I’ve fixed it to simply allocate more memory if the standard buffers
aren’t enough (because I had some long (i.e. > 255) query strings that I
needed to support), but there are other solutions as well.

Index: c/include/search.h

— c/include/search.h (revision 615)
+++ c/include/search.h (working copy)
@@ -819,6 +819,7 @@
char *qstr;
char *qstrp;
char buf[QP_CONC_WORDS][MAX_WORD_SIZE];

  • char *dynbuf;
    int buf_index;
    HashTable *field_cache;
    HashSet *fields;
    Index: c/src/q_parser.y
    ===================================================================
    — c/src/q_parser.y (revision 615)
    +++ c/src/q_parser.y (working copy)
    @@ -173,6 +173,11 @@
    char *bufp = buf;
    qp->buf_index = (qp->buf_index + 1) % QP_CONC_WORDS;

  • if (qp->dynbuf) {

  •  free(qp->dynbuf);
    
  •  qp->dynbuf = NULL;
    
  • }

  • qp->qstrp–; /* need to back up one character */

    while (!strchr(not_word, (c=*qp->qstrp++))) {
    @@ -192,6 +197,15 @@
    default:
    *bufp++ = c;
    }

  • /* we’ve exceeded the static buffer. switch to the dynamic

  •  one. */
    
  • if (!qp->dynbuf && ((bufp - buf) == MAX_WORD_SIZE)) {

  •   qp->dynbuf = calloc(strlen(qp->qstr) + 1, sizeof(char));
    
  •   strncpy(qp->dynbuf, buf, MAX_WORD_SIZE);
    
  •   buf = qp->dynbuf;
    
  •   bufp = buf + MAX_WORD_SIZE;
    
  • }
    }
    qp->qstrp–;
    /* check for keywords. There are only four so we have a bit of a
    hack which
    @@ -262,7 +276,7 @@
    }
    mutex_unlock(&qp->mutex);
    RAISE(PARSE_ERROR, "couldn’t parse query ``%s’’. Error message
    "

  •          " was %se", buf, (char *)msg);
    
  •          "was: %s", buf, (char *)msg);
    
    }
    return 0;
    }
    @@ -707,6 +721,9 @@
    if (self->tokenized_fields) {
    hs_destroy(self->tokenized_fields);
    }
  • if (self->dynbuf) {
  •  free(self->dynbuf);
    
  • }
    hs_destroy(self->all_fields);
    hs_destroy(self->fields_buf);
    h_destroy(self->field_cache);
    @@ -754,6 +771,7 @@
    self->analyzer = analyzer;
    self->ts_cache = h_new_str(&free, (free_ft)&ts_deref);
    self->buf_index = 0;
  • self->dynbuf = NULL;
    self->non_tokenizer = non_tokenizer_new();
    mutex_init(&self->mutex, NULL);
    return self;

On 9/25/06, William M. [email protected] wrote:

needed to support), but there are other solutions as well.
int buf_index;

  • if (qp->dynbuf) {
    }
    qp->qstrp–;
    @@ -707,6 +721,9 @@
    self->analyzer = analyzer;
    self->ts_cache = h_new_str(&free, (free_ft)&ts_deref);
    self->buf_index = 0;
  • self->dynbuf = NULL;
    self->non_tokenizer = non_tokenizer_new();
    mutex_init(&self->mutex, NULL);
    return self;

Thanks, I’ll release a new gem ASAP.

cheers,
Dave