Should make the default behaviour safer for careless callers that open random untrusted files. Bug-Id: CVE-2016-1897 Bug-Id: CVE-2016-1898tags/n3.1
| @@ -13,6 +13,11 @@ libavutil: 2015-08-28 | |||||
| API changes, most recent first: | API changes, most recent first: | ||||
| 2016-xx-xx - xxxxxxx - lavf 57.4.0 - avformat.h | |||||
| Add AVFormatContext.protocol_whitelist and protocol_blacklist. | |||||
| Add 'protocol_whitelist' and 'protocol_blacklist' private options for | |||||
| avio_open2(). | |||||
| 2016-xx-xx - lavc 57.13.0 - avcodec.h | 2016-xx-xx - lavc 57.13.0 - avcodec.h | ||||
| Add AVCodecContext.hw_frames_ctx. | Add AVCodecContext.hw_frames_ctx. | ||||
| @@ -1261,6 +1261,24 @@ typedef struct AVFormatContext { | |||||
| * A callback for closing the streams opened with AVFormatContext.io_open(). | * A callback for closing the streams opened with AVFormatContext.io_open(). | ||||
| */ | */ | ||||
| void (*io_close)(struct AVFormatContext *s, AVIOContext *pb); | void (*io_close)(struct AVFormatContext *s, AVIOContext *pb); | ||||
| /** | |||||
| * A comma-separated list of protocol names that will not be used internally | |||||
| * by libavformat. If this field is a non-empty string, then protocols | |||||
| * listed here will be forbidden. | |||||
| * | |||||
| * This field should be set using AVOptions. | |||||
| */ | |||||
| char *protocol_blacklist; | |||||
| /** | |||||
| * A comma-separated list of protocol names that can be used internally by | |||||
| * libavformat. If this field is a non-empty string, all protocols not | |||||
| * listed here will be forbidden. | |||||
| * | |||||
| * This field should be set using AVOptions. | |||||
| */ | |||||
| char *protocol_whitelist; | |||||
| } AVFormatContext; | } AVFormatContext; | ||||
| typedef struct AVPacketList { | typedef struct AVPacketList { | ||||
| @@ -41,20 +41,53 @@ | |||||
| #define SHORT_SEEK_THRESHOLD 4096 | #define SHORT_SEEK_THRESHOLD 4096 | ||||
| typedef struct AVIOInternal { | typedef struct AVIOInternal { | ||||
| const AVClass *class; | |||||
| char *protocol_whitelist; | |||||
| char *protocol_blacklist; | |||||
| URLContext *h; | URLContext *h; | ||||
| const URLProtocol **protocols; | const URLProtocol **protocols; | ||||
| } AVIOInternal; | } AVIOInternal; | ||||
| static void *io_priv_child_next(void *obj, void *prev) | |||||
| { | |||||
| AVIOInternal *internal = obj; | |||||
| return prev ? NULL : internal->h; | |||||
| } | |||||
| static const AVClass *io_priv_child_class_next(const AVClass *prev) | |||||
| { | |||||
| return prev ? NULL : &ffurl_context_class; | |||||
| } | |||||
| #define OFFSET(x) offsetof(AVIOInternal, x) | |||||
| static const AVOption io_priv_options[] = { | |||||
| { "protocol_whitelist", "A comma-separated list of allowed protocols", | |||||
| OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING }, | |||||
| { "protocol_blacklist", "A comma-separated list of forbidden protocols", | |||||
| OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING }, | |||||
| { NULL }, | |||||
| }; | |||||
| static const AVClass io_priv_class = { | |||||
| .class_name = "AVIOContext", | |||||
| .item_name = av_default_item_name, | |||||
| .version = LIBAVUTIL_VERSION_INT, | |||||
| .option = io_priv_options, | |||||
| .child_next = io_priv_child_next, | |||||
| .child_class_next = io_priv_child_class_next, | |||||
| }; | |||||
| static void *ff_avio_child_next(void *obj, void *prev) | static void *ff_avio_child_next(void *obj, void *prev) | ||||
| { | { | ||||
| AVIOContext *s = obj; | AVIOContext *s = obj; | ||||
| AVIOInternal *internal = s->opaque; | |||||
| return prev ? NULL : internal->h; | |||||
| return prev ? NULL : s->opaque; | |||||
| } | } | ||||
| static const AVClass *ff_avio_child_class_next(const AVClass *prev) | static const AVClass *ff_avio_child_class_next(const AVClass *prev) | ||||
| { | { | ||||
| return prev ? NULL : &ffurl_context_class; | |||||
| return prev ? NULL : &io_priv_class; | |||||
| } | } | ||||
| static const AVOption ff_avio_options[] = { | static const AVOption ff_avio_options[] = { | ||||
| @@ -750,8 +783,11 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) | |||||
| if (!internal) | if (!internal) | ||||
| goto fail; | goto fail; | ||||
| internal->class = &io_priv_class; | |||||
| internal->h = h; | internal->h = h; | ||||
| av_opt_set_defaults(internal); | |||||
| *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, | *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, | ||||
| internal, io_read_packet, io_write_packet, io_seek); | internal, io_read_packet, io_write_packet, io_seek); | ||||
| if (!*s) | if (!*s) | ||||
| @@ -766,6 +802,8 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) | |||||
| (*s)->av_class = &ff_avio_class; | (*s)->av_class = &ff_avio_class; | ||||
| return 0; | return 0; | ||||
| fail: | fail: | ||||
| if (internal) | |||||
| av_opt_free(internal); | |||||
| av_freep(&internal); | av_freep(&internal); | ||||
| av_freep(&buffer); | av_freep(&buffer); | ||||
| return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
| @@ -849,10 +887,21 @@ int avio_open2(AVIOContext **s, const char *filename, int flags, | |||||
| { | { | ||||
| AVIOInternal *internal; | AVIOInternal *internal; | ||||
| const URLProtocol **protocols; | const URLProtocol **protocols; | ||||
| char *proto_whitelist = NULL, *proto_blacklist = NULL; | |||||
| AVDictionaryEntry *e; | |||||
| URLContext *h; | URLContext *h; | ||||
| int err; | int err; | ||||
| protocols = ffurl_get_protocols(NULL, NULL); | |||||
| if (options) { | |||||
| e = av_dict_get(*options, "protocol_whitelist", NULL, 0); | |||||
| if (e) | |||||
| proto_whitelist = e->value; | |||||
| e = av_dict_get(*options, "protocol_blacklist", NULL, 0); | |||||
| if (e) | |||||
| proto_blacklist = e->value; | |||||
| } | |||||
| protocols = ffurl_get_protocols(proto_whitelist, proto_blacklist); | |||||
| if (!protocols) | if (!protocols) | ||||
| return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
| @@ -872,6 +921,14 @@ int avio_open2(AVIOContext **s, const char *filename, int flags, | |||||
| internal = (*s)->opaque; | internal = (*s)->opaque; | ||||
| internal->protocols = protocols; | internal->protocols = protocols; | ||||
| if (options) { | |||||
| err = av_opt_set_dict(internal, options); | |||||
| if (err < 0) { | |||||
| avio_closep(s); | |||||
| return err; | |||||
| } | |||||
| } | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -887,6 +944,8 @@ int avio_close(AVIOContext *s) | |||||
| internal = s->opaque; | internal = s->opaque; | ||||
| h = internal->h; | h = internal->h; | ||||
| av_opt_free(internal); | |||||
| av_freep(&internal->protocols); | av_freep(&internal->protocols); | ||||
| av_freep(&s->opaque); | av_freep(&s->opaque); | ||||
| av_freep(&s->buffer); | av_freep(&s->buffer); | ||||
| @@ -20,6 +20,7 @@ | |||||
| #include "avformat.h" | #include "avformat.h" | ||||
| #include "avio_internal.h" | #include "avio_internal.h" | ||||
| #include "internal.h" | #include "internal.h" | ||||
| #include "url.h" | |||||
| #include "libavutil/internal.h" | #include "libavutil/internal.h" | ||||
| #include "libavutil/opt.h" | #include "libavutil/opt.h" | ||||
| @@ -93,7 +94,26 @@ static const AVClass av_format_context_class = { | |||||
| static int io_open_default(AVFormatContext *s, AVIOContext **pb, | static int io_open_default(AVFormatContext *s, AVIOContext **pb, | ||||
| const char *url, int flags, AVDictionary **options) | const char *url, int flags, AVDictionary **options) | ||||
| { | { | ||||
| return avio_open2(pb, url, flags, &s->interrupt_callback, options); | |||||
| AVDictionary *opts_local = NULL; | |||||
| int ret; | |||||
| if (!options) | |||||
| options = &opts_local; | |||||
| if (s->protocol_whitelist) { | |||||
| ret = av_dict_set(options, "protocol_whitelist", s->protocol_whitelist, 0); | |||||
| if (ret < 0) | |||||
| goto finish; | |||||
| } | |||||
| if (s->protocol_blacklist) { | |||||
| ret = av_dict_set(options, "protocol_blacklist", s->protocol_blacklist, 0); | |||||
| if (ret < 0) | |||||
| goto finish; | |||||
| } | |||||
| ret = avio_open2(pb, url, flags, &s->interrupt_callback, options); | |||||
| finish: | |||||
| av_dict_free(&opts_local); | |||||
| return ret; | |||||
| } | } | ||||
| static void io_close_default(AVFormatContext *s, AVIOContext *pb) | static void io_close_default(AVFormatContext *s, AVIOContext *pb) | ||||
| @@ -70,6 +70,10 @@ static const AVOption avformat_options[] = { | |||||
| {"auto", "enabled when required by target format", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_AUTO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, | {"auto", "enabled when required by target format", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_AUTO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, | ||||
| {"make_non_negative", "shift timestamps so they are non negative", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, | {"make_non_negative", "shift timestamps so they are non negative", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, | ||||
| {"make_zero", "shift timestamps so they start at 0", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_ZERO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, | {"make_zero", "shift timestamps so they start at 0", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_ZERO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, | ||||
| {"protocol_blacklist", "A comma-separated list of blacklisted protocols used for opening files internally by lavf", | |||||
| OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = "concat" }, .flags = E | D }, | |||||
| {"protocol_whitelist", "A comma-separated list of whitelisted protocols used for opening files internally by lavf", | |||||
| OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = E | D }, | |||||
| {NULL}, | {NULL}, | ||||
| }; | }; | ||||
| @@ -1668,7 +1668,8 @@ int ff_rtsp_connect(AVFormatContext *s) | |||||
| return AVERROR(EIO); | return AVERROR(EIO); | ||||
| if (!rt->protocols) { | if (!rt->protocols) { | ||||
| rt->protocols = ffurl_get_protocols(NULL, NULL); | |||||
| rt->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
| s->protocol_blacklist); | |||||
| if (!rt->protocols) | if (!rt->protocols) | ||||
| return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
| } | } | ||||
| @@ -2252,7 +2253,8 @@ static int sdp_read_header(AVFormatContext *s) | |||||
| return AVERROR(EIO); | return AVERROR(EIO); | ||||
| if (!rt->protocols) { | if (!rt->protocols) { | ||||
| rt->protocols = ffurl_get_protocols(NULL, NULL); | |||||
| rt->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
| s->protocol_blacklist); | |||||
| if (!rt->protocols) | if (!rt->protocols) | ||||
| return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
| } | } | ||||
| @@ -2379,7 +2381,8 @@ static int rtp_read_header(AVFormatContext *s) | |||||
| return AVERROR(EIO); | return AVERROR(EIO); | ||||
| if (!rt->protocols) { | if (!rt->protocols) { | ||||
| rt->protocols = ffurl_get_protocols(NULL, NULL); | |||||
| rt->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
| s->protocol_blacklist); | |||||
| if (!rt->protocols) | if (!rt->protocols) | ||||
| return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
| } | } | ||||
| @@ -640,7 +640,8 @@ static int rtsp_listen(AVFormatContext *s) | |||||
| enum RTSPMethod methodcode; | enum RTSPMethod methodcode; | ||||
| if (!rt->protocols) { | if (!rt->protocols) { | ||||
| rt->protocols = ffurl_get_protocols(NULL, NULL); | |||||
| rt->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
| s->protocol_blacklist); | |||||
| if (!rt->protocols) | if (!rt->protocols) | ||||
| return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
| } | } | ||||
| @@ -85,7 +85,8 @@ static int sap_read_header(AVFormatContext *s) | |||||
| av_strlcpy(host, "224.2.127.254", sizeof(host)); | av_strlcpy(host, "224.2.127.254", sizeof(host)); | ||||
| } | } | ||||
| sap->protocols = ffurl_get_protocols(NULL, NULL); | |||||
| sap->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
| s->protocol_blacklist); | |||||
| if (!sap->protocols) { | if (!sap->protocols) { | ||||
| ret = AVERROR(ENOMEM); | ret = AVERROR(ENOMEM); | ||||
| goto fail; | goto fail; | ||||
| @@ -138,7 +138,8 @@ static int sap_write_header(AVFormatContext *s) | |||||
| freeaddrinfo(ai); | freeaddrinfo(ai); | ||||
| } | } | ||||
| sap->protocols = ffurl_get_protocols(NULL, NULL); | |||||
| sap->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
| s->protocol_blacklist); | |||||
| if (!sap->protocols) { | if (!sap->protocols) { | ||||
| ret = AVERROR(ENOMEM); | ret = AVERROR(ENOMEM); | ||||
| goto fail; | goto fail; | ||||
| @@ -312,7 +312,7 @@ static int ism_write_header(AVFormatContext *s) | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| c->protocols = ffurl_get_protocols(NULL, NULL); | |||||
| c->protocols = ffurl_get_protocols(s->protocol_whitelist, s->protocol_blacklist); | |||||
| if (!c->protocols) { | if (!c->protocols) { | ||||
| ret = AVERROR(ENOMEM); | ret = AVERROR(ENOMEM); | ||||
| goto fail; | goto fail; | ||||
| @@ -30,7 +30,7 @@ | |||||
| #include "libavutil/version.h" | #include "libavutil/version.h" | ||||
| #define LIBAVFORMAT_VERSION_MAJOR 57 | #define LIBAVFORMAT_VERSION_MAJOR 57 | ||||
| #define LIBAVFORMAT_VERSION_MINOR 3 | |||||
| #define LIBAVFORMAT_VERSION_MINOR 4 | |||||
| #define LIBAVFORMAT_VERSION_MICRO 0 | #define LIBAVFORMAT_VERSION_MICRO 0 | ||||
| #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | ||||