libarchive: Backport "Fix crash on failure to convert WCS/UTF-8 pathname"

Backport libarchive commit `d4cf95cdac` (archive_write: Fix crash on
failure to convert WCS/UTF-8 pathname to MBS, 2026-02-04) and supporting
parent commits from libarchive PR 2856 [1].

[1] https://github.com/libarchive/libarchive/pull/2856

Issue: #26903
This commit is contained in:
Brad King
2026-02-04 16:35:03 -05:00
committed by Alex Overchenko
parent c19aaaab8b
commit e5c6c121c6
5 changed files with 112 additions and 26 deletions

View File

@@ -777,7 +777,7 @@ archive_string_append_from_wcs_in_codepage(struct archive_string *as,
int r;
defchar_used = 0;
if (to_cp == CP_UTF8 || sc == NULL)
if (to_cp == CP_UTF8)
dp = NULL;
else
dp = &defchar_used;
@@ -1878,6 +1878,9 @@ archive_string_conversion_free(struct archive *a)
const char *
archive_string_conversion_charset_name(struct archive_string_conv *sc)
{
if (sc == NULL) {
return "current locale";
}
if (sc->flag & SCONV_TO_CHARSET)
return (sc->to_charset);
else
@@ -4128,7 +4131,12 @@ archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes,
* character-set. */
if ((aes->aes_set & AES_SET_MBS) == 0) {
const char *pm; /* unused */
archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
if (archive_mstring_get_mbs(a, aes, &pm) != 0) {
/* We have another form, but failed to convert it to
* the native locale. Transitively, we've failed to
* convert it to the specified character set. */
ret = -1;
}
}
/* If we already have an MBS form, use it to be translated to
* specified character-set. */
@@ -4146,6 +4154,8 @@ archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes,
if (length != NULL)
*length = aes->aes_mbs_in_locale.length;
} else {
/* Either we have no string in any form,
* or conversion failed and set 'ret != 0'. */
*p = NULL;
if (length != NULL)
*length = 0;

View File

@@ -293,6 +293,17 @@ archive_write_gnutar_header(struct archive_write *a,
} else
sconv = gnutar->opt_sconv;
/* Sanity check. */
if (archive_entry_pathname(entry) == NULL
#if defined(_WIN32) && !defined(__CYGWIN__)
&& archive_entry_pathname_w(entry) == NULL
#endif
) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Can't record entry in tar file without pathname");
return ARCHIVE_FAILED;
}
/* Only regular files (not hardlinks) have data. */
if (archive_entry_hardlink(entry) != NULL ||
archive_entry_symlink(entry) != NULL ||
@@ -385,17 +396,30 @@ archive_write_gnutar_header(struct archive_write *a,
r = archive_entry_pathname_l(entry, &(gnutar->pathname),
&(gnutar->pathname_length), sconv);
if (r != 0) {
const char* p_mbs;
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for pathname");
ret = ARCHIVE_FATAL;
goto exit_write_header;
}
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname '%s' to %s",
archive_entry_pathname(entry),
archive_string_conversion_charset_name(sconv));
ret2 = ARCHIVE_WARN;
p_mbs = archive_entry_pathname(entry);
if (p_mbs) {
/* We have a wrongly-encoded MBS pathname.
* Warn and use it. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname '%s' to %s", p_mbs,
archive_string_conversion_charset_name(sconv));
ret2 = ARCHIVE_WARN;
} else {
/* We have no MBS pathname. Fail. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname to %s",
archive_string_conversion_charset_name(sconv));
return ARCHIVE_FAILED;
}
}
r = archive_entry_uname_l(entry, &(gnutar->uname),
&(gnutar->uname_length), sconv);

View File

@@ -254,11 +254,11 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
sconv = ustar->opt_sconv;
/* Sanity check. */
if (archive_entry_pathname(entry) == NULL
#if defined(_WIN32) && !defined(__CYGWIN__)
if (archive_entry_pathname_w(entry) == NULL) {
#else
if (archive_entry_pathname(entry) == NULL) {
&& archive_entry_pathname_w(entry) == NULL
#endif
) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Can't record entry in tar file without pathname");
return (ARCHIVE_FAILED);
@@ -409,15 +409,28 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
*/
r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
if (r != 0) {
const char* p_mbs;
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,
"Can't translate pathname '%s' to %s",
pp, archive_string_conversion_charset_name(sconv));
ret = ARCHIVE_WARN;
p_mbs = archive_entry_pathname(entry);
if (p_mbs) {
/* We have a wrongly-encoded MBS pathname.
* Warn and use it. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname '%s' to %s", p_mbs,
archive_string_conversion_charset_name(sconv));
ret = ARCHIVE_WARN;
} else {
/* We have no MBS pathname. Fail. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname to %s",
archive_string_conversion_charset_name(sconv));
return ARCHIVE_FAILED;
}
}
if (copy_length <= USTAR_name_size)
memcpy(h + USTAR_name_offset, pp, copy_length);

View File

@@ -232,7 +232,11 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)
sconv = v7tar->opt_sconv;
/* Sanity check. */
if (archive_entry_pathname(entry) == NULL) {
if (archive_entry_pathname(entry) == NULL
#if defined(_WIN32) && !defined(__CYGWIN__)
&& archive_entry_pathname_w(entry) == NULL
#endif
) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Can't record entry in tar file without pathname");
return (ARCHIVE_FAILED);
@@ -382,15 +386,28 @@ format_header_v7tar(struct archive_write *a, char h[512],
*/
r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
if (r != 0) {
const char* p_mbs;
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,
"Can't translate pathname '%s' to %s",
pp, archive_string_conversion_charset_name(sconv));
ret = ARCHIVE_WARN;
p_mbs = archive_entry_pathname(entry);
if (p_mbs) {
/* We have a wrongly-encoded MBS pathname.
* Warn and use it. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname '%s' to %s", p_mbs,
archive_string_conversion_charset_name(sconv));
ret = ARCHIVE_WARN;
} else {
/* We have no MBS pathname. Fail. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname to %s",
archive_string_conversion_charset_name(sconv));
return ARCHIVE_FAILED;
}
}
if (strict && copy_length < V7TAR_name_size)
memcpy(h + V7TAR_name_offset, pp, copy_length);

View File

@@ -802,6 +802,17 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
int version_needed = 10;
#define MIN_VERSION_NEEDED(x) do { if (version_needed < x) { version_needed = x; } } while (0)
/* Sanity check. */
if (archive_entry_pathname(entry) == NULL
#if defined(_WIN32) && !defined(__CYGWIN__)
&& archive_entry_pathname_w(entry) == NULL
#endif
) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Can't record entry in zip file without pathname");
return ARCHIVE_FAILED;
}
/* Ignore types of entries that we don't support. */
type = archive_entry_filetype(entry);
if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) {
@@ -882,22 +893,33 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_FATAL);
}
if (sconv != NULL) {
{
const char *p;
size_t len;
if (archive_entry_pathname_l(zip->entry, &p, &len, sconv) != 0) {
const char* p_mbs;
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,
"Can't translate Pathname '%s' to %s",
archive_entry_pathname(zip->entry),
archive_string_conversion_charset_name(sconv));
ret2 = ARCHIVE_WARN;
p_mbs = archive_entry_pathname(zip->entry);
if (p_mbs) {
/* We have a wrongly-encoded MBS pathname. Warn and use it. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname '%s' to %s", p_mbs,
archive_string_conversion_charset_name(sconv));
ret2 = ARCHIVE_WARN;
} else {
/* We have no MBS pathname. Fail. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname to %s",
archive_string_conversion_charset_name(sconv));
return ARCHIVE_FAILED;
}
}
if (len > 0)
archive_entry_set_pathname(zip->entry, p);