Vulnerability: Heap-based buffer overflow in archive_string_append_from_wcs() (archive_string.c) in libarchive-3.4.1dev allows remote attackers to cause a denial of service (out-of-bounds write in heap memory resulting into a crash) via a crafted archive file. NOTE: this only affects users who downloaded the development code from GitHub. Users of the product's official releases are unaffected.
Vulnerability ID: CVE-2020-21674
CWE: CWE-787
Severity score: 2.9
Project: https://github.com/libarchive/libarchive
Published: 2020-10-15
File: libarchive/archive_string.c
Commit message: Fix a possible heap-buffer-overflow in archive_string_append_from_wcs()

When we grow the archive_string buffer, we have to make sure it fits
at least one maximum-sized multibyte character in the current locale
and the null character.

Fixes #1298
Commit author: Martin Matuska

Vulnerable version (before fix):
 __FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33 #define wmemmove(a,b,i)  (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t)) #endif  struct archive_string_conv { 	struct archive_string_conv	*next; 	char				*from_charset; archive_string_append_from_wcs(struct archive_string *as, 			as->s[as->length] = '\0'; 			/* Re-allocate buffer for MBS. */ 			if (archive_string_ensure(as, 			    as->length + len * 2 + 1) == NULL) 				return (-1); 			p = as->s + as->length; 			end = as->s + as->buffer_length - MB_CUR_MAX -1; strncat_from_utf8_libarchive2(struct archive_string *as, 			as->length = p - as->s; 			/* Re-allocate buffer for MBS. */ 			if (archive_string_ensure(as, 			    as->length + len * 2 + 1) == NULL) 				return (-1); 			p = as->s + as->length; 			end = as->s + as->buffer_length - MB_CUR_MAX -1;

Fixed version (after fix):
 __FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33 #define wmemmove(a,b,i)  (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t)) #endif  #undef max #define max(a, b)       ((a)>(b)?(a):(b))  struct archive_string_conv { 	struct archive_string_conv	*next; 	char				*from_charset; archive_string_append_from_wcs(struct archive_string *as, 			as->s[as->length] = '\0'; 			/* Re-allocate buffer for MBS. */ 			if (archive_string_ensure(as, 			    as->length + max(len * 2, 			    (size_t)MB_CUR_MAX) + 1) == NULL) 				return (-1); 			p = as->s + as->length; 			end = as->s + as->buffer_length - MB_CUR_MAX -1; strncat_from_utf8_libarchive2(struct archive_string *as, 			as->length = p - as->s; 			/* Re-allocate buffer for MBS. */ 			if (archive_string_ensure(as, 			    as->length + max(len * 2, 			    (size_t)MB_CUR_MAX) + 1) == NULL) 				return (-1); 			p = as->s + as->length; 			end = as->s + as->buffer_length - MB_CUR_MAX -1;

Patch:
patch: @@ -75,6 +75,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33
 #define wmemmove(a,b,i)  (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t))
 #endif
 
+#undef max
+#define max(a, b)       ((a)>(b)?(a):(b))
+
 struct archive_string_conv {
 	struct archive_string_conv	*next;
 	char				*from_charset;
@@ -804,7 +807,8 @@ archive_string_append_from_wcs(struct archive_string *as,
 			as->s[as->length] = '\0';
 			/* Re-allocate buffer for MBS. */
 			if (archive_string_ensure(as,
-			    as->length + len * 2 + 1) == NULL)
+			    as->length + max(len * 2,
+			    (size_t)MB_CUR_MAX) + 1) == NULL)
 				return (-1);
 			p = as->s + as->length;
 			end = as->s + as->buffer_length - MB_CUR_MAX -1;
@@ -3446,7 +3450,8 @@ strncat_from_utf8_libarchive2(struct archive_string *as,
 			as->length = p - as->s;
 			/* Re-allocate buffer for MBS. */
 			if (archive_string_ensure(as,
-			    as->length + len * 2 + 1) == NULL)
+			    as->length + max(len * 2,
+			    (size_t)MB_CUR_MAX) + 1) == NULL)
 				return (-1);
 			p = as->s + as->length;
 			end = as->s + as->buffer_length - MB_CUR_MAX -1;
path: libarchive/archive_string.c