Commit message: None
CVE: CVE-2017-13720
CWE: CWE-125
Vulnerability description: In the PatternMatch function in fontfile/fontdir.c in libXfont through 1.5.2 and 2.x before 2.0.2, an attacker with access to an X connection can cause a buffer over-read during pattern matching of fonts, leading to information disclosure or a crash (denial of service). This occurs because '\0' characters are incorrectly skipped in situations involving ? characters.

Project: libxfont

FontFileAddEntry(FontTablePtr table, FontEntryPtr prototype)
{
    FontEntryPtr    entry;
    int		    newsize;

    /* can't add entries to a sorted table, pointers get broken! */
    if (table->sorted)
	return (FontEntryPtr) 0;    /* "cannot" happen */
    if (table->used == table->size) {
	if (table->size >= ((INT32_MAX / sizeof(FontEntryRec)) - 100))
	    /* If we've read so many entries we're going to ask for 2gb
	       or more of memory, something is so wrong with this font
	       directory that we should just give up before we overflow. */
	    return NULL;
	newsize = table->size + 100;
	entry = realloc(table->entries, newsize * sizeof(FontEntryRec));
	if (!entry)
	    return (FontEntryPtr)0;
	table->size = newsize;
	table->entries = entry;
    }
    entry = &table->entries[table->used];
    *entry = *prototype;
    entry->name.name = malloc(prototype->name.length + 1);
    if (!entry->name.name)
	return (FontEntryPtr)0;
    memcpy (entry->name.name, prototype->name.name, prototype->name.length);
    entry->name.name[entry->name.length] = '\0';
    table->used++;
    return entry;
}

Project: libxfont

FontFileFreeDir (FontDirectoryPtr dir)
{
    FontFileFreeTable (&dir->scalable);
    FontFileFreeTable (&dir->nonScalable);
    free(dir);
}

Project: libxfont

FontFileFreeEntry (FontEntryPtr entry)
{
    FontScalableExtraPtr   extra;
    int i;

    if (entry->name.name)
	free(entry->name.name);
    entry->name.name = NULL;

    switch (entry->type)
    {
    case FONT_ENTRY_SCALABLE:
	free (entry->u.scalable.fileName);
	extra = entry->u.scalable.extra;
	for (i = 0; i < extra->numScaled; i++)
	    if (extra->scaled[i].vals.ranges)
		free (extra->scaled[i].vals.ranges);
	free (extra->scaled);
	free (extra);
	break;
    case FONT_ENTRY_BITMAP:
	free (entry->u.bitmap.fileName);
	entry->u.bitmap.fileName = NULL;
	break;
    case FONT_ENTRY_ALIAS:
	free (entry->u.alias.resolved);
	entry->u.alias.resolved = NULL;
	break;
    }
}

Project: libxfont

FontFileFreeTable (FontTablePtr table)
{
    int	i;

    for (i = 0; i < table->used; i++)
	FontFileFreeEntry (&table->entries[i]);
    free (table->entries);
}

Project: libxfont

FontFileInitTable (FontTablePtr table, int size)
{
    if (size < 0 || (size > INT32_MAX/sizeof(FontEntryRec)))
	return FALSE;
    if (size)
    {
	table->entries = malloc(sizeof(FontEntryRec) * size);
	if (!table->entries)
	    return FALSE;
    }
    else
	table->entries = 0;
    table->used = 0;
    table->size = size;
    table->sorted = FALSE;
    return TRUE;
}

Project: libxfont

FontFileMakeDir(const char *dirName, int size)
{
    FontDirectoryPtr	dir;
    int			dirlen;
    int			needslash = 0;
    const char		*attrib;
    int			attriblen;

#if !defined(WIN32)
    attrib = strchr(dirName, ':');
#else
    /* OS/2 uses the colon in the drive letter descriptor, skip this */
    attrib = strchr(dirName+2, ':');
#endif
    if (attrib) {
	dirlen = attrib - dirName;
	attriblen = strlen(attrib);
    } else {
	dirlen = strlen(dirName);
	attriblen = 0;
    }
    if (dirName[dirlen - 1] != '/')
#ifdef NCD
    if (dirlen)     /* leave out slash for builtins */
#endif
	needslash = 1;
    dir = malloc(sizeof *dir + dirlen + needslash + 1 +
		 (attriblen ? attriblen + 1 : 0));
    if (!dir)
	return (FontDirectoryPtr)0;
    if (!FontFileInitTable (&dir->scalable, 0))
    {
	free (dir);
	return (FontDirectoryPtr)0;
    }
    if (!FontFileInitTable (&dir->nonScalable, size))
    {
	FontFileFreeTable (&dir->scalable);
	free (dir);
	return (FontDirectoryPtr)0;
    }
    dir->directory = (char *) (dir + 1);
    dir->dir_mtime = 0;
    dir->alias_mtime = 0;
    if (attriblen)
	dir->attributes = dir->directory + dirlen + needslash + 1;
    else
	dir->attributes = NULL;
    strncpy(dir->directory, dirName, dirlen);
    dir->directory[dirlen] = '\0';
    if (dir->attributes)
	strcpy(dir->attributes, attrib);
    if (needslash)
	strcat(dir->directory, "/");
    return dir;
}

Project: libxfont

FontFileNameCompare(const void* a, const void* b)
{
    FontEntryPtr    a_name = (FontEntryPtr) a,
		    b_name = (FontEntryPtr) b;

    return strcmpn(a_name->name.name, b_name->name.name);
}

Project: libxfont

FontFileSortTable (FontTablePtr table)
{
    if (!table->sorted) {
	qsort((char *) table->entries, table->used, sizeof(FontEntryRec),
	      FontFileNameCompare);
	table->sorted = TRUE;
    }
}

Project: libxfont

SetupWildMatch(FontTablePtr table, FontNamePtr pat,
	       int *leftp, int *rightp, int *privatep)
{
    int         nDashes;
    char        c;
    char       *t;
    char       *firstWild;
    char       *firstDigit;
    int         first;
    int         center,
                left,
                right;
    int         result;
    char	*name;

    name = pat->name;
    nDashes = pat->ndashes;
    firstWild = 0;
    firstDigit = 0;
    t = name;
    while ((c = *t++)) {
	if (isWild(c)) {
	    if (!firstWild)
		firstWild = t - 1;
	}
	if (isDigit(c)) {
	    if (!firstDigit)
		firstDigit = t - 1;
	}
    }
    left = 0;
    right = table->used;
    if (firstWild)
	*privatep = nDashes;
    else
	*privatep = -1;
    if (!table->sorted) {
	*leftp = left;
	*rightp = right;
	return -1;
    } else if (firstWild) {
	if (firstDigit && firstDigit < firstWild)
	    first = firstDigit - name;
	else
	    first = firstWild - name;
	while (left < right) {
	    center = (left + right) / 2;
	    result = strncmp(name, table->entries[center].name.name, first);
	    if (result == 0)
		break;
	    if (result < 0)
		right = center;
	    else
		left = center + 1;
	}
	*leftp = left;
	*rightp = right;
	return -1;
    } else {
	while (left < right) {
	    center = (left + right) / 2;
	    result = strcmpn(name, table->entries[center].name.name);
	    if (result == 0)
		return center;
	    if (result < 0)
		right = center;
	    else
		left = center + 1;
	}
	*leftp = 1;
	*rightp = 0;
	return -1;
    }
}

Project: libxfont

static int strcmpn(const char *s1, const char *s2)
{
    int digits, predigits = 0;
    const char *ss1, *ss2;

    while (1) {
	if (*s1 == 0 && *s2 == 0)
	    return 0;
	digits = Xisdigit(*s1) && Xisdigit(*s2);
	if (digits && !predigits) {
	    ss1 = s1;
	    ss2 = s2;
	    while (Xisdigit(*ss1) && Xisdigit(*ss2))
		ss1++, ss2++;
	    if (!Xisdigit(*ss1) && Xisdigit(*ss2))
		return -1;
	    if (Xisdigit(*ss1) && !Xisdigit(*ss2))
		return 1;
	}
	if ((unsigned char)*s1 < (unsigned char)*s2)
	    return -1;
	if ((unsigned char)*s1 > (unsigned char)*s2)
	    return 1;
	predigits = digits;
	s1++, s2++;
    }
}