Commit message: Merge pull request #27 from kcwu/fix-strgrow

Fix potential heap buffer corruption due to Strgrow
CVE: CVE-2016-9442
CWE: CWE-119
Vulnerability description: An issue was discovered in the Tatsuya Kinoshita w3m fork before 0.5.3-31. w3m allows remote attackers to cause memory corruption in certain conditions via a crafted HTML page.

Project: w3m

Sprintf(char *fmt, ...)
{
    int len = 0;
    int status = SP_NORMAL;
    int p = 0;
    char *f;
    Str s;
    va_list ap;

    va_start(ap, fmt);
    for (f = fmt; *f; f++) {
      redo:
	switch (status) {
	case SP_NORMAL:
	    if (*f == '%') {
		status = SP_PREC;
		p = 0;
	    }
	    else
		len++;
	    break;
	case SP_PREC:
	    if (IS_ALPHA(*f)) {
		/* conversion char. */
		double vd;
		int vi;
		char *vs;
		void *vp;

		switch (*f) {
		case 'l':
		case 'h':
		case 'L':
		case 'w':
		    continue;
		case 'd':
		case 'i':
		case 'o':
		case 'x':
		case 'X':
		case 'u':
		    vi = va_arg(ap, int);
		    len += (p > 0) ? p : 10;
		    break;
		case 'f':
		case 'g':
		case 'e':
		case 'G':
		case 'E':
		    vd = va_arg(ap, double);
		    len += (p > 0) ? p : 15;
		    break;
		case 'c':
		    len += 1;
		    vi = va_arg(ap, int);
		    break;
		case 's':
		    vs = va_arg(ap, char *);
		    vi = strlen(vs);
		    len += (p > vi) ? p : vi;
		    break;
		case 'p':
		    vp = va_arg(ap, void *);
		    len += 10;
		    break;
		case 'n':
		    vp = va_arg(ap, void *);
		    break;
		}
		status = SP_NORMAL;
	    }
	    else if (IS_DIGIT(*f))
		p = p * 10 + *f - '0';
	    else if (*f == '.')
		status = SP_PREC2;
	    else if (*f == '%') {
		status = SP_NORMAL;
		len++;
	    }
	    break;
	case SP_PREC2:
	    if (IS_ALPHA(*f)) {
		status = SP_PREC;
		goto redo;
	    }
	    break;
	}
    }
    va_end(ap);
    s = Strnew_size(len * 2);
    va_start(ap, fmt);
    vsprintf(s->ptr, fmt, ap);
    va_end(ap);
    s->length = strlen(s->ptr);
    if (s->length > len * 2) {
	fprintf(stderr, "Sprintf: string too long\n");
	exit(1);
    }
    return s;
}

Project: w3m

Stralign_center(Str s, int width)
{
    Str n;
    int i, w;

    STR_LENGTH_CHECK(s);
    if (s->length >= width)
	return Strdup(s);
    n = Strnew_size(width);
    w = (width - s->length) / 2;
    for (i = 0; i < w; i++)
	Strcat_char(n, ' ');
    Strcat(n, s);
    for (i = w + s->length; i < width; i++)
	Strcat_char(n, ' ');
    return n;
}

Project: w3m

Stralign_left(Str s, int width)
{
    Str n;
    int i;

    STR_LENGTH_CHECK(s);
    if (s->length >= width)
	return Strdup(s);
    n = Strnew_size(width);
    Strcopy(n, s);
    for (i = s->length; i < width; i++)
	Strcat_char(n, ' ');
    return n;
}

Project: w3m

Stralign_right(Str s, int width)
{
    Str n;
    int i;

    STR_LENGTH_CHECK(s);
    if (s->length >= width)
	return Strdup(s);
    n = Strnew_size(width);
    for (i = s->length; i < width; i++)
	Strcat_char(n, ' ');
    Strcat(n, s);
    return n;
}

Project: w3m

Strcat(Str x, Str y)
{
    STR_LENGTH_CHECK(y);
    Strcat_charp_n(x, y->ptr, y->length);
}

Project: w3m

Strcat_charp(Str x, const char *y)
{
    if (y == NULL)
	return;
    Strcat_charp_n(x, y, strlen(y));
}

Project: w3m

Strcat_charp_n(Str x, const char *y, int n)
{
    int newlen;

    STR_LENGTH_CHECK(x);
    if (y == NULL)
	return;
    newlen = x->length + n + 1;
    if (x->area_size < newlen) {
	char *old = x->ptr;
	newlen = newlen * 3 / 2;
	x->ptr = GC_MALLOC_ATOMIC(newlen);
	x->area_size = newlen;
	bcopy((void *)old, (void *)x->ptr, x->length);
	GC_free(old);
    }
    bcopy((void *)y, (void *)&x->ptr[x->length], n);
    x->length += n;
    x->ptr[x->length] = '\0';
}

Project: w3m

Strcat_m_charp(Str x, ...)
{
    va_list ap;
    char *p;

    va_start(ap, x);
    while ((p = va_arg(ap, char *)) != NULL)
	 Strcat_charp_n(x, p, strlen(p));
}

Project: w3m

Strclear(Str s)
{
    s->length = 0;
    s->ptr[0] = '\0';
}

Project: w3m

Strcopy(Str x, Str y)
{
    STR_LENGTH_CHECK(x);
    STR_LENGTH_CHECK(y);
    if (x->area_size < y->length + 1) {
	GC_free(x->ptr);
	x->ptr = GC_MALLOC_ATOMIC(y->length + 1);
	x->area_size = y->length + 1;
    }
    bcopy((void *)y->ptr, (void *)x->ptr, y->length + 1);
    x->length = y->length;
}

Project: w3m

Strcopy_charp(Str x, const char *y)
{
    int len;

    STR_LENGTH_CHECK(x);
    if (y == NULL) {
	x->length = 0;
	return;
    }
    len = strlen(y);
    if (x->area_size < len + 1) {
	GC_free(x->ptr);
	x->ptr = GC_MALLOC_ATOMIC(len + 1);
	x->area_size = len + 1;
    }
    bcopy((void *)y, (void *)x->ptr, len + 1);
    x->length = len;
}

Project: w3m

Strcopy_charp_n(Str x, const char *y, int n)
{
    int len = n;

    STR_LENGTH_CHECK(x);
    if (y == NULL) {
	x->length = 0;
	return;
    }
    if (x->area_size < len + 1) {
	GC_free(x->ptr);
	x->ptr = GC_MALLOC_ATOMIC(len + 1);
	x->area_size = len + 1;
    }
    bcopy((void *)y, (void *)x->ptr, n);
    x->ptr[n] = '\0';
    x->length = n;
}

Project: w3m

Strdelete(Str s, int pos, int n)
{
    int i;
    STR_LENGTH_CHECK(s);
    if (s->length <= pos + n) {
	s->ptr[pos] = '\0';
	s->length = pos;
	return;
    }
    for (i = pos; i < s->length - n; i++)
	s->ptr[i] = s->ptr[i + n];
    s->ptr[i] = '\0';
    s->length = i;
}

Project: w3m

Strfgetall(FILE * f)
{
    Str s = Strnew();
    int c;
    while ((c = fgetc(f)) != EOF) {
	Strcat_char(s, c);
    }
    return s;
}

Project: w3m

Strfgets(FILE * f)
{
    Str s = Strnew();
    int c;
    while ((c = fgetc(f)) != EOF) {
	Strcat_char(s, c);
	if (c == '\n')
	    break;
    }
    return s;
}

Project: w3m

Strfree(Str x)
{
    GC_free(x->ptr);
    GC_free(x);
}

Project: w3m

Strinsert_char(Str s, int pos, char c)
{
    int i;
    STR_LENGTH_CHECK(s);
    if (pos < 0 || s->length < pos)
	return;
    if (s->length + 2 > s->area_size)
	Strgrow(s);
    for (i = s->length; i > pos; i--)
	s->ptr[i] = s->ptr[i - 1];
    s->ptr[++s->length] = '\0';
    s->ptr[pos] = c;
}

Project: w3m

Strinsert_charp(Str s, int pos, const char *p)
{
    STR_LENGTH_CHECK(s);
    while (*p)
	Strinsert_char(s, pos++, *(p++));
}

Project: w3m

Strlower(Str s)
{
    int i;
    STR_LENGTH_CHECK(s);
    for (i = 0; i < s->length; i++)
	s->ptr[i] = TOLOWER(s->ptr[i]);
}

Project: w3m

Strnew_charp(const char *p)
{
    Str x;
    int n;

    if (p == NULL)
	return Strnew();
    x = GC_MALLOC(sizeof(struct _Str));
    n = strlen(p) + 1;
    x->ptr = GC_MALLOC_ATOMIC(n);
    x->area_size = n;
    x->length = n - 1;
    bcopy((void *)p, (void *)x->ptr, n);
    return x;
}

Project: w3m

Strnew_m_charp(const char *p, ...)
{
    va_list ap;
    Str r = Strnew();

    va_start(ap, p);
    while (p != NULL) {
	Strcat_charp(r, p);
	p = va_arg(ap, char *);
    }
    return r;
}

Project: w3m

Strnew_size(int n)
{
    Str x = GC_MALLOC(sizeof(struct _Str));
    x->ptr = GC_MALLOC_ATOMIC(n + 1);
    x->ptr[0] = '\0';
    x->area_size = n + 1;
    x->length = 0;
    return x;
}

Project: w3m

Strremovefirstspaces(Str s)
{
    int i;

    STR_LENGTH_CHECK(s);
    for (i = 0; i < s->length && IS_SPACE(s->ptr[i]); i++) ;
    if (i == 0)
	return;
    Strdelete(s, 0, i);
}

Project: w3m

Strremovetrailingspaces(Str s)
{
    int i;

    STR_LENGTH_CHECK(s);
    for (i = s->length - 1; i >= 0 && IS_SPACE(s->ptr[i]); i--) ;
    s->length = i + 1;
    s->ptr[i + 1] = '\0';
}

Project: w3m

Strshrink(Str s, int n)
{
    STR_LENGTH_CHECK(s);
    if (n >= s->length) {
	s->length = 0;
	s->ptr[0] = '\0';
    }
    else {
	s->length -= n;
	s->ptr[s->length] = '\0';
    }
}

Project: w3m

Strsubstr(Str s, int beg, int len)
{
    Str new_s;
    int i;

    STR_LENGTH_CHECK(s);
    new_s = Strnew();
    if (beg >= s->length)
	return new_s;
    for (i = 0; i < len && beg + i < s->length; i++)
	Strcat_char(new_s, s->ptr[beg + i]);
    return new_s;
}

Project: w3m

Strtruncate(Str s, int pos)
{
    STR_LENGTH_CHECK(s);
    s->ptr[pos] = '\0';
    s->length = pos;
}

Project: w3m

Strupper(Str s)
{
    int i;
    STR_LENGTH_CHECK(s);
    for (i = 0; i < s->length; i++)
	s->ptr[i] = TOUPPER(s->ptr[i]);
}