ext_get_plugin(const char *name, const char *module, const char *revision)
{
    uint16_t u;

    assert(name);
    assert(module);

    for (u = 0; u < ext_plugins_count; u++) {
        if (!strcmp(name, ext_plugins[u].name) && !strcmp(module, ext_plugins[u].module) &&
                ((!revision && !ext_plugins[u].revision) || (revision && !strcmp(revision, ext_plugins[u].revision)))) {
            /* we have the match */
            return ext_plugins[u].plugin;
        }
    }

    /* plugin not found */
    return NULL;
}


FastHufDecoder::FastHufDecoder
    (const char *&table,
     int numBytes,
     int minSymbol,
     int maxSymbol,
     int rleSymbol)
:
    _rleSymbol (rleSymbol),
    _numSymbols (0),
    _minCodeLength (255),
    _maxCodeLength (0),
    _idToSymbol (0)
{
    //
    // List of symbols that we find with non-zero code lengths
    // (listed in the order we find them). Store these in the
    // same format as the code book stores codes + lengths -
    // low 6 bits are the length, everything above that is
    // the symbol.
    //

    std::vector<Int64> symbols;

    //
    // The 'base' table is the minimum code at each code length. base[i]
    // is the smallest code (numerically) of length i.
    //

    Int64 base[MAX_CODE_LEN + 1];

    //
    // The 'offset' table is the position (in sorted order) of the first id
    // of a given code lenght. Array is indexed by code length, like base.
    //

    Int64 offset[MAX_CODE_LEN + 1];

    //
    // Count of how many codes at each length there are. Array is
    // indexed by code length, like base and offset.
    //

    size_t codeCount[MAX_CODE_LEN + 1];

    for (int i = 0; i <= MAX_CODE_LEN; ++i)
    {
        codeCount[i] = 0;
        base[i]      = 0xffffffffffffffffULL;
        offset[i]    = 0;
    }

    //
    // Count the number of codes, the min/max code lengths, the number of
    // codes with each length, and record symbols with non-zero code
    // length as we find them.
    //

    const char *currByte     = table;
    Int64       currBits     = 0;
    int         currBitCount = 0;

    const int SHORT_ZEROCODE_RUN = 59;
    const int LONG_ZEROCODE_RUN  = 63;
    const int SHORTEST_LONG_RUN  = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;

    for (Int64 symbol = static_cast<Int64>(minSymbol); symbol <= static_cast<Int64>(maxSymbol); symbol++)
    {
        if (currByte - table > numBytes)
        {
            throw IEX_NAMESPACE::InputExc ("Error decoding Huffman table "
                                           "(Truncated table data).");
        }

        //
        // Next code length - either:
        //       0-58  (literal code length)
        //       59-62 (various lengths runs of 0)
        //       63    (run of n 0's, with n is the next 8 bits)
        //

        Int64 codeLen = readBits (6, currBits, currBitCount, currByte);

        if (codeLen == (Int64) LONG_ZEROCODE_RUN)
        {
            if (currByte - table > numBytes)
            {
                throw IEX_NAMESPACE::InputExc ("Error decoding Huffman table "
                                               "(Truncated table data).");
            }

            int runLen = readBits (8, currBits, currBitCount, currByte) +
                         SHORTEST_LONG_RUN;

            if (symbol + runLen > static_cast<Int64>(maxSymbol + 1))
            {
                throw IEX_NAMESPACE::InputExc ("Error decoding Huffman table "
                                               "(Run beyond end of table).");
            }

            symbol += runLen - 1;

        }
        else if (codeLen >= static_cast<Int64>(SHORT_ZEROCODE_RUN))
        {
            int runLen = codeLen - SHORT_ZEROCODE_RUN + 2;

            if (symbol + runLen > static_cast<Int64>(maxSymbol + 1))
            {
                throw IEX_NAMESPACE::InputExc ("Error decoding Huffman table "
                                               "(Run beyond end of table).");
            }

            symbol += runLen - 1;

        }
        else if (codeLen != 0)
        {
            symbols.push_back ((symbol << 6) | (codeLen & 63));

            if (codeLen < _minCodeLength)
                _minCodeLength = codeLen;

            if (codeLen > _maxCodeLength)
                _maxCodeLength = codeLen;

            codeCount[codeLen]++;
        }
    }

    for (int i = 0; i < MAX_CODE_LEN; ++i)
        _numSymbols += codeCount[i];

    table = currByte;

    //
    // Compute base - once we have the code length counts, there
    //                is a closed form solution for this
    //

    {
        double* countTmp = new double[_maxCodeLength+1];

        for (int l = _minCodeLength; l <= _maxCodeLength; ++l)
        {
            countTmp[l] = (double)codeCount[l] *
                          (double)(2 << (_maxCodeLength-l));
        }

        for (int l = _minCodeLength; l <= _maxCodeLength; ++l)
        {
            double tmp = 0;

            for (int k =l + 1; k <= _maxCodeLength; ++k)
                tmp += countTmp[k];

            tmp /= (double)(2 << (_maxCodeLength - l));

            base[l] = (Int64)ceil (tmp);
        }

        delete [] countTmp;
    }

    //
    // Compute offset - these are the positions of the first
    //                  id (not symbol) that has length [i]
    //

    offset[_maxCodeLength] = 0;

    for (int i= _maxCodeLength - 1; i >= _minCodeLength; i--)
        offset[i] = offset[i + 1] + codeCount[i + 1];

    //
    // Allocate and fill the symbol-to-id mapping. Smaller Ids should be
    // mapped to less-frequent symbols (which have longer codes). Use
    // the offset table to tell us where the id's for a given code
    // length start off.
    //

    _idToSymbol = new int[_numSymbols];

    Int64 mapping[MAX_CODE_LEN + 1];
    for (int i = 0; i < MAX_CODE_LEN + 1; ++i)
        mapping[i] = -1;
    for (int i = _minCodeLength; i <= _maxCodeLength; ++i)
        mapping[i] = offset[i];

    for (std::vector<Int64>::const_iterator i = symbols.begin();
         i != symbols.end();
         ++i)
    {
        int codeLen = *i & 63;
        int symbol  = *i >> 6;

        if (mapping[codeLen] >= static_cast<Int64>(_numSymbols))
        {
            delete[] _idToSymbol;
            _idToSymbol = NULL;
            throw IEX_NAMESPACE::InputExc ("Huffman decode error "
                                           "(Invalid symbol in header).");
        }
        _idToSymbol[mapping[codeLen]] = symbol;
        mapping[codeLen]++;
    }

    //
    // exceptions can be thrown whilst building tables. Delete
    // _idToSynmbol before re-throwing to prevent memory leak
    //
    try
    {
      buildTables(base, offset);
    }catch(...)
    {
            delete[] _idToSymbol;
            _idToSymbol = NULL;
            throw;
    }
}

compute Huf codelengths using 64 bit to prevent shift overflow

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>


FastHufDecoder::FastHufDecoder
    (const char *&table,
     int numBytes,
     int minSymbol,
     int maxSymbol,
     int rleSymbol)
:
    _rleSymbol (rleSymbol),
    _numSymbols (0),
    _minCodeLength (255),
    _maxCodeLength (0),
    _idToSymbol (0)
{
    //
    // List of symbols that we find with non-zero code lengths
    // (listed in the order we find them). Store these in the
    // same format as the code book stores codes + lengths -
    // low 6 bits are the length, everything above that is
    // the symbol.
    //

    std::vector<Int64> symbols;

    //
    // The 'base' table is the minimum code at each code length. base[i]
    // is the smallest code (numerically) of length i.
    //

    Int64 base[MAX_CODE_LEN + 1];

    //
    // The 'offset' table is the position (in sorted order) of the first id
    // of a given code lenght. Array is indexed by code length, like base.
    //

    Int64 offset[MAX_CODE_LEN + 1];

    //
    // Count of how many codes at each length there are. Array is
    // indexed by code length, like base and offset.
    //

    size_t codeCount[MAX_CODE_LEN + 1];

    for (int i = 0; i <= MAX_CODE_LEN; ++i)
    {
        codeCount[i] = 0;
        base[i]      = 0xffffffffffffffffULL;
        offset[i]    = 0;
    }

    //
    // Count the number of codes, the min/max code lengths, the number of
    // codes with each length, and record symbols with non-zero code
    // length as we find them.
    //

    const char *currByte     = table;
    Int64       currBits     = 0;
    int         currBitCount = 0;

    const int SHORT_ZEROCODE_RUN = 59;
    const int LONG_ZEROCODE_RUN  = 63;
    const int SHORTEST_LONG_RUN  = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;

    for (Int64 symbol = static_cast<Int64>(minSymbol); symbol <= static_cast<Int64>(maxSymbol); symbol++)
    {
        if (currByte - table > numBytes)
        {
            throw IEX_NAMESPACE::InputExc ("Error decoding Huffman table "
                                           "(Truncated table data).");
        }

        //
        // Next code length - either:
        //       0-58  (literal code length)
        //       59-62 (various lengths runs of 0)
        //       63    (run of n 0's, with n is the next 8 bits)
        //

        Int64 codeLen = readBits (6, currBits, currBitCount, currByte);

        if (codeLen == (Int64) LONG_ZEROCODE_RUN)
        {
            if (currByte - table > numBytes)
            {
                throw IEX_NAMESPACE::InputExc ("Error decoding Huffman table "
                                               "(Truncated table data).");
            }

            int runLen = readBits (8, currBits, currBitCount, currByte) +
                         SHORTEST_LONG_RUN;

            if (symbol + runLen > static_cast<Int64>(maxSymbol + 1))
            {
                throw IEX_NAMESPACE::InputExc ("Error decoding Huffman table "
                                               "(Run beyond end of table).");
            }

            symbol += runLen - 1;

        }
        else if (codeLen >= static_cast<Int64>(SHORT_ZEROCODE_RUN))
        {
            int runLen = codeLen - SHORT_ZEROCODE_RUN + 2;

            if (symbol + runLen > static_cast<Int64>(maxSymbol + 1))
            {
                throw IEX_NAMESPACE::InputExc ("Error decoding Huffman table "
                                               "(Run beyond end of table).");
            }

            symbol += runLen - 1;

        }
        else if (codeLen != 0)
        {
            symbols.push_back ((symbol << 6) | (codeLen & 63));

            if (codeLen < _minCodeLength)
                _minCodeLength = codeLen;

            if (codeLen > _maxCodeLength)
                _maxCodeLength = codeLen;

            codeCount[codeLen]++;
        }
    }

    for (int i = 0; i < MAX_CODE_LEN; ++i)
        _numSymbols += codeCount[i];

    table = currByte;

    //
    // Compute base - once we have the code length counts, there
    //                is a closed form solution for this
    //

    {
        double* countTmp = new double[_maxCodeLength+1];

        for (int l = _minCodeLength; l <= _maxCodeLength; ++l)
        {
            countTmp[l] = (double)codeCount[l] *
                          (double)(2ll << (_maxCodeLength-l));
        }

        for (int l = _minCodeLength; l <= _maxCodeLength; ++l)
        {
            double tmp = 0;

            for (int k =l + 1; k <= _maxCodeLength; ++k)
                tmp += countTmp[k];

            tmp /= (double)(2ll << (_maxCodeLength - l));

            base[l] = (Int64)ceil (tmp);
        }

        delete [] countTmp;
    }

    //
    // Compute offset - these are the positions of the first
    //                  id (not symbol) that has length [i]
    //

    offset[_maxCodeLength] = 0;

    for (int i= _maxCodeLength - 1; i >= _minCodeLength; i--)
        offset[i] = offset[i + 1] + codeCount[i + 1];

    //
    // Allocate and fill the symbol-to-id mapping. Smaller Ids should be
    // mapped to less-frequent symbols (which have longer codes). Use
    // the offset table to tell us where the id's for a given code
    // length start off.
    //

    _idToSymbol = new int[_numSymbols];

    Int64 mapping[MAX_CODE_LEN + 1];
    for (int i = 0; i < MAX_CODE_LEN + 1; ++i)
        mapping[i] = -1;
    for (int i = _minCodeLength; i <= _maxCodeLength; ++i)
        mapping[i] = offset[i];

    for (std::vector<Int64>::const_iterator i = symbols.begin();
         i != symbols.end();
         ++i)
    {
        int codeLen = *i & 63;
        int symbol  = *i >> 6;

        if (mapping[codeLen] >= static_cast<Int64>(_numSymbols))
        {
            delete[] _idToSymbol;
            _idToSymbol = NULL;
            throw IEX_NAMESPACE::InputExc ("Huffman decode error "
                                           "(Invalid symbol in header).");
        }
        _idToSymbol[mapping[codeLen]] = symbol;
        mapping[codeLen]++;
    }

    //
    // exceptions can be thrown whilst building tables. Delete
    // _idToSynmbol before re-throwing to prevent memory leak
    //
    try
    {
      buildTables(base, offset);
    }catch(...)
    {
            delete[] _idToSymbol;
            _idToSymbol = NULL;
            throw;
    }
}


static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool compress TSRMLS_DC) /* {{{ */
{
	const char *oldname = NULL;
	char *oldpath = NULL;
	char *basename = NULL, *basepath = NULL;
	char *newname = NULL, *newpath = NULL;
	zval *ret, arg1;
	zend_class_entry *ce;
	char *error;
	const char *pcr_error;
	int ext_len = ext ? strlen(ext) : 0;
	int oldname_len;
	phar_archive_data **pphar = NULL;
	php_stream_statbuf ssb;

	if (!ext) {
		if (phar->is_zip) {

			if (phar->is_data) {
				ext = "zip";
			} else {
				ext = "phar.zip";
			}

		} else if (phar->is_tar) {

			switch (phar->flags) {
				case PHAR_FILE_COMPRESSED_GZ:
					if (phar->is_data) {
						ext = "tar.gz";
					} else {
						ext = "phar.tar.gz";
					}
					break;
				case PHAR_FILE_COMPRESSED_BZ2:
					if (phar->is_data) {
						ext = "tar.bz2";
					} else {
						ext = "phar.tar.bz2";
					}
					break;
				default:
					if (phar->is_data) {
						ext = "tar";
					} else {
						ext = "phar.tar";
					}
			}
		} else {

			switch (phar->flags) {
				case PHAR_FILE_COMPRESSED_GZ:
					ext = "phar.gz";
					break;
				case PHAR_FILE_COMPRESSED_BZ2:
					ext = "phar.bz2";
					break;
				default:
					ext = "phar";
			}
		}
	} else if (phar_path_check(&ext, &ext_len, &pcr_error) > pcr_is_ok) {

		if (phar->is_data) {
			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar converted from \"%s\" has invalid extension %s", phar->fname, ext);
		} else {
			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar converted from \"%s\" has invalid extension %s", phar->fname, ext);
		}
		return NULL;
	}

	if (ext[0] == '.') {
		++ext;
	}

	oldpath = estrndup(phar->fname, phar->fname_len);
	oldname = zend_memrchr(phar->fname, '/', phar->fname_len);
	++oldname;
	oldname_len = strlen(oldname);

	basename = estrndup(oldname, oldname_len);
	spprintf(&newname, 0, "%s.%s", strtok(basename, "."), ext);
	efree(basename);

	basepath = estrndup(oldpath, (strlen(oldpath) - oldname_len));
	phar->fname_len = spprintf(&newpath, 0, "%s%s", basepath, newname);
	phar->fname = newpath;
	phar->ext = newpath + phar->fname_len - strlen(ext) - 1;
	efree(basepath);
	efree(newname);

	if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, newpath, phar->fname_len, (void **) &pphar)) {
		efree(oldpath);
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars, new phar name is in phar.cache_list", phar->fname);
		return NULL;
	}

	if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), newpath, phar->fname_len, (void **) &pphar)) {
		if ((*pphar)->fname_len == phar->fname_len && !memcmp((*pphar)->fname, phar->fname, phar->fname_len)) {
			if (!zend_hash_num_elements(&phar->manifest)) {
				(*pphar)->is_tar = phar->is_tar;
				(*pphar)->is_zip = phar->is_zip;
				(*pphar)->is_data = phar->is_data;
				(*pphar)->flags = phar->flags;
				(*pphar)->fp = phar->fp;
				phar->fp = NULL;
				phar_destroy_phar_data(phar TSRMLS_CC);
				phar = *pphar;
				phar->refcount++;
				newpath = oldpath;
				goto its_ok;
			}
		}

		efree(oldpath);
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars, a phar with that name already exists", phar->fname);
		return NULL;
	}
its_ok:
	if (SUCCESS == php_stream_stat_path(newpath, &ssb)) {
		efree(oldpath);
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar \"%s\" exists and must be unlinked prior to conversion", newpath);
		return NULL;
	}
	if (!phar->is_data) {
		if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 1, 1, 1 TSRMLS_CC)) {
			efree(oldpath);
			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar \"%s\" has invalid extension %s", phar->fname, ext);
			return NULL;
		}

		if (phar->alias) {
			if (phar->is_temporary_alias) {
				phar->alias = NULL;
				phar->alias_len = 0;
			} else {
				phar->alias = estrndup(newpath, strlen(newpath));
				phar->alias_len = strlen(newpath);
				phar->is_temporary_alias = 1;
				zend_hash_update(&(PHAR_GLOBALS->phar_alias_map), newpath, phar->fname_len, (void*)&phar, sizeof(phar_archive_data*), NULL);
			}
		}

	} else {

		if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 0, 1, 1 TSRMLS_CC)) {
			efree(oldpath);
			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar \"%s\" has invalid extension %s", phar->fname, ext);
			return NULL;
		}

		phar->alias = NULL;
		phar->alias_len = 0;
	}

	if ((!pphar || phar == *pphar) && SUCCESS != zend_hash_update(&(PHAR_GLOBALS->phar_fname_map), newpath, phar->fname_len, (void*)&phar, sizeof(phar_archive_data*), NULL)) {
		efree(oldpath);
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars", phar->fname);
		return NULL;
	}

	phar_flush(phar, 0, 0, 1, &error TSRMLS_CC);

	if (error) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s", error);
		efree(error);
		efree(oldpath);
		return NULL;
	}

	efree(oldpath);

	if (phar->is_data) {
		ce = phar_ce_data;
	} else {
		ce = phar_ce_archive;
	}

	MAKE_STD_ZVAL(ret);

	if (SUCCESS != object_init_ex(ret, ce)) {
		zval_dtor(ret);
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate phar object when converting archive \"%s\"", phar->fname);
		return NULL;
	}

	INIT_PZVAL(&arg1);
	ZVAL_STRINGL(&arg1, phar->fname, phar->fname_len, 0);

	zend_call_method_with_1_params(&ret, ce, &ce->constructor, "__construct", NULL, &arg1);
	return ret;
}

Fixed bug #68901 (use after free)


static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool compress TSRMLS_DC) /* {{{ */
{
	const char *oldname = NULL;
	char *oldpath = NULL;
	char *basename = NULL, *basepath = NULL;
	char *newname = NULL, *newpath = NULL;
	zval *ret, arg1;
	zend_class_entry *ce;
	char *error;
	const char *pcr_error;
	int ext_len = ext ? strlen(ext) : 0;
	int oldname_len;
	phar_archive_data **pphar = NULL;
	php_stream_statbuf ssb;

	if (!ext) {
		if (phar->is_zip) {

			if (phar->is_data) {
				ext = "zip";
			} else {
				ext = "phar.zip";
			}

		} else if (phar->is_tar) {

			switch (phar->flags) {
				case PHAR_FILE_COMPRESSED_GZ:
					if (phar->is_data) {
						ext = "tar.gz";
					} else {
						ext = "phar.tar.gz";
					}
					break;
				case PHAR_FILE_COMPRESSED_BZ2:
					if (phar->is_data) {
						ext = "tar.bz2";
					} else {
						ext = "phar.tar.bz2";
					}
					break;
				default:
					if (phar->is_data) {
						ext = "tar";
					} else {
						ext = "phar.tar";
					}
			}
		} else {

			switch (phar->flags) {
				case PHAR_FILE_COMPRESSED_GZ:
					ext = "phar.gz";
					break;
				case PHAR_FILE_COMPRESSED_BZ2:
					ext = "phar.bz2";
					break;
				default:
					ext = "phar";
			}
		}
	} else if (phar_path_check(&ext, &ext_len, &pcr_error) > pcr_is_ok) {

		if (phar->is_data) {
			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar converted from \"%s\" has invalid extension %s", phar->fname, ext);
		} else {
			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar converted from \"%s\" has invalid extension %s", phar->fname, ext);
		}
		return NULL;
	}

	if (ext[0] == '.') {
		++ext;
	}

	oldpath = estrndup(phar->fname, phar->fname_len);
	oldname = zend_memrchr(phar->fname, '/', phar->fname_len);
	++oldname;
	oldname_len = strlen(oldname);

	basename = estrndup(oldname, oldname_len);
	spprintf(&newname, 0, "%s.%s", strtok(basename, "."), ext);
	efree(basename);

	basepath = estrndup(oldpath, (strlen(oldpath) - oldname_len));
	phar->fname_len = spprintf(&newpath, 0, "%s%s", basepath, newname);
	phar->fname = newpath;
	phar->ext = newpath + phar->fname_len - strlen(ext) - 1;
	efree(basepath);
	efree(newname);

	if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, newpath, phar->fname_len, (void **) &pphar)) {
		efree(oldpath);
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars, new phar name is in phar.cache_list", phar->fname);
		return NULL;
	}

	if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), newpath, phar->fname_len, (void **) &pphar)) {
		if ((*pphar)->fname_len == phar->fname_len && !memcmp((*pphar)->fname, phar->fname, phar->fname_len)) {
			if (!zend_hash_num_elements(&phar->manifest)) {
				(*pphar)->is_tar = phar->is_tar;
				(*pphar)->is_zip = phar->is_zip;
				(*pphar)->is_data = phar->is_data;
				(*pphar)->flags = phar->flags;
				(*pphar)->fp = phar->fp;
				phar->fp = NULL;
				phar_destroy_phar_data(phar TSRMLS_CC);
				phar = *pphar;
				phar->refcount++;
				newpath = oldpath;
				goto its_ok;
			}
		}

		efree(oldpath);
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars, a phar with that name already exists", phar->fname);
		return NULL;
	}
its_ok:
	if (SUCCESS == php_stream_stat_path(newpath, &ssb)) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar \"%s\" exists and must be unlinked prior to conversion", newpath);
		efree(oldpath);
		return NULL;
	}
	if (!phar->is_data) {
		if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 1, 1, 1 TSRMLS_CC)) {
			efree(oldpath);
			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar \"%s\" has invalid extension %s", phar->fname, ext);
			return NULL;
		}

		if (phar->alias) {
			if (phar->is_temporary_alias) {
				phar->alias = NULL;
				phar->alias_len = 0;
			} else {
				phar->alias = estrndup(newpath, strlen(newpath));
				phar->alias_len = strlen(newpath);
				phar->is_temporary_alias = 1;
				zend_hash_update(&(PHAR_GLOBALS->phar_alias_map), newpath, phar->fname_len, (void*)&phar, sizeof(phar_archive_data*), NULL);
			}
		}

	} else {

		if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 0, 1, 1 TSRMLS_CC)) {
			efree(oldpath);
			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar \"%s\" has invalid extension %s", phar->fname, ext);
			return NULL;
		}

		phar->alias = NULL;
		phar->alias_len = 0;
	}

	if ((!pphar || phar == *pphar) && SUCCESS != zend_hash_update(&(PHAR_GLOBALS->phar_fname_map), newpath, phar->fname_len, (void*)&phar, sizeof(phar_archive_data*), NULL)) {
		efree(oldpath);
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars", phar->fname);
		return NULL;
	}

	phar_flush(phar, 0, 0, 1, &error TSRMLS_CC);

	if (error) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s", error);
		efree(error);
		efree(oldpath);
		return NULL;
	}

	efree(oldpath);

	if (phar->is_data) {
		ce = phar_ce_data;
	} else {
		ce = phar_ce_archive;
	}

	MAKE_STD_ZVAL(ret);

	if (SUCCESS != object_init_ex(ret, ce)) {
		zval_dtor(ret);
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate phar object when converting archive \"%s\"", phar->fname);
		return NULL;
	}

	INIT_PZVAL(&arg1);
	ZVAL_STRINGL(&arg1, phar->fname, phar->fname_len, 0);

	zend_call_method_with_1_params(&ret, ce, &ce->constructor, "__construct", NULL, &arg1);
	return ret;
}

Fixed bug #68901 (use after free)


rndr_quote(struct buf *ob, const struct buf *text, void *opaque)
{
	if (!text || !text->size)
		return 0;

	BUFPUTSL(ob, "<q>");
	bufput(ob, text->data, text->size);
	BUFPUTSL(ob, "</q>");

	return 1;
}

Fix a security issue using `:quote` with `:escape_html`

Reported by @johan-smits.


rndr_underline(struct buf *ob, const struct buf *text, void *opaque)
{
	if (!text || !text->size)
		return 0;

	BUFPUTSL(ob, "<u>");
	bufput(ob, text->data, text->size);
	BUFPUTSL(ob, "</u>");

	return 1;
}


ppm_load_read_header(FILE       *fp,
                     pnm_struct *img)
{
    /* PPM Headers Variable Declaration */
    gchar *ptr;
    //gchar *retval;
    gchar  header[MAX_CHARS_IN_ROW];
    gint   maxval;

    /* Check the PPM file Type P2 or P5 */
    fgets (header,MAX_CHARS_IN_ROW,fp);

    if (header[0] != ASCII_P ||
        (header[1] != PIXMAP_ASCII &&
         header[1] != PIXMAP_RAW))
      {
        g_warning ("Image is not a portable pixmap");
        return FALSE;
      }

    img->type = header[1];

    /* Check the Comments */
    fgets (header,MAX_CHARS_IN_ROW,fp);
    while(header[0] == '#')
      {
        fgets (header,MAX_CHARS_IN_ROW,fp);
      }

    /* Get Width and Height */
    img->width  = strtol (header,&ptr,0);
    img->height = atoi (ptr);

    fgets (header,MAX_CHARS_IN_ROW,fp);
    maxval = strtol (header,&ptr,0);

    if ((maxval != 255) && (maxval != 65535))
      {
        g_warning ("Image is not an 8-bit or 16-bit portable pixmap");
        return FALSE;
      }

  switch (maxval)
    {
    case 255:
      img->bpc = sizeof (guchar);
      break;

    case 65535:
      img->bpc = sizeof (gushort);
      break;

    default:
      g_warning ("%s: Programmer stupidity error", G_STRLOC);
    }

    /* Later on, img->numsamples is multiplied with img->bpc to allocate
     * memory. Ensure it doesn't overflow. */
    if (!img->width || !img->height ||
        G_MAXSIZE / img->width / img->height / CHANNEL_COUNT < img->bpc)
      {
        g_warning ("Illegal width/height: %ld/%ld", img->width, img->height);
        return FALSE;
      }
    img->numsamples = img->width * img->height * CHANNEL_COUNT;

    return TRUE;
}

ppm-load: CVE-2012-4433: add plausibility checks for header fields

Refuse values that are non-decimal, negative or overflow the target
type.


ppm_load_read_header(FILE       *fp,
                     pnm_struct *img)
{
    /* PPM Headers Variable Declaration */
    gchar *ptr;
    //gchar *retval;
    gchar  header[MAX_CHARS_IN_ROW];
    gint   maxval;

    /* Check the PPM file Type P2 or P5 */
    fgets (header,MAX_CHARS_IN_ROW,fp);

    if (header[0] != ASCII_P ||
        (header[1] != PIXMAP_ASCII &&
         header[1] != PIXMAP_RAW))
      {
        g_warning ("Image is not a portable pixmap");
        return FALSE;
      }

    img->type = header[1];

    /* Check the Comments */
    fgets (header,MAX_CHARS_IN_ROW,fp);
    while(header[0] == '#')
      {
        fgets (header,MAX_CHARS_IN_ROW,fp);
      }

    /* Get Width and Height */
    errno = 0;
    img->width  = strtol (header,&ptr,10);
    if (errno)
      {
        g_warning ("Error reading width: %s", strerror(errno));
        return FALSE;
      }
    else if (img->width < 0)
      {
        g_warning ("Error: width is negative");
        return FALSE;
      }

    img->height = strtol (ptr,&ptr,10);
    if (errno)
      {
        g_warning ("Error reading height: %s", strerror(errno));
        return FALSE;
      }
    else if (img->width < 0)
      {
        g_warning ("Error: height is negative");
        return FALSE;
      }

    fgets (header,MAX_CHARS_IN_ROW,fp);
    maxval = strtol (header,&ptr,10);

    if ((maxval != 255) && (maxval != 65535))
      {
        g_warning ("Image is not an 8-bit or 16-bit portable pixmap");
        return FALSE;
      }

  switch (maxval)
    {
    case 255:
      img->bpc = sizeof (guchar);
      break;

    case 65535:
      img->bpc = sizeof (gushort);
      break;

    default:
      g_warning ("%s: Programmer stupidity error", G_STRLOC);
    }

    /* Later on, img->numsamples is multiplied with img->bpc to allocate
     * memory. Ensure it doesn't overflow. */
    if (!img->width || !img->height ||
        G_MAXSIZE / img->width / img->height / CHANNEL_COUNT < img->bpc)
      {
        g_warning ("Illegal width/height: %ld/%ld", img->width, img->height);
        return FALSE;
      }
    img->numsamples = img->width * img->height * CHANNEL_COUNT;

    return TRUE;
}


enum_func_status
php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
									unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
									zend_bool as_int_or_float, zend_bool copy_data, MYSQLND_STATS * stats TSRMLS_DC)
{

	unsigned int i;
	zend_bool last_field_was_string = FALSE;
	zval **current_field, **end_field, **start_field;
	zend_uchar * p = row_buffer->ptr;
	size_t data_size = row_buffer->app;
	zend_uchar * bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */

	DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_aux");

	if (!fields) {
		DBG_RETURN(FAIL);
	}

	end_field = (start_field = fields) + field_count;

	for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
		DBG_INF("Directly creating zval");
		MAKE_STD_ZVAL(*current_field);
		if (!*current_field) {
			DBG_RETURN(FAIL);
		}
	}

	for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
		/* Don't reverse the order. It is significant!*/
		zend_uchar *this_field_len_pos = p;
		/* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
		unsigned long len = php_mysqlnd_net_field_length(&p);

		if (copy_data == FALSE && current_field > start_field && last_field_was_string) {
			/*
			  Normal queries:
			  We have to put \0 now to the end of the previous field, if it was
			  a string. IS_NULL doesn't matter. Because we have already read our
			  length, then we can overwrite it in the row buffer.
			  This statement terminates the previous field, not the current one.

			  NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
			  Any string's length is encoded in at least one byte, so we can stick
			  a \0 there.
			*/

			*this_field_len_pos = '\0';
		}

		/* NULL or NOT NULL, this is the question! */
		if (len == MYSQLND_NULL_LENGTH) {
			ZVAL_NULL(*current_field);
			last_field_was_string = FALSE;
		} else {
#if defined(MYSQLND_STRING_TO_INT_CONVERSION)
			struct st_mysqlnd_perm_bind perm_bind =
					mysqlnd_ps_fetch_functions[fields_metadata[i].type];
#endif
			if (MYSQLND_G(collect_statistics)) {
				enum_mysqlnd_collected_stats statistic;
				switch (fields_metadata[i].type) {
					case MYSQL_TYPE_DECIMAL:	statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
					case MYSQL_TYPE_TINY:		statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
					case MYSQL_TYPE_SHORT:		statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
					case MYSQL_TYPE_LONG:		statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
					case MYSQL_TYPE_FLOAT:		statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
					case MYSQL_TYPE_DOUBLE:		statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
					case MYSQL_TYPE_NULL:		statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
					case MYSQL_TYPE_TIMESTAMP:	statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
					case MYSQL_TYPE_LONGLONG:	statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
					case MYSQL_TYPE_INT24:		statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
					case MYSQL_TYPE_DATE:		statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
					case MYSQL_TYPE_TIME:		statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
					case MYSQL_TYPE_DATETIME:	statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
					case MYSQL_TYPE_YEAR:		statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
					case MYSQL_TYPE_NEWDATE:	statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
					case MYSQL_TYPE_VARCHAR:	statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
					case MYSQL_TYPE_BIT:		statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
					case MYSQL_TYPE_NEWDECIMAL:	statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
					case MYSQL_TYPE_ENUM:		statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
					case MYSQL_TYPE_SET:		statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
					case MYSQL_TYPE_JSON:		statistic = STAT_TEXT_TYPE_FETCHED_JSON; break;
					case MYSQL_TYPE_TINY_BLOB:	statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
					case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
					case MYSQL_TYPE_LONG_BLOB:	statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
					case MYSQL_TYPE_BLOB:		statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
					case MYSQL_TYPE_VAR_STRING:	statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
					case MYSQL_TYPE_STRING:		statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
					case MYSQL_TYPE_GEOMETRY:	statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
					default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
				}
				MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1, STAT_BYTES_RECEIVED_PURE_DATA_TEXT, len);
			}
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
			if (as_int_or_float && perm_bind.php_type == IS_LONG) {
				zend_uchar save = *(p + len);
				/* We have to make it ASCIIZ temporarily */
				*(p + len) = '\0';
				if (perm_bind.pack_len < SIZEOF_LONG) {
					/* direct conversion */
					int64_t v =
#ifndef PHP_WIN32
						atoll((char *) p);
#else
						_atoi64((char *) p);
#endif
					ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
				} else {
					uint64_t v =
#ifndef PHP_WIN32
						(uint64_t) atoll((char *) p);
#else
						(uint64_t) _atoi64((char *) p);
#endif
					zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
					/* We have to make it ASCIIZ temporarily */
#if SIZEOF_LONG==8
					if (uns == TRUE && v > 9223372036854775807L)
#elif SIZEOF_LONG==4
					if ((uns == TRUE && v > L64(2147483647)) ||
						(uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
						(L64(-2147483648) > (int64_t) v))))
#else
#error Need fix for this architecture
#endif /* SIZEOF */
					{
						ZVAL_STRINGL(*current_field, (char *)p, len, 0);
					} else {
						ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
					}
				}
				*(p + len) = save;
			} else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
				zend_uchar save = *(p + len);
				/* We have to make it ASCIIZ temporarily */
				*(p + len) = '\0';
				ZVAL_DOUBLE(*current_field, atof((char *) p));
				*(p + len) = save;
			} else
#endif /* MYSQLND_STRING_TO_INT_CONVERSION */
			if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
				/*
				  BIT fields are specially handled. As they come as bit mask, we have
				  to convert it to human-readable representation. As the bits take
				  less space in the protocol than the numbers they represent, we don't
				  have enough space in the packet buffer to overwrite inside.
				  Thus, a bit more space is pre-allocated at the end of the buffer,
				  see php_mysqlnd_rowp_read(). And we add the strings at the end.
				  Definitely not nice, _hackish_ :(, but works.
				*/
				zend_uchar *start = bit_area;
				ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), 0, &p, len TSRMLS_CC);
				/*
				  We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
				  later in this function there will be an advancement.
				*/
				p -= len;
				if (Z_TYPE_PP(current_field) == IS_LONG) {
					bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
					ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, copy_data);
				} else if (Z_TYPE_PP(current_field) == IS_STRING){
					memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
					bit_area += Z_STRLEN_PP(current_field);
					*bit_area++ = '\0';
					zval_dtor(*current_field);
					ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, copy_data);
				}
			} else {
				ZVAL_STRINGL(*current_field, (char *)p, len, copy_data);
			}
			p += len;
			last_field_was_string = TRUE;
		}
	}
	if (copy_data == FALSE && last_field_was_string) {
		/* Normal queries: The buffer has one more byte at the end, because we need it */
		row_buffer->ptr[data_size] = '\0';
	}

	DBG_RETURN(PASS);

Fix bug #72293 - Heap overflow in mysqlnd related to BIT fields


enum_func_status
php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
									unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
									zend_bool as_int_or_float, zend_bool copy_data, MYSQLND_STATS * stats TSRMLS_DC)
{

	unsigned int i;
	zend_bool last_field_was_string = FALSE;
	zval **current_field, **end_field, **start_field;
	zend_uchar * p = row_buffer->ptr;
	size_t data_size = row_buffer->app;
	zend_uchar * bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
	const zend_uchar * const packet_end = (zend_uchar*) row_buffer->ptr + data_size;

	DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_aux");

	if (!fields) {
		DBG_RETURN(FAIL);
	}

	end_field = (start_field = fields) + field_count;

	for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
		DBG_INF("Directly creating zval");
		MAKE_STD_ZVAL(*current_field);
		if (!*current_field) {
			DBG_RETURN(FAIL);
		}
	}

	for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
		/* Don't reverse the order. It is significant!*/
		zend_uchar *this_field_len_pos = p;
		/* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
		const unsigned long len = php_mysqlnd_net_field_length(&p);

		if (len != MYSQLND_NULL_LENGTH && ((p + len) > packet_end)) {
			php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing "MYSQLND_SZ_T_SPEC
											  " bytes after end of packet", (p + len) - packet_end - 1);
			DBG_RETURN(FAIL);
		}
		if (copy_data == FALSE && current_field > start_field && last_field_was_string) {
			/*
			  Normal queries:
			  We have to put \0 now to the end of the previous field, if it was
			  a string. IS_NULL doesn't matter. Because we have already read our
			  length, then we can overwrite it in the row buffer.
			  This statement terminates the previous field, not the current one.

			  NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
			  Any string's length is encoded in at least one byte, so we can stick
			  a \0 there.
			*/

			*this_field_len_pos = '\0';
		}

		/* NULL or NOT NULL, this is the question! */
		if (len == MYSQLND_NULL_LENGTH) {
			ZVAL_NULL(*current_field);
			last_field_was_string = FALSE;
		} else {
#if defined(MYSQLND_STRING_TO_INT_CONVERSION)
			struct st_mysqlnd_perm_bind perm_bind =
					mysqlnd_ps_fetch_functions[fields_metadata[i].type];
#endif
			if (MYSQLND_G(collect_statistics)) {
				enum_mysqlnd_collected_stats statistic;
				switch (fields_metadata[i].type) {
					case MYSQL_TYPE_DECIMAL:	statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
					case MYSQL_TYPE_TINY:		statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
					case MYSQL_TYPE_SHORT:		statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
					case MYSQL_TYPE_LONG:		statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
					case MYSQL_TYPE_FLOAT:		statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
					case MYSQL_TYPE_DOUBLE:		statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
					case MYSQL_TYPE_NULL:		statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
					case MYSQL_TYPE_TIMESTAMP:	statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
					case MYSQL_TYPE_LONGLONG:	statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
					case MYSQL_TYPE_INT24:		statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
					case MYSQL_TYPE_DATE:		statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
					case MYSQL_TYPE_TIME:		statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
					case MYSQL_TYPE_DATETIME:	statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
					case MYSQL_TYPE_YEAR:		statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
					case MYSQL_TYPE_NEWDATE:	statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
					case MYSQL_TYPE_VARCHAR:	statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
					case MYSQL_TYPE_BIT:		statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
					case MYSQL_TYPE_NEWDECIMAL:	statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
					case MYSQL_TYPE_ENUM:		statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
					case MYSQL_TYPE_SET:		statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
					case MYSQL_TYPE_JSON:		statistic = STAT_TEXT_TYPE_FETCHED_JSON; break;
					case MYSQL_TYPE_TINY_BLOB:	statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
					case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
					case MYSQL_TYPE_LONG_BLOB:	statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
					case MYSQL_TYPE_BLOB:		statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
					case MYSQL_TYPE_VAR_STRING:	statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
					case MYSQL_TYPE_STRING:		statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
					case MYSQL_TYPE_GEOMETRY:	statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
					default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
				}
				MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1, STAT_BYTES_RECEIVED_PURE_DATA_TEXT, len);
			}
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
			if (as_int_or_float && perm_bind.php_type == IS_LONG) {
				zend_uchar save = *(p + len);
				/* We have to make it ASCIIZ temporarily */
				*(p + len) = '\0';
				if (perm_bind.pack_len < SIZEOF_LONG) {
					/* direct conversion */
					int64_t v =
#ifndef PHP_WIN32
						atoll((char *) p);
#else
						_atoi64((char *) p);
#endif
					ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
				} else {
					uint64_t v =
#ifndef PHP_WIN32
						(uint64_t) atoll((char *) p);
#else
						(uint64_t) _atoi64((char *) p);
#endif
					zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
					/* We have to make it ASCIIZ temporarily */
#if SIZEOF_LONG==8
					if (uns == TRUE && v > 9223372036854775807L)
#elif SIZEOF_LONG==4
					if ((uns == TRUE && v > L64(2147483647)) ||
						(uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
						(L64(-2147483648) > (int64_t) v))))
#else
#error Need fix for this architecture
#endif /* SIZEOF */
					{
						ZVAL_STRINGL(*current_field, (char *)p, len, 0);
					} else {
						ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
					}
				}
				*(p + len) = save;
			} else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
				zend_uchar save = *(p + len);
				/* We have to make it ASCIIZ temporarily */
				*(p + len) = '\0';
				ZVAL_DOUBLE(*current_field, atof((char *) p));
				*(p + len) = save;
			} else
#endif /* MYSQLND_STRING_TO_INT_CONVERSION */
			if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
				/*
				  BIT fields are specially handled. As they come as bit mask, we have
				  to convert it to human-readable representation. As the bits take
				  less space in the protocol than the numbers they represent, we don't
				  have enough space in the packet buffer to overwrite inside.
				  Thus, a bit more space is pre-allocated at the end of the buffer,
				  see php_mysqlnd_rowp_read(). And we add the strings at the end.
				  Definitely not nice, _hackish_ :(, but works.
				*/
				zend_uchar *start = bit_area;
				ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), 0, &p, len TSRMLS_CC);
				/*
				  We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
				  later in this function there will be an advancement.
				*/
				p -= len;
				if (Z_TYPE_PP(current_field) == IS_LONG) {
					bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
					ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, copy_data);
				} else if (Z_TYPE_PP(current_field) == IS_STRING){
					memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
					bit_area += Z_STRLEN_PP(current_field);
					*bit_area++ = '\0';
					zval_dtor(*current_field);
					ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, copy_data);
				}
			} else {
				ZVAL_STRINGL(*current_field, (char *)p, len, copy_data);
			}
			p += len;
			last_field_was_string = TRUE;
		}
	}
	if (copy_data == FALSE && last_field_was_string) {
		/* Normal queries: The buffer has one more byte at the end, because we need it */
		row_buffer->ptr[data_size] = '\0';
	}

	DBG_RETURN(PASS);


static void cil_reset_classperms_set(struct cil_classperms_set *cp_set)
{
	cil_reset_classpermission(cp_set->set);
}

libsepol/cil: cil_reset_classperms_set() should not reset classpermission

In struct cil_classperms_set, the set field is a pointer to a
struct cil_classpermission which is looked up in the symbol table.
Since the cil_classperms_set does not create the cil_classpermission,
it should not reset it.

Set the set field to NULL instead of resetting the classpermission
that it points to.

Signed-off-by: James Carter <jwcart2@gmail.com>


static void cil_reset_classpermissionset(struct cil_classpermissionset *cps)
{
	cil_reset_classperms_list(cps->classperms);
}


void APar_ExtractDetails(FILE *isofile, uint8_t optional_output) {
  char uint32_buffer[5];
  Trackage track = {0};

  AtomicInfo *mvhdAtom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0);
  if (mvhdAtom != NULL) {
    APar_ExtractMovieDetails(uint32_buffer, isofile, mvhdAtom);
    fprintf(stdout,
            "Movie duration: %.3lf seconds (%s) - %.2lf* kbp/sec bitrate "
            "(*=approximate)\n",
            movie_info.seconds,
            secsTOtime(movie_info.seconds),
            movie_info.simple_bitrate_calc);
    if (optional_output & SHOW_DATE_INFO) {
      fprintf(stdout,
              "  Presentation Creation Date (UTC):     %s\n",
              APar_extract_UTC(movie_info.creation_time));
      fprintf(stdout,
              "  Presentation Modification Date (UTC): %s\n",
              APar_extract_UTC(movie_info.modified_time));
    }
  }

  AtomicInfo *iodsAtom = APar_FindAtom("moov.iods", false, VERSIONED_ATOM, 0);
  if (iodsAtom != NULL) {
    movie_info.contains_iods = true;
    APar_Extract_iods_Info(isofile, iodsAtom);
  }

  if (optional_output & SHOW_TRACK_INFO) {
    APar_TrackLevelInfo(&track,
                        NULL); // With track_num set to 0, it will return the
                               // total trak atom into total_tracks here.

    fprintf(
        stdout, "Low-level details. Total tracks: %u\n", track.total_tracks);
    fprintf(stdout,
            "Trk  Type  Handler                    Kind  Lang  Bytes\n");

    if (track.total_tracks > 0) {
      while (track.total_tracks > track.track_num) {
        track.track_num += 1;
        TrackInfo track_info = {0};

        // tracknum, handler type, handler name
        APar_ExtractTrackDetails(uint32_buffer, isofile, &track, &track_info);
        uint16_t more_whitespace =
            purge_extraneous_characters(track_info.track_hdlr_name);

        if (strlen(track_info.track_hdlr_name) == 0) {
          memcpy(track_info.track_hdlr_name, "[none listed]", 13);
        }
        fprintf(stdout,
                "%u    %s  %s",
                track.track_num,
                uint32tochar4(track_info.track_type, uint32_buffer),
                track_info.track_hdlr_name);

        uint16_t handler_len = strlen(track_info.track_hdlr_name);
        if (handler_len < 25 + more_whitespace) {
          for (uint16_t i = handler_len; i < 25 + more_whitespace; i++) {
            fprintf(stdout, " ");
          }
        }

        // codec, language
        fprintf(stdout,
                "  %s  %s   %" PRIu64,
                uint32tochar4(track_info.track_codec, uint32_buffer),
                track_info.unpacked_lang,
                track_info.sample_aggregate);

        if (track_info.encoder_name[0] != 0 && track_info.contains_esds) {
          purge_extraneous_characters(track_info.encoder_name);
          fprintf(stdout, "   Encoder: %s", track_info.encoder_name);
        }
        if (track_info.type_of_track & DRM_PROTECTED_TRACK) {
          fprintf(stdout,
                  " (protected %s)",
                  uint32tochar4(track_info.protected_codec, uint32_buffer));
        }

        fprintf(stdout, "\n");
        /*---------------------------------*/

        if (track_info.type_of_track & VIDEO_TRACK ||
            track_info.type_of_track & AUDIO_TRACK) {
          APar_Print_TrackDetails(&track_info);
        }

        if (optional_output & SHOW_DATE_INFO) {
          fprintf(stdout,
                  "       Creation Date (UTC):     %s\n",
                  APar_extract_UTC(track_info.creation_time));
          fprintf(stdout,
                  "       Modification Date (UTC): %s\n",
                  APar_extract_UTC(track_info.modified_time));
        }
      }
    }
  }
}

Avoid stack overflow

refs: https://github.com/wez/atomicparsley/issues/32


void APar_ExtractDetails(FILE *isofile, uint8_t optional_output) {
  char uint32_buffer[8];
  Trackage track = {0};

  AtomicInfo *mvhdAtom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0);
  if (mvhdAtom != NULL) {
    APar_ExtractMovieDetails(uint32_buffer, isofile, mvhdAtom);
    fprintf(stdout,
            "Movie duration: %.3lf seconds (%s) - %.2lf* kbp/sec bitrate "
            "(*=approximate)\n",
            movie_info.seconds,
            secsTOtime(movie_info.seconds),
            movie_info.simple_bitrate_calc);
    if (optional_output & SHOW_DATE_INFO) {
      fprintf(stdout,
              "  Presentation Creation Date (UTC):     %s\n",
              APar_extract_UTC(movie_info.creation_time));
      fprintf(stdout,
              "  Presentation Modification Date (UTC): %s\n",
              APar_extract_UTC(movie_info.modified_time));
    }
  }

  AtomicInfo *iodsAtom = APar_FindAtom("moov.iods", false, VERSIONED_ATOM, 0);
  if (iodsAtom != NULL) {
    movie_info.contains_iods = true;
    APar_Extract_iods_Info(isofile, iodsAtom);
  }

  if (optional_output & SHOW_TRACK_INFO) {
    APar_TrackLevelInfo(&track,
                        NULL); // With track_num set to 0, it will return the
                               // total trak atom into total_tracks here.

    fprintf(
        stdout, "Low-level details. Total tracks: %u\n", track.total_tracks);
    fprintf(stdout,
            "Trk  Type  Handler                    Kind  Lang  Bytes\n");

    if (track.total_tracks > 0) {
      while (track.total_tracks > track.track_num) {
        track.track_num += 1;
        TrackInfo track_info = {0};

        // tracknum, handler type, handler name
        APar_ExtractTrackDetails(uint32_buffer, isofile, &track, &track_info);
        uint16_t more_whitespace =
            purge_extraneous_characters(track_info.track_hdlr_name);

        if (strlen(track_info.track_hdlr_name) == 0) {
          memcpy(track_info.track_hdlr_name, "[none listed]", 13);
        }
        fprintf(stdout,
                "%u    %s  %s",
                track.track_num,
                uint32tochar4(track_info.track_type, uint32_buffer),
                track_info.track_hdlr_name);

        uint16_t handler_len = strlen(track_info.track_hdlr_name);
        if (handler_len < 25 + more_whitespace) {
          for (uint16_t i = handler_len; i < 25 + more_whitespace; i++) {
            fprintf(stdout, " ");
          }
        }

        // codec, language
        fprintf(stdout,
                "  %s  %s   %" PRIu64,
                uint32tochar4(track_info.track_codec, uint32_buffer),
                track_info.unpacked_lang,
                track_info.sample_aggregate);

        if (track_info.encoder_name[0] != 0 && track_info.contains_esds) {
          purge_extraneous_characters(track_info.encoder_name);
          fprintf(stdout, "   Encoder: %s", track_info.encoder_name);
        }
        if (track_info.type_of_track & DRM_PROTECTED_TRACK) {
          fprintf(stdout,
                  " (protected %s)",
                  uint32tochar4(track_info.protected_codec, uint32_buffer));
        }

        fprintf(stdout, "\n");
        /*---------------------------------*/

        if (track_info.type_of_track & VIDEO_TRACK ||
            track_info.type_of_track & AUDIO_TRACK) {
          APar_Print_TrackDetails(&track_info);
        }

        if (optional_output & SHOW_DATE_INFO) {
          fprintf(stdout,
                  "       Creation Date (UTC):     %s\n",
                  APar_extract_UTC(track_info.creation_time));
          fprintf(stdout,
                  "       Modification Date (UTC): %s\n",
                  APar_extract_UTC(track_info.modified_time));
        }
      }
    }
  }
}


_gnutls_ciphertext2compressed (gnutls_session_t session,
			       opaque * compress_data,
			       int compress_size,
			       gnutls_datum_t ciphertext, uint8_t type)
{
  uint8_t MAC[MAX_HASH_SIZE];
  uint16_t c_length;
  uint8_t pad;
  int length;
  digest_hd_st td;
  uint16_t blocksize;
  int ret, i, pad_failed = 0;
  uint8_t major, minor;
  gnutls_protocol_t ver;
  int hash_size =
    _gnutls_hash_get_algo_len (session->security_parameters.
			       read_mac_algorithm);

  ver = gnutls_protocol_get_version (session);
  minor = _gnutls_version_get_minor (ver);
  major = _gnutls_version_get_major (ver);

  blocksize = _gnutls_cipher_get_block_size (session->security_parameters.
					     read_bulk_cipher_algorithm);

  /* initialize MAC
   */
  ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
		 session->connection_state.read_mac_secret.data,
		 session->connection_state.read_mac_secret.size, ver);

  if (ret < 0
      && session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  if (ciphertext.size < (unsigned) blocksize + hash_size)
    {
      _gnutls_record_log
	("REC[%x]: Short record length %d < %d + %d (under attack?)\n",
	 session, ciphertext.size, blocksize, hash_size);
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

  /* actual decryption (inplace)
   */
  switch (_gnutls_cipher_is_block
	  (session->security_parameters.read_bulk_cipher_algorithm))
    {
    case CIPHER_STREAM:
      if ((ret = _gnutls_cipher_decrypt (&session->connection_state.
					 read_cipher_state,
					 ciphertext.data,
					 ciphertext.size)) < 0)
	{
	  gnutls_assert ();
	  return ret;
	}

      length = ciphertext.size - hash_size;

      break;
    case CIPHER_BLOCK:
      if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0))
	{
	  gnutls_assert ();
	  return GNUTLS_E_DECRYPTION_FAILED;
	}

      if ((ret = _gnutls_cipher_decrypt (&session->connection_state.
					 read_cipher_state,
					 ciphertext.data,
					 ciphertext.size)) < 0)
	{
	  gnutls_assert ();
	  return ret;
	}

      /* ignore the IV in TLS 1.1.
       */
      if (session->security_parameters.version >= GNUTLS_TLS1_1)
	{
	  ciphertext.size -= blocksize;
	  ciphertext.data += blocksize;

	  if (ciphertext.size == 0)
	    {
	      gnutls_assert ();
	      return GNUTLS_E_DECRYPTION_FAILED;
	    }
	}

      pad = ciphertext.data[ciphertext.size - 1] + 1;	/* pad */

      if ((int)pad > (int)ciphertext.size - hash_size)
	{
	  gnutls_assert ();
	  /* We do not fail here. We check below for the
	   * the pad_failed. If zero means success.
	   */
	  pad_failed = GNUTLS_E_DECRYPTION_FAILED;
	}

      length = ciphertext.size - hash_size - pad;

      /* Check the pading bytes (TLS 1.x)
       */
      if (ver >= GNUTLS_TLS1 && pad_failed == 0)
	for (i = 2; i < pad; i++)
	  {
	    if (ciphertext.data[ciphertext.size - i] !=
		ciphertext.data[ciphertext.size - 1])
	      pad_failed = GNUTLS_E_DECRYPTION_FAILED;
	  }
      break;
    default:
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  if (length < 0)
    length = 0;
  c_length = _gnutls_conv_uint16 ((uint16_t) length);

  /* Pass the type, version, length and compressed through
   * MAC.
   */
  if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      _gnutls_hmac (&td,
		    UINT64DATA (session->connection_state.
				read_sequence_number), 8);

      _gnutls_hmac (&td, &type, 1);
      if (ver >= GNUTLS_TLS1)
	{			/* TLS 1.x */
	  _gnutls_hmac (&td, &major, 1);
	  _gnutls_hmac (&td, &minor, 1);
	}
      _gnutls_hmac (&td, &c_length, 2);

      if (length > 0)
	_gnutls_hmac (&td, ciphertext.data, length);

      mac_deinit (&td, MAC, ver);
    }

  /* This one was introduced to avoid a timing attack against the TLS
   * 1.0 protocol.
   */
  if (pad_failed != 0)
    return pad_failed;

  /* HMAC was not the same.
   */
  if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

  /* copy the decrypted stuff to compress_data.
   */
  if (compress_size < length)
    {
      gnutls_assert ();
      return GNUTLS_E_DECOMPRESSION_FAILED;
    }
  memcpy (compress_data, ciphertext.data, length);

  return length;
}

Fix broken debug check for GNUTLS-SA-2008-1.


_gnutls_ciphertext2compressed (gnutls_session_t session,
			       opaque * compress_data,
			       int compress_size,
			       gnutls_datum_t ciphertext, uint8_t type)
{
  uint8_t MAC[MAX_HASH_SIZE];
  uint16_t c_length;
  uint8_t pad;
  int length;
  digest_hd_st td;
  uint16_t blocksize;
  int ret, i, pad_failed = 0;
  uint8_t major, minor;
  gnutls_protocol_t ver;
  int hash_size =
    _gnutls_hash_get_algo_len (session->security_parameters.
			       read_mac_algorithm);

  ver = gnutls_protocol_get_version (session);
  minor = _gnutls_version_get_minor (ver);
  major = _gnutls_version_get_major (ver);

  blocksize = _gnutls_cipher_get_block_size (session->security_parameters.
					     read_bulk_cipher_algorithm);

  /* initialize MAC
   */
  ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
		 session->connection_state.read_mac_secret.data,
		 session->connection_state.read_mac_secret.size, ver);

  if (ret < 0
      && session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  /* actual decryption (inplace)
   */
  switch (_gnutls_cipher_is_block
	  (session->security_parameters.read_bulk_cipher_algorithm))
    {
    case CIPHER_STREAM:
      if ((ret = _gnutls_cipher_decrypt (&session->connection_state.
					 read_cipher_state,
					 ciphertext.data,
					 ciphertext.size)) < 0)
	{
	  gnutls_assert ();
	  return ret;
	}

      length = ciphertext.size - hash_size;

      break;
    case CIPHER_BLOCK:
      if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0))
	{
	  gnutls_assert ();
	  return GNUTLS_E_DECRYPTION_FAILED;
	}

      if ((ret = _gnutls_cipher_decrypt (&session->connection_state.
					 read_cipher_state,
					 ciphertext.data,
					 ciphertext.size)) < 0)
	{
	  gnutls_assert ();
	  return ret;
	}

      /* ignore the IV in TLS 1.1.
       */
      if (session->security_parameters.version >= GNUTLS_TLS1_1)
	{
	  ciphertext.size -= blocksize;
	  ciphertext.data += blocksize;

	  if (ciphertext.size == 0)
	    {
	      gnutls_assert ();
	      return GNUTLS_E_DECRYPTION_FAILED;
	    }
	}

      pad = ciphertext.data[ciphertext.size - 1] + 1;	/* pad */

      if ((int)pad > (int)ciphertext.size - hash_size)
	{
	  gnutls_assert ();
	  _gnutls_record_log
	    ("REC[%x]: Short record length %d > %d - %d (under attack?)\n",
	     session, pad, ciphertext.size, hash_size);
	  /* We do not fail here. We check below for the
	   * the pad_failed. If zero means success.
	   */
	  pad_failed = GNUTLS_E_DECRYPTION_FAILED;
	}

      length = ciphertext.size - hash_size - pad;

      /* Check the pading bytes (TLS 1.x)
       */
      if (ver >= GNUTLS_TLS1 && pad_failed == 0)
	for (i = 2; i < pad; i++)
	  {
	    if (ciphertext.data[ciphertext.size - i] !=
		ciphertext.data[ciphertext.size - 1])
	      pad_failed = GNUTLS_E_DECRYPTION_FAILED;
	  }
      break;
    default:
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  if (length < 0)
    length = 0;
  c_length = _gnutls_conv_uint16 ((uint16_t) length);

  /* Pass the type, version, length and compressed through
   * MAC.
   */
  if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      _gnutls_hmac (&td,
		    UINT64DATA (session->connection_state.
				read_sequence_number), 8);

      _gnutls_hmac (&td, &type, 1);
      if (ver >= GNUTLS_TLS1)
	{			/* TLS 1.x */
	  _gnutls_hmac (&td, &major, 1);
	  _gnutls_hmac (&td, &minor, 1);
	}
      _gnutls_hmac (&td, &c_length, 2);

      if (length > 0)
	_gnutls_hmac (&td, ciphertext.data, length);

      mac_deinit (&td, MAC, ver);
    }

  /* This one was introduced to avoid a timing attack against the TLS
   * 1.0 protocol.
   */
  if (pad_failed != 0)
    return pad_failed;

  /* HMAC was not the same.
   */
  if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

  /* copy the decrypted stuff to compress_data.
   */
  if (compress_size < length)
    {
      gnutls_assert ();
      return GNUTLS_E_DECOMPRESSION_FAILED;
    }
  memcpy (compress_data, ciphertext.data, length);

  return length;
}


bool SFD_GetFontMetaData( FILE *sfd,
			  char *tok,
			  SplineFont *sf,
			  SFD_GetFontMetaDataData* d )
{
    int ch;
    int i;
    KernClass* kc = 0;
    int old;
    char val[2000];

    // This allows us to assume we can dereference d
    // at all times
    static SFD_GetFontMetaDataData my_static_d;
    static int my_static_d_is_virgin = 1;
    if( !d )
    {
	if( my_static_d_is_virgin )
	{
	    my_static_d_is_virgin = 0;
	    SFD_GetFontMetaDataData_Init( &my_static_d );
	}
	d = &my_static_d;
    }

    if ( strmatch(tok,"FontName:")==0 )
    {
	geteol(sfd,val);
	sf->fontname = copy(val);
    }
    else if ( strmatch(tok,"FullName:")==0 )
    {
	geteol(sfd,val);
	sf->fullname = copy(val);
    }
    else if ( strmatch(tok,"FamilyName:")==0 )
    {
	geteol(sfd,val);
	sf->familyname = copy(val);
    }
    else if ( strmatch(tok,"DefaultBaseFilename:")==0 )
    {
	geteol(sfd,val);
	sf->defbasefilename = copy(val);
    }
    else if ( strmatch(tok,"Weight:")==0 )
    {
	getprotectedname(sfd,val);
	sf->weight = copy(val);
    }
    else if ( strmatch(tok,"Copyright:")==0 )
    {
	sf->copyright = getquotedeol(sfd);
    }
    else if ( strmatch(tok,"Comments:")==0 )
    {
	char *temp = getquotedeol(sfd);
	sf->comments = latin1_2_utf8_copy(temp);
	free(temp);
    }
    else if ( strmatch(tok,"UComments:")==0 )
    {
	sf->comments = SFDReadUTF7Str(sfd);
    }
    else if ( strmatch(tok,"FontLog:")==0 )
    {
	sf->fontlog = SFDReadUTF7Str(sfd);
    }
    else if ( strmatch(tok,"Version:")==0 )
    {
	geteol(sfd,val);
	sf->version = copy(val);
    }
    else if ( strmatch(tok,"StyleMapFamilyName:")==0 )
    {
    sf->styleMapFamilyName = SFDReadUTF7Str(sfd);
    }
    /* Legacy attribute for StyleMapFamilyName. Deprecated. */
    else if ( strmatch(tok,"OS2FamilyName:")==0 )
    {
    if (sf->styleMapFamilyName == NULL)
        sf->styleMapFamilyName = SFDReadUTF7Str(sfd);
    }
    else if ( strmatch(tok,"FONDName:")==0 )
    {
	geteol(sfd,val);
	sf->fondname = copy(val);
    }
    else if ( strmatch(tok,"ItalicAngle:")==0 )
    {
	getreal(sfd,&sf->italicangle);
    }
    else if ( strmatch(tok,"StrokeWidth:")==0 )
    {
	getreal(sfd,&sf->strokewidth);
    }
    else if ( strmatch(tok,"UnderlinePosition:")==0 )
    {
	getreal(sfd,&sf->upos);
    }
    else if ( strmatch(tok,"UnderlineWidth:")==0 )
    {
	getreal(sfd,&sf->uwidth);
    }
    else if ( strmatch(tok,"ModificationTime:")==0 )
    {
	getlonglong(sfd,&sf->modificationtime);
    }
    else if ( strmatch(tok,"CreationTime:")==0 )
    {
	getlonglong(sfd,&sf->creationtime);
	d->hadtimes = true;
    }
    else if ( strmatch(tok,"PfmFamily:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->pfminfo.pfmfamily = temp;
	sf->pfminfo.pfmset = true;
    }
    else if ( strmatch(tok,"LangName:")==0 )
    {
	sf->names = SFDGetLangName(sfd,sf->names);
    }
    else if ( strmatch(tok,"GaspTable:")==0 )
    {
	SFDGetGasp(sfd,sf);
    }
    else if ( strmatch(tok,"DesignSize:")==0 )
    {
	SFDGetDesignSize(sfd,sf);
    }
    else if ( strmatch(tok,"OtfFeatName:")==0 )
    {
	SFDGetOtfFeatName(sfd,sf);
    }
    else if ( strmatch(tok,"PfmWeight:")==0 || strmatch(tok,"TTFWeight:")==0 )
    {
	getsint(sfd,&sf->pfminfo.weight);
	sf->pfminfo.pfmset = true;
    }
    else if ( strmatch(tok,"TTFWidth:")==0 )
    {
	getsint(sfd,&sf->pfminfo.width);
	sf->pfminfo.pfmset = true;
    }
    else if ( strmatch(tok,"Panose:")==0 )
    {
	int temp,i;
	for ( i=0; i<10; ++i )
	{
	    getint(sfd,&temp);
	    sf->pfminfo.panose[i] = temp;
	}
	sf->pfminfo.panose_set = true;
    }
    else if ( strmatch(tok,"LineGap:")==0 )
    {
	getsint(sfd,&sf->pfminfo.linegap);
	sf->pfminfo.pfmset = true;
    }
    else if ( strmatch(tok,"VLineGap:")==0 )
    {
	getsint(sfd,&sf->pfminfo.vlinegap);
	sf->pfminfo.pfmset = true;
    }
    else if ( strmatch(tok,"HheadAscent:")==0 )
    {
	getsint(sfd,&sf->pfminfo.hhead_ascent);
    }
    else if ( strmatch(tok,"HheadAOffset:")==0 )
    {
	int temp;
	getint(sfd,&temp); sf->pfminfo.hheadascent_add = temp;
    }
    else if ( strmatch(tok,"HheadDescent:")==0 )
    {
	getsint(sfd,&sf->pfminfo.hhead_descent);
    }
    else if ( strmatch(tok,"HheadDOffset:")==0 )
    {
	int temp;
	getint(sfd,&temp); sf->pfminfo.hheaddescent_add = temp;
    }
    else if ( strmatch(tok,"OS2TypoLinegap:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_typolinegap);
    }
    else if ( strmatch(tok,"OS2TypoAscent:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_typoascent);
    }
    else if ( strmatch(tok,"OS2TypoAOffset:")==0 )
    {
	int temp;
	getint(sfd,&temp); sf->pfminfo.typoascent_add = temp;
    }
    else if ( strmatch(tok,"OS2TypoDescent:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_typodescent);
    }
    else if ( strmatch(tok,"OS2TypoDOffset:")==0 )
    {
	int temp;
	getint(sfd,&temp); sf->pfminfo.typodescent_add = temp;
    }
    else if ( strmatch(tok,"OS2WinAscent:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_winascent);
    }
    else if ( strmatch(tok,"OS2WinDescent:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_windescent);
    }
    else if ( strmatch(tok,"OS2WinAOffset:")==0 )
    {
	int temp;
	getint(sfd,&temp); sf->pfminfo.winascent_add = temp;
    }
    else if ( strmatch(tok,"OS2WinDOffset:")==0 )
    {
	int temp;
	getint(sfd,&temp); sf->pfminfo.windescent_add = temp;
    }
    else if ( strmatch(tok,"HHeadAscent:")==0 )
    {
	// DUPLICATE OF ABOVE
	getsint(sfd,&sf->pfminfo.hhead_ascent);
    }
    else if ( strmatch(tok,"HHeadDescent:")==0 )
    {
	// DUPLICATE OF ABOVE
	getsint(sfd,&sf->pfminfo.hhead_descent);
    }

    else if ( strmatch(tok,"HHeadAOffset:")==0 )
    {
	// DUPLICATE OF ABOVE
	int temp;
	getint(sfd,&temp); sf->pfminfo.hheadascent_add = temp;
    }
    else if ( strmatch(tok,"HHeadDOffset:")==0 )
    {
	// DUPLICATE OF ABOVE
	int temp;
	getint(sfd,&temp); sf->pfminfo.hheaddescent_add = temp;
    }
    else if ( strmatch(tok,"MacStyle:")==0 )
    {
	getsint(sfd,&sf->macstyle);
    }
    else if ( strmatch(tok,"OS2SubXSize:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_subxsize);
	sf->pfminfo.subsuper_set = true;
    }
    else if ( strmatch(tok,"OS2SubYSize:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_subysize);
    }
    else if ( strmatch(tok,"OS2SubXOff:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_subxoff);
    }
    else if ( strmatch(tok,"OS2SubYOff:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_subyoff);
    }
    else if ( strmatch(tok,"OS2SupXSize:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_supxsize);
    }
    else if ( strmatch(tok,"OS2SupYSize:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_supysize);
    }
    else if ( strmatch(tok,"OS2SupXOff:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_supxoff);
    }
    else if ( strmatch(tok,"OS2SupYOff:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_supyoff);
    }
    else if ( strmatch(tok,"OS2StrikeYSize:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_strikeysize);
    }
    else if ( strmatch(tok,"OS2StrikeYPos:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_strikeypos);
    }
    else if ( strmatch(tok,"OS2CapHeight:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_capheight);
    }
    else if ( strmatch(tok,"OS2XHeight:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_xheight);
    }
    else if ( strmatch(tok,"OS2FamilyClass:")==0 )
    {
	getsint(sfd,&sf->pfminfo.os2_family_class);
    }
    else if ( strmatch(tok,"OS2Vendor:")==0 )
    {
	while ( isspace(nlgetc(sfd)));
	sf->pfminfo.os2_vendor[0] = nlgetc(sfd);
	sf->pfminfo.os2_vendor[1] = nlgetc(sfd);
	sf->pfminfo.os2_vendor[2] = nlgetc(sfd);
	sf->pfminfo.os2_vendor[3] = nlgetc(sfd);
	(void) nlgetc(sfd);
    }
    else if ( strmatch(tok,"OS2CodePages:")==0 )
    {
	gethexints(sfd,sf->pfminfo.codepages,2);
	sf->pfminfo.hascodepages = true;
    }
    else if ( strmatch(tok,"OS2UnicodeRanges:")==0 )
    {
	gethexints(sfd,sf->pfminfo.unicoderanges,4);
	sf->pfminfo.hasunicoderanges = true;
    }
    else if ( strmatch(tok,"TopEncoding:")==0 )
    {
	/* Obsolete */
	getint(sfd,&sf->top_enc);
    }
    else if ( strmatch(tok,"Ascent:")==0 )
    {
	getint(sfd,&sf->ascent);
    }
    else if ( strmatch(tok,"Descent:")==0 )
    {
	getint(sfd,&sf->descent);
    }
    else if ( strmatch(tok,"InvalidEm:")==0 )
    {
	getint(sfd,&sf->invalidem);
    }
    else if ( strmatch(tok,"woffMajor:")==0 )
    {
	getint(sfd,&sf->woffMajor);
    }
    else if ( strmatch(tok,"woffMinor:")==0 )
    {
	getint(sfd,&sf->woffMinor);
    }
    else if ( strmatch(tok,"woffMetadata:")==0 )
    {
	sf->woffMetadata = SFDReadUTF7Str(sfd);
    }
    else if ( strmatch(tok,"UFOAscent:")==0 )
    {
	    getreal(sfd,&sf->ufo_ascent);
    }
    else if ( strmatch(tok,"UFODescent:")==0 )
    {
	getreal(sfd,&sf->ufo_descent);
    }
    else if ( strmatch(tok,"sfntRevision:")==0 )
    {
	gethex(sfd,(uint32 *)&sf->sfntRevision);
    }
    else if ( strmatch(tok,"LayerCount:")==0 )
    {
	d->had_layer_cnt = true;
	getint(sfd,&sf->layer_cnt);
	if ( sf->layer_cnt>2 ) {
	    sf->layers = realloc(sf->layers,sf->layer_cnt*sizeof(LayerInfo));
	    memset(sf->layers+2,0,(sf->layer_cnt-2)*sizeof(LayerInfo));
	}
    }
    else if ( strmatch(tok,"Layer:")==0 )
    {
        // TODO: Read the U. F. O. path.
	int layer, o2, bk;
	getint(sfd,&layer);
	if ( layer>=sf->layer_cnt ) {
	    sf->layers = realloc(sf->layers,(layer+1)*sizeof(LayerInfo));
	    memset(sf->layers+sf->layer_cnt,0,((layer+1)-sf->layer_cnt)*sizeof(LayerInfo));
	    sf->layer_cnt = layer+1;
	}
	getint(sfd,&o2);
	sf->layers[layer].order2 = o2;
	sf->layers[layer].background = layer==ly_back;
	/* Used briefly, now background is after layer name */
	while ( (ch=nlgetc(sfd))==' ' );
	ungetc(ch,sfd);
	if ( ch!='"' ) {
	    getint(sfd,&bk);
	    sf->layers[layer].background = bk;
	}
	/* end of section for obsolete format */
	sf->layers[layer].name = SFDReadUTF7Str(sfd);
	while ( (ch=nlgetc(sfd))==' ' );
	ungetc(ch,sfd);
	if ( ch!='\n' ) {
	    getint(sfd,&bk);
	    sf->layers[layer].background = bk;
	}
	while ( (ch=nlgetc(sfd))==' ' );
	ungetc(ch,sfd);
	if ( ch!='\n' ) { sf->layers[layer].ufo_path = SFDReadUTF7Str(sfd); }
    }
    else if ( strmatch(tok,"PreferredKerning:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->preferred_kerning = temp;
    }
    else if ( strmatch(tok,"StrokedFont:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->strokedfont = temp;
    }
    else if ( strmatch(tok,"MultiLayer:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->multilayer = temp;
    }
    else if ( strmatch(tok,"NeedsXUIDChange:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->changed_since_xuidchanged = temp;
    }
    else if ( strmatch(tok,"VerticalOrigin:")==0 )
    {
	// this doesn't seem to be written ever.
	int temp;
	getint(sfd,&temp);
	sf->hasvmetrics = true;
    }
    else if ( strmatch(tok,"HasVMetrics:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->hasvmetrics = temp;
    }
    else if ( strmatch(tok,"Justify:")==0 )
    {
	SFDParseJustify(sfd,sf,tok);
    }
    else if ( strmatch(tok,"BaseHoriz:")==0 )
    {
	sf->horiz_base = SFDParseBase(sfd);
	d->last_base = sf->horiz_base;
	d->last_base_script = NULL;
    }
    else if ( strmatch(tok,"BaseVert:")==0 )
    {
	sf->vert_base = SFDParseBase(sfd);
	d->last_base = sf->vert_base;
	d->last_base_script = NULL;
    }
    else if ( strmatch(tok,"BaseScript:")==0 )
    {
	struct basescript *bs = SFDParseBaseScript(sfd,d->last_base);
	if ( d->last_base==NULL )
	{
	    BaseScriptFree(bs);
	    bs = NULL;
	}
	else if ( d->last_base_script!=NULL )
	    d->last_base_script->next = bs;
	else
	    d->last_base->scripts = bs;
	d->last_base_script = bs;
    }
    else if ( strmatch(tok,"StyleMap:")==0 )
    {
    gethex(sfd,(uint32 *)&sf->pfminfo.stylemap);
    }
    /* Legacy attribute for StyleMap. Deprecated. */
    else if ( strmatch(tok,"OS2StyleName:")==0 )
    {
    char* sname = SFDReadUTF7Str(sfd);
    if (sf->pfminfo.stylemap == -1) {
        if (strcmp(sname,"bold italic")==0) sf->pfminfo.stylemap = 0x21;
        else if (strcmp(sname,"bold")==0) sf->pfminfo.stylemap = 0x20;
        else if (strcmp(sname,"italic")==0) sf->pfminfo.stylemap = 0x01;
        else if (strcmp(sname,"regular")==0) sf->pfminfo.stylemap = 0x40;
    }
    free(sname);
    }
    else if ( strmatch(tok,"FSType:")==0 )
    {
	getsint(sfd,&sf->pfminfo.fstype);
    }
    else if ( strmatch(tok,"OS2Version:")==0 )
    {
	getsint(sfd,&sf->os2_version);
    }
    else if ( strmatch(tok,"OS2_WeightWidthSlopeOnly:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->weight_width_slope_only = temp;
    }
    else if ( strmatch(tok,"OS2_UseTypoMetrics:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->use_typo_metrics = temp;
    }
    else if ( strmatch(tok,"UseUniqueID:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->use_uniqueid = temp;
    }
    else if ( strmatch(tok,"UseXUID:")==0 )
    {
	int temp;
	getint(sfd,&temp);
	sf->use_xuid = temp;
    }
    else if ( strmatch(tok,"UniqueID:")==0 )
    {
	getint(sfd,&sf->uniqueid);
    }
    else if ( strmatch(tok,"XUID:")==0 )
    {
	geteol(sfd,tok);
	sf->xuid = copy(tok);
    }
    else if ( strmatch(tok,"Lookup:")==0 )
    {
	OTLookup *otl;
	int temp;
	if ( sf->sfd_version<2 ) {
	    IError( "Lookups should not happen in version 1 sfd files." );
	    exit(1);
	}
	otl = chunkalloc(sizeof(OTLookup));
	getint(sfd,&temp); otl->lookup_type = temp;
	getint(sfd,&temp); otl->lookup_flags = temp;
	getint(sfd,&temp); otl->store_in_afm = temp;
	otl->lookup_name = SFDReadUTF7Str(sfd);
	if ( otl->lookup_type<gpos_single ) {
	    if ( d->lastsotl==NULL )
		sf->gsub_lookups = otl;
	    else
		d->lastsotl->next = otl;
	    d->lastsotl = otl;
	} else {
	    if ( d->lastpotl==NULL )
		sf->gpos_lookups = otl;
	    else
		d->lastpotl->next = otl;
	    d->lastpotl = otl;
	}
	SFDParseLookup(sfd,otl);
    }
    else if ( strmatch(tok,"MarkAttachClasses:")==0 )
    {
	getint(sfd,&sf->mark_class_cnt);
	sf->mark_classes = malloc(sf->mark_class_cnt*sizeof(char *));
	sf->mark_class_names = malloc(sf->mark_class_cnt*sizeof(char *));
	sf->mark_classes[0] = NULL; sf->mark_class_names[0] = NULL;
	for ( i=1; i<sf->mark_class_cnt; ++i )
	{
	    /* Class 0 is unused */
	    int temp;
	    while ( (temp=nlgetc(sfd))=='\n' || temp=='\r' ); ungetc(temp,sfd);
	    sf->mark_class_names[i] = SFDReadUTF7Str(sfd);
	    getint(sfd,&temp);
	    sf->mark_classes[i] = malloc(temp+1); sf->mark_classes[i][temp] = '\0';
	    nlgetc(sfd);	/* skip space */
	    fread(sf->mark_classes[i],1,temp,sfd);
	}
    }
    else if ( strmatch(tok,"MarkAttachSets:")==0 )
    {
	getint(sfd,&sf->mark_set_cnt);
	sf->mark_sets = malloc(sf->mark_set_cnt*sizeof(char *));
	sf->mark_set_names = malloc(sf->mark_set_cnt*sizeof(char *));
	for ( i=0; i<sf->mark_set_cnt; ++i )
	{
	    /* Set 0 is used */
	    int temp;
	    while ( (temp=nlgetc(sfd))=='\n' || temp=='\r' ); ungetc(temp,sfd);
	    sf->mark_set_names[i] = SFDReadUTF7Str(sfd);
	    getint(sfd,&temp);
	    sf->mark_sets[i] = malloc(temp+1); sf->mark_sets[i][temp] = '\0';
	    nlgetc(sfd);	/* skip space */
	    fread(sf->mark_sets[i],1,temp,sfd);
	}
    }
    else if ( strmatch(tok,"KernClass2:")==0 || strmatch(tok,"VKernClass2:")==0 ||
	      strmatch(tok,"KernClass:")==0 || strmatch(tok,"VKernClass:")==0 ||
	      strmatch(tok,"KernClass3:")==0 || strmatch(tok,"VKernClass3:")==0 )
    {
	int kernclassversion = 0;
	int isv = tok[0]=='V';
	int kcvoffset = (isv ? 10 : 9); //Offset to read kerning class version
	if (isdigit(tok[kcvoffset])) kernclassversion = tok[kcvoffset] - '0';
	int temp, classstart=1;
	int old = (kernclassversion == 0);

	if ( (sf->sfd_version<2)!=old ) {
	    IError( "Version mixup in Kerning Classes of sfd file." );
	    exit(1);
	}
	kc = chunkalloc(old ? sizeof(KernClass1) : sizeof(KernClass));
	getint(sfd,&kc->first_cnt);
	ch=nlgetc(sfd);
	if ( ch=='+' )
	    classstart = 0;
	else
	    ungetc(ch,sfd);
	getint(sfd,&kc->second_cnt);
	if ( old ) {
	    getint(sfd,&temp); ((KernClass1 *) kc)->sli = temp;
	    getint(sfd,&temp); ((KernClass1 *) kc)->flags = temp;
	} else {
	    kc->subtable = SFFindLookupSubtableAndFreeName(sf,SFDReadUTF7Str(sfd));
	    if ( kc->subtable!=NULL && kc->subtable->kc==NULL )
		kc->subtable->kc = kc;
	    else {
		if ( kc->subtable==NULL )
		    LogError(_("Bad SFD file, missing subtable in kernclass defn.\n") );
		else
		    LogError(_("Bad SFD file, two kerning classes assigned to the same subtable: %s\n"), kc->subtable->subtable_name );
		kc->subtable = NULL;
	    }
	}
	kc->firsts = calloc(kc->first_cnt,sizeof(char *));
	kc->seconds = calloc(kc->second_cnt,sizeof(char *));
	kc->offsets = calloc(kc->first_cnt*kc->second_cnt,sizeof(int16));
	kc->adjusts = calloc(kc->first_cnt*kc->second_cnt,sizeof(DeviceTable));
	if (kernclassversion >= 3) {
	  kc->firsts_flags = calloc(kc->first_cnt, sizeof(int));
	  kc->seconds_flags = calloc(kc->second_cnt, sizeof(int));
	  kc->offsets_flags = calloc(kc->first_cnt*kc->second_cnt, sizeof(int));
	  kc->firsts_names = calloc(kc->first_cnt, sizeof(char*));
	  kc->seconds_names = calloc(kc->second_cnt, sizeof(char*));
	}
	kc->firsts[0] = NULL;
	for ( i=classstart; i<kc->first_cnt; ++i ) {
	  if (kernclassversion < 3) {
	    getint(sfd,&temp);
	    kc->firsts[i] = malloc(temp+1); kc->firsts[i][temp] = '\0';
	    nlgetc(sfd);	/* skip space */
	    fread(kc->firsts[i],1,temp,sfd);
	  } else {
	    getint(sfd,&kc->firsts_flags[i]);
	    while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
	    kc->firsts_names[i] = SFDReadUTF7Str(sfd);
	    while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
	    kc->firsts[i] = SFDReadUTF7Str(sfd);
            if (kc->firsts[i] == NULL) kc->firsts[i] = copy(""); // In certain places, this must be defined.
	    while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
	  }
	}
	kc->seconds[0] = NULL;
	for ( i=1; i<kc->second_cnt; ++i ) {
	  if (kernclassversion < 3) {
	    getint(sfd,&temp);
	    kc->seconds[i] = malloc(temp+1); kc->seconds[i][temp] = '\0';
	    nlgetc(sfd);	/* skip space */
	    fread(kc->seconds[i],1,temp,sfd);
	  } else {
	    getint(sfd,&temp);
	    kc->seconds_flags[i] = temp;
	    while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
	    kc->seconds_names[i] = SFDReadUTF7Str(sfd);
	    while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
	    kc->seconds[i] = SFDReadUTF7Str(sfd);
            if (kc->seconds[i] == NULL) kc->seconds[i] = copy(""); // In certain places, this must be defined.
	    while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
	  }
	}
	for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i ) {
	  if (kernclassversion >= 3) {
	    getint(sfd,&temp);
	    kc->offsets_flags[i] = temp;
	  }
	    getint(sfd,&temp);
	    kc->offsets[i] = temp;
	    SFDReadDeviceTable(sfd,&kc->adjusts[i]);
	}
	if ( !old && kc->subtable == NULL ) {
	    /* Error. Ignore it. Free it. Whatever */;
	} else if ( !isv ) {
	    if ( d->lastkc==NULL )
		sf->kerns = kc;
	    else
		d->lastkc->next = kc;
	    d->lastkc = kc;
	} else {
	    if ( d->lastvkc==NULL )
		sf->vkerns = kc;
	    else
		d->lastvkc->next = kc;
	    d->lastvkc = kc;
	}
    }
    else if ( strmatch(tok,"ContextPos2:")==0 || strmatch(tok,"ContextSub2:")==0 ||
	      strmatch(tok,"ChainPos2:")==0 || strmatch(tok,"ChainSub2:")==0 ||
	      strmatch(tok,"ReverseChain2:")==0 ||
	      strmatch(tok,"ContextPos:")==0 || strmatch(tok,"ContextSub:")==0 ||
	      strmatch(tok,"ChainPos:")==0 || strmatch(tok,"ChainSub:")==0 ||
	      strmatch(tok,"ReverseChain:")==0 )
    {
	FPST *fpst;
	int old;
	if ( strchr(tok,'2')!=NULL ) {
	    old = false;
	    fpst = chunkalloc(sizeof(FPST));
	} else {
	    old = true;
	    fpst = chunkalloc(sizeof(FPST1));
	}
	if ( (sf->sfd_version<2)!=old ) {
	    IError( "Version mixup in FPST of sfd file." );
	    exit(1);
	}
	if ( d->lastfp==NULL )
	    sf->possub = fpst;
	else
	    d->lastfp->next = fpst;
	d->lastfp = fpst;
	SFDParseChainContext(sfd,sf,fpst,tok,old);
    }
    else if ( strmatch(tok,"Group:")==0 ) {
        struct ff_glyphclasses *grouptmp = calloc(1, sizeof(struct ff_glyphclasses));
        while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
        grouptmp->classname = SFDReadUTF7Str(sfd);
        while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
        grouptmp->glyphs = SFDReadUTF7Str(sfd);
        while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
        if (d->lastgroup != NULL) d->lastgroup->next = grouptmp; else sf->groups = grouptmp;
        d->lastgroup = grouptmp;
    }
    else if ( strmatch(tok,"GroupKern:")==0 ) {
        int temp = 0;
        struct ff_rawoffsets *kerntmp = calloc(1, sizeof(struct ff_rawoffsets));
        while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
        kerntmp->left = SFDReadUTF7Str(sfd);
        while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
        kerntmp->right = SFDReadUTF7Str(sfd);
        while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
        getint(sfd,&temp);
        kerntmp->offset = temp;
        while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
        if (d->lastgroupkern != NULL) d->lastgroupkern->next = kerntmp; else sf->groupkerns = kerntmp;
        d->lastgroupkern = kerntmp;
    }
    else if ( strmatch(tok,"GroupVKern:")==0 ) {
        int temp = 0;
        struct ff_rawoffsets *kerntmp = calloc(1, sizeof(struct ff_rawoffsets));
        while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
        kerntmp->left = SFDReadUTF7Str(sfd);
        while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
        kerntmp->right = SFDReadUTF7Str(sfd);
        while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
        getint(sfd,&temp);
        kerntmp->offset = temp;
        while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
        if (d->lastgroupvkern != NULL) d->lastgroupvkern->next = kerntmp; else sf->groupvkerns = kerntmp;
        d->lastgroupvkern = kerntmp;
    }
    else if ( strmatch(tok,"MacIndic2:")==0 || strmatch(tok,"MacContext2:")==0 ||
	      strmatch(tok,"MacLigature2:")==0 || strmatch(tok,"MacSimple2:")==0 ||
	      strmatch(tok,"MacKern2:")==0 || strmatch(tok,"MacInsert2:")==0 ||
	      strmatch(tok,"MacIndic:")==0 || strmatch(tok,"MacContext:")==0 ||
	      strmatch(tok,"MacLigature:")==0 || strmatch(tok,"MacSimple:")==0 ||
	      strmatch(tok,"MacKern:")==0 || strmatch(tok,"MacInsert:")==0 )
    {
	ASM *sm;
	if ( strchr(tok,'2')!=NULL ) {
	    old = false;
	    sm = chunkalloc(sizeof(ASM));
	} else {
	    old = true;
	    sm = chunkalloc(sizeof(ASM1));
	}
	if ( (sf->sfd_version<2)!=old ) {
	    IError( "Version mixup in state machine of sfd file." );
	    exit(1);
	}
	if ( d->lastsm==NULL )
	    sf->sm = sm;
	else
	    d->lastsm->next = sm;
	d->lastsm = sm;
	SFDParseStateMachine(sfd,sf,sm,tok,old);
    }
    else if ( strmatch(tok,"MacFeat:")==0 )
    {
	sf->features = SFDParseMacFeatures(sfd,tok);
    }
    else if ( strmatch(tok,"TtfTable:")==0 )
    {
	/* Old, binary format */
	/* still used for maxp and unknown tables */
	SFDGetTtfTable(sfd,sf,d->lastttf);
    }
    else if ( strmatch(tok,"TtTable:")==0 )
    {
	/* text instruction format */
	SFDGetTtTable(sfd,sf,d->lastttf);
    }

    ///////////////////

    else if ( strmatch(tok,"ShortTable:")==0 )
    {
	// only read, not written.
	/* text number format */
	SFDGetShortTable(sfd,sf,d->lastttf);
    }
    else
    {
        //
        // We didn't have a match ourselves.
        //
        return false;
    }
    return true;
}

Fix for #4084 Use-after-free (heap) in the SFD_GetFontMetaData() function
Fix for #4086 NULL pointer dereference in the SFDGetSpiros() function
Fix for #4088 NULL pointer dereference in the SFD_AssignLookups() function
Add empty sf->fontname string if it isn't set, fixing #4089 #4090 and many
  other potential issues (many downstream calls to strlen() on the value).