CVE-2015-3905 (C, published 2015-06-08T14:59Z)
Description: value: Buffer overflow in the set_cs_start function in t1disasm.c in t1utils before 1.39 allows remote attackers to cause a denial of service (crash) and possibly execute arbitrary code via a crafted font file.
Severity: HIGH (CVSS v3 base score: )
Weakness: CWE-119 \u2014 Improper Restriction of Operations within the Bounds of a Memory Buffer
The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
Repository: https://github.com/kohler/t1utils
Files changed:
Makefile.am
t1asm.c
t1asmhelp.h
t1disasm.c
t1lib.c
t1mac.c
Commit message: Security fixes.

- Don't overflow the small cs_start buffer (reported by Niels
  Thykier via the debian tracker (Jakub Wilk), found with a
  fuzzer ("American fuzzy lop")).

- Cast arguments to <ctype.h> functions to unsigned char.


Vulnerable code:
	clp.c t1lib.h t1lib.c t1asm.c
	clp.c t1lib.h t1lib.c t1disasm.c
/* lenIV and charstring start command */
static int lenIV = 4;
static char cs_start[10];

  while (isspace(*p))
  if (isdigit(string[0]) || string[0] == '-' || string[0] == '+') {
    while (*++string && isdigit(*string))
  char *p, *q, *r;
      if (strncmp(line, "currentfile eexec", 17) == 0 && isspace(line[17])) {
	for (p = line + 18; isspace(*p); p++)
	lenIV = atoi(line + 6);
      } else if ((p = strstr(line, "string currentfile"))
		 && strstr(line, "readstring")) { /* enforce `readstring' */
	/* locate the name of the charstring start command */
	*p = '\0';                                  /* damage line[] */
	q = strrchr(line, '/');
	if (q) {
	  r = cs_start;
	  ++q;
	  while (!isspace(*q) && *q != '{')
	    *r++ = *q++;
	  *r = '\0';
	}
	*p = 's';                                   /* repair line[] */
      if ((p = strstr(line, "/Subrs")) && isdigit(p[7]))
      else if ((p = strstr(line, "/CharStrings")) && isdigit(p[13]))
      for (q = p; isspace(*q) && *q != '\n'; q++)
static int lenIV = 4;
static char cs_start[10];
/* If the line contains an entry of the form `/lenIV <num>' then set the global
   lenIV to <num>.  This indicates the number of random bytes at the beginning
   of each charstring. */

static void
set_lenIV(char *line)
{
  char *p = strstr(line, "/lenIV ");

  /* Allow lenIV to be negative. Thanks to Tom Kacvinsky <tjk@ams.org> */
  if (p && (isdigit(p[7]) || p[7] == '+' || p[7] == '-')) {
    lenIV = atoi(p + 7);
  }
}

static void
set_cs_start(char *line)
{
  char *p, *q, *r;

  if ((p = strstr(line, "string currentfile"))) {
    /* enforce presence of `readstring' -- 5/29/99 */
    if (!strstr(line, "readstring"))
      return;
    /* locate the name of the charstring start command */
    *p = '\0';					  /* damage line[] */
    q = strrchr(line, '/');
    if (q) {
      r = cs_start;
      ++q;
      while (!isspace(*q) && *q != '{')
	*r++ = *q++;
      *r = '\0';
    }
    *p = 's';					  /* repair line[] */
  }
}

    if (isspace(*s))
	    if (strncmp(line, "currentfile eexec", 17) == 0 && isspace(line[17])) {
		for (line += 18; isspace(*line); line++)
	    for (; line < last && isspace(*line); line++)
	    else if (last >= line + 4 && isxdigit(line[0]) && isxdigit(line[1])
		     && isxdigit(line[2]) && isxdigit(line[3]))
    for (s += 9; isspace(*s); s++) ;
      while (*t && !isspace(*t)) t++;
      if (isupper(*s) || isdigit(*s)) {
      } else if (islower(*s)) {

Fixed code:
	clp.c t1lib.h t1asmhelp.h t1lib.c t1asm.c
	clp.c t1lib.h t1asmhelp.h t1lib.c t1disasm.c
#include "t1asmhelp.h"
  while (isspace((unsigned char) *p))
  if (isdigit((unsigned char) string[0]) || string[0] == '-' || string[0] == '+') {
    while (*++string && isdigit((unsigned char) *string))
  char *p, *q;
      if (strncmp(line, "currentfile eexec", 17) == 0 && isspace((unsigned char) line[17])) {
	for (p = line + 18; isspace((unsigned char) *p); p++)
        set_lenIV(line);
      } else if ((p = strstr(line, "string currentfile"))) {
        set_cs_start(line);
      if ((p = strstr(line, "/Subrs")) && isdigit((unsigned char) p[7]))
      else if ((p = strstr(line, "/CharStrings")) && isdigit((unsigned char) p[13]))
      for (q = p; isspace((unsigned char) *q) && *q != '\n'; q++)
#ifndef T1ASMHELP_H
#define T1ASMHELP_H

static int lenIV = 4;

/* If the line contains an entry of the form `/lenIV <num>' then set the global
   lenIV to <num>.  This indicates the number of random bytes at the beginning
   of each charstring. */

static void
set_lenIV(const char* line)
{
  char *p = strstr(line, "/lenIV ");

  /* Allow lenIV to be negative. Thanks to Tom Kacvinsky <tjk@ams.org> */
  if (p && (isdigit((unsigned char) p[7]) || p[7] == '+' || p[7] == '-')) {
    lenIV = atoi(p + 7);
  }
}


static const char* cs_start = "";

static void
set_cs_start(const char* line)
{
    static int cs_start_set = 0;
    char *p, *q, *r;

    if ((p = strstr(line, "string currentfile"))
        && strstr(line, "readstring")) {
        /* locate the name of the charstring start command */
        for (q = p; q != line && q[-1] != '/'; --q)
            /* nada */;
        if (q != line) {
            for (r = q; r != p && !isspace((unsigned char) *r) && *r != '{'; ++r)
                /* nada */;
            if (cs_start_set)
                free((char*) cs_start);
            cs_start = p = malloc(r - q + 1);
            memcpy(p, q, r - q);
            p[r - q] = 0;
            cs_start_set = 1;
        }
    }
}

#endif
#include "t1asmhelp.h"
    if (isspace((unsigned char) *s))
	    if (strncmp(line, "currentfile eexec", 17) == 0 && isspace((unsigned char) line[17])) {
		for (line += 18; isspace((unsigned char) *line); line++)
	    for (; line < last && isspace((unsigned char) *line); line++)
	    else if (last >= line + 4 && isxdigit((unsigned char) line[0])
                     && isxdigit((unsigned char) line[1])
		     && isxdigit((unsigned char) line[2])
                     && isxdigit((unsigned char) line[3]))
    for (s += 9; isspace((unsigned char) *s); s++)
        /* skip */;
      while (*t && !isspace((unsigned char) *t)) t++;
      if (isupper((unsigned char) *s) || isdigit((unsigned char) *s)) {
      } else if (islower((unsigned char) *s)) {