stream_libarchive: fix seeking fallback

In commit 1199c1e3, we added checks to every libarchive API call to make
sure the archive was closed on ARCHIVE_FATAL - otherwise, libarchive
could endow us with free CVEs (such as it apparently happens when you
continue reading a rar archive that uses features not yet supported by
libarchive).

This broke the fallback for seeking in unseekable archive formats. Of
course libarchive won't tell us directly whether a format implementation
has seek support or not - and OF COURSE it returns ARCHIVE_FATAL if it
has no seek support. (The error string, which you can retrieve via API,
is actually more detailed, and also claims it's an "internal error". I
don't think so, libarchive.) Returning ARCHIVE_FATAL means we have to
assume free CVEs are ahead, and we have to close the archive. Which
breaks the fallback in a dumb way (we have no way of telling which of
those cases happened anyway).

Fix this by assuming that all seek errors are potentially due to lack of
seek support. If the seek call fails, reopen the archive, and set a flag
so the seek API is never tried again. (This means we can still skip
ahead for forward seeks, which is more efficient than skipping from the
start of the archive entry.)

Also fix an old typo in an error message.
This commit is contained in:
wm4 2017-12-24 06:14:10 +01:00 committed by Martin Herkt
parent 2a43060560
commit bf111f9c3c
1 changed files with 14 additions and 14 deletions

View File

@ -142,7 +142,7 @@ static bool mp_archive_check_fatal(struct mp_archive *mpa, int r)
{
if (r > ARCHIVE_FATAL)
return false;
MP_FATAL(mpa, "fatal error received - cllsing archive\n");
MP_FATAL(mpa, "fatal error received - closing archive\n");
mp_archive_close(mpa);
return true;
}
@ -347,6 +347,7 @@ bool mp_archive_next_entry(struct mp_archive *mpa)
struct priv {
struct mp_archive *mpa;
bool broken_seek;
struct stream *src;
int64_t entry_size;
char *entry_name;
@ -400,17 +401,16 @@ static int archive_entry_fill_buffer(stream_t *s, char *buffer, int max_len)
static int archive_entry_seek(stream_t *s, int64_t newpos)
{
struct priv *p = s->priv;
if (!p->mpa)
return -1;
locale_t oldlocale = uselocale(p->mpa->locale);
int r = archive_seek_data(p->mpa->arch, newpos, SEEK_SET);
uselocale(oldlocale);
if (r >= 0)
return 1;
if (mp_archive_check_fatal(p->mpa, r)) {
mp_archive_free(p->mpa);
p->mpa = NULL;
return -1;
if (p->mpa && !p->broken_seek) {
locale_t oldlocale = uselocale(p->mpa->locale);
int r = archive_seek_data(p->mpa->arch, newpos, SEEK_SET);
uselocale(oldlocale);
if (r >= 0)
return 1;
MP_WARN(s, "possibly unsupported seeking - switching to reopening\n");
p->broken_seek = true;
if (reopen_archive(s) < STREAM_OK)
return -1;
}
// libarchive can't seek in most formats.
if (newpos < s->pos) {
@ -430,8 +430,8 @@ static int archive_entry_seek(stream_t *s, int64_t newpos)
return -1;
int size = MPMIN(newpos - s->pos, sizeof(buffer));
oldlocale = uselocale(p->mpa->locale);
r = archive_read_data(p->mpa->arch, buffer, size);
locale_t oldlocale = uselocale(p->mpa->locale);
int r = archive_read_data(p->mpa->arch, buffer, size);
if (r < 0) {
MP_ERR(s, "%s\n", archive_error_string(p->mpa->arch));
uselocale(oldlocale);