Commit message: Do something sensible for empty strings to make fuzzers happy.
CVE: CVE-2017-14166
CWE: CWE-125
Vulnerability description: libarchive 3.3.2 allows remote attackers to cause a denial of service (xml_data heap-based buffer over-read and application crash) via a crafted xar archive, related to the mishandling of empty strings in the atol8 function in archive_read_support_format_xar.c.

Project: libarchive
File: archive_read_support_format_xar.c

archive_read_support_format_xar(struct archive *_a)
{
	struct archive_read *a = (struct archive_read *)_a;
	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
	    ARCHIVE_STATE_NEW, "archive_read_support_format_xar");

	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
	    "Xar not supported on this platform");
	return (ARCHIVE_WARN);
}

Project: libarchive
File: archive_read_support_format_xar.c

expat_read_toc(struct archive_read *a)
{
	struct xar *xar;
	XML_Parser parser;
	struct expat_userData ud;

	ud.state = ARCHIVE_OK;
	ud.archive = a;

	xar = (struct xar *)(a->format->data);

	/* Initialize XML Parser library. */
	parser = XML_ParserCreate(NULL);
	if (parser == NULL) {
		archive_set_error(&a->archive, ENOMEM,
		    "Couldn't allocate memory for xml parser");
		return (ARCHIVE_FATAL);
	}
	XML_SetUserData(parser, &ud);
	XML_SetElementHandler(parser, expat_start_cb, expat_end_cb);
	XML_SetCharacterDataHandler(parser, expat_data_cb);
	xar->xmlsts = INIT;

	while (xar->toc_remaining && ud.state == ARCHIVE_OK) {
		enum XML_Status xr;
		const void *d;
		size_t outbytes;
		size_t used;
		int r;

		d = NULL;
		r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
		if (r != ARCHIVE_OK)
			return (r);
		xar->toc_remaining -= used;
		xar->offset += used;
		xar->toc_total += outbytes;
		PRINT_TOC(d, outbytes);

		xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0);
		__archive_read_consume(a, used);
		if (xr == XML_STATUS_ERROR) {
			XML_ParserFree(parser);
			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
			    "XML Parsing failed");
			return (ARCHIVE_FATAL);
		}
	}
	XML_ParserFree(parser);
	return (ud.state);
}

Project: libarchive
File: archive_read_support_format_xar.c

expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts)
{
	struct expat_userData *ud = (struct expat_userData *)userData;
	struct archive_read *a = ud->archive;
	struct xmlattr_list list;
	int r;

	r = expat_xmlattr_setup(a, &list, atts);
	if (r == ARCHIVE_OK)
		r = xml_start(a, (const char *)name, &list);
	xmlattr_cleanup(&list);
	ud->state = r;
}

Project: libarchive
File: archive_read_support_format_xar.c

xar_read_header(struct archive_read *a, struct archive_entry *entry)
{
	struct xar *xar;
	struct xar_file *file;
	struct xattr *xattr;
	int r;

	xar = (struct xar *)(a->format->data);
	r = ARCHIVE_OK;

	if (xar->offset == 0) {
		/* Create a character conversion object. */
		if (xar->sconv == NULL) {
			xar->sconv = archive_string_conversion_from_charset(
			    &(a->archive), "UTF-8", 1);
			if (xar->sconv == NULL)
				return (ARCHIVE_FATAL);
		}

		/* Read TOC. */
		r = read_toc(a);
		if (r != ARCHIVE_OK)
			return (r);
	}

	for (;;) {
		file = xar->file = heap_get_entry(&(xar->file_queue));
		if (file == NULL) {
			xar->end_of_file = 1;
			return (ARCHIVE_EOF);
		}
		if ((file->mode & AE_IFMT) != AE_IFDIR)
			break;
		if (file->has != (HAS_PATHNAME | HAS_TYPE))
			break;
		/*
		 * If a file type is a directory and it does not have
		 * any metadata, do not export.
		 */
		file_free(file);
	}
	archive_entry_set_atime(entry, file->atime, 0);
	archive_entry_set_ctime(entry, file->ctime, 0);
	archive_entry_set_mtime(entry, file->mtime, 0);
	archive_entry_set_gid(entry, file->gid);
	if (file->gname.length > 0 &&
	    archive_entry_copy_gname_l(entry, file->gname.s,
		archive_strlen(&(file->gname)), xar->sconv) != 0) {
		if (errno == ENOMEM) {
			archive_set_error(&a->archive, ENOMEM,
			    "Can't allocate memory for Gname");
			return (ARCHIVE_FATAL);
		}
		archive_set_error(&a->archive,
		    ARCHIVE_ERRNO_FILE_FORMAT,
		    "Gname cannot be converted from %s to current locale.",
		    archive_string_conversion_charset_name(xar->sconv));
		r = ARCHIVE_WARN;
	}
	archive_entry_set_uid(entry, file->uid);
	if (file->uname.length > 0 &&
	    archive_entry_copy_uname_l(entry, file->uname.s,
		archive_strlen(&(file->uname)), xar->sconv) != 0) {
		if (errno == ENOMEM) {
			archive_set_error(&a->archive, ENOMEM,
			    "Can't allocate memory for Uname");
			return (ARCHIVE_FATAL);
		}
		archive_set_error(&a->archive,
		    ARCHIVE_ERRNO_FILE_FORMAT,
		    "Uname cannot be converted from %s to current locale.",
		    archive_string_conversion_charset_name(xar->sconv));
		r = ARCHIVE_WARN;
	}
	archive_entry_set_mode(entry, file->mode);
	if (archive_entry_copy_pathname_l(entry, file->pathname.s,
	    archive_strlen(&(file->pathname)), xar->sconv) != 0) {
		if (errno == ENOMEM) {
			archive_set_error(&a->archive, ENOMEM,
			    "Can't allocate memory for Pathname");
			return (ARCHIVE_FATAL);
		}
		archive_set_error(&a->archive,
		    ARCHIVE_ERRNO_FILE_FORMAT,
		    "Pathname cannot be converted from %s to current locale.",
		    archive_string_conversion_charset_name(xar->sconv));
		r = ARCHIVE_WARN;
	}


	if (file->symlink.length > 0 &&
	    archive_entry_copy_symlink_l(entry, file->symlink.s,
		archive_strlen(&(file->symlink)), xar->sconv) != 0) {
		if (errno == ENOMEM) {
			archive_set_error(&a->archive, ENOMEM,
			    "Can't allocate memory for Linkname");
			return (ARCHIVE_FATAL);
		}
		archive_set_error(&a->archive,
		    ARCHIVE_ERRNO_FILE_FORMAT,
		    "Linkname cannot be converted from %s to current locale.",
		    archive_string_conversion_charset_name(xar->sconv));
		r = ARCHIVE_WARN;
	}
	/* Set proper nlink. */
	if ((file->mode & AE_IFMT) == AE_IFDIR)
		archive_entry_set_nlink(entry, file->subdirs + 2);
	else
		archive_entry_set_nlink(entry, file->nlink);
	archive_entry_set_size(entry, file->size);
	if (archive_strlen(&(file->hardlink)) > 0)
		archive_entry_set_hardlink(entry, file->hardlink.s);
	archive_entry_set_ino64(entry, file->ino64);
	if (file->has & HAS_DEV)
		archive_entry_set_dev(entry, file->dev);
	if (file->has & HAS_DEVMAJOR)
		archive_entry_set_devmajor(entry, file->devmajor);
	if (file->has & HAS_DEVMINOR)
		archive_entry_set_devminor(entry, file->devminor);
	if (archive_strlen(&(file->fflags_text)) > 0)
		archive_entry_copy_fflags_text(entry, file->fflags_text.s);

	xar->entry_init = 1;
	xar->entry_total = 0;
	xar->entry_remaining = file->length;
	xar->entry_size = file->size;
	xar->entry_encoding = file->encoding;
	xar->entry_a_sum = file->a_sum;
	xar->entry_e_sum = file->e_sum;
	/*
	 * Read extended attributes.
	 */
	xattr = file->xattr_list;
	while (xattr != NULL) {
		const void *d;
		size_t outbytes, used;

		r = move_reading_point(a, xattr->offset);
		if (r != ARCHIVE_OK)
			break;
		r = rd_contents_init(a, xattr->encoding,
		    xattr->a_sum.alg, xattr->e_sum.alg);
		if (r != ARCHIVE_OK)
			break;
		d = NULL;
		r = rd_contents(a, &d, &outbytes, &used, xattr->length);
		if (r != ARCHIVE_OK)
			break;
		if (outbytes != xattr->size) {
			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
			    "Decompressed size error");
			r = ARCHIVE_FATAL;
			break;
		}
		r = checksum_final(a,
		    xattr->a_sum.val, xattr->a_sum.len,
		    xattr->e_sum.val, xattr->e_sum.len);
		if (r != ARCHIVE_OK)
			break;
		archive_entry_xattr_add_entry(entry,
		    xattr->name.s, d, outbytes);
		xattr = xattr->next;
	}
	if (r != ARCHIVE_OK) {
		file_free(file);
		return (r);
	}

	if (xar->entry_remaining > 0)
		/* Move reading point to the beginning of current
		 * file contents. */
		r = move_reading_point(a, file->offset);
	else
		r = ARCHIVE_OK;

	file_free(file);
	return (r);
}

Project: libarchive
File: archive_read_support_format_xar.c

xml2_read_cb(void *context, char *buffer, int len)
{
	struct archive_read *a;
	struct xar *xar;
	const void *d;
	size_t outbytes;
	size_t used = 0;
	int r;

	a = (struct archive_read *)context;
	xar = (struct xar *)(a->format->data);

	if (xar->toc_remaining <= 0)
		return (0);
	d = buffer;
	outbytes = len;
	r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
	if (r != ARCHIVE_OK)
		return (r);
	__archive_read_consume(a, used);
	xar->toc_remaining -= used;
	xar->offset += used;
	xar->toc_total += outbytes;
	PRINT_TOC(buffer, len);

	return ((int)outbytes);
}