Opened 15 months ago

#10199 new defect

OOB memory access with AVIOContext, seeking, AVIO_FLAG_DIRECT, and ogg files

Reported by: Andrew Kelley Owned by:
Priority: normal Component: undetermined
Version: 5.1.2 Keywords:
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

To reproduce, apply the following diff:

--- a/doc/examples/avio_reading.c
+++ b/doc/examples/avio_reading.c
@@ -37,6 +37,7 @@
 struct buffer_data {
     uint8_t *ptr;
     size_t size; ///< size left in the buffer
+    size_t original_size;
 };
 
 static int read_packet(void *opaque, uint8_t *buf, int buf_size)
@@ -56,6 +57,39 @@ static int read_packet(void *opaque, uint8_t *buf, int buf_size)
     return buf_size;
 }
 
+static int64_t seek_packet(void *opaque, int64_t offset, int whence)
+{
+    struct buffer_data *bd = (struct buffer_data *)opaque;
+
+    if (whence & AVSEEK_FORCE) {
+        // doesn't matter
+        whence -= AVSEEK_FORCE;
+    }
+
+    if (whence & AVSEEK_SIZE) {
+        return bd->original_size;
+    }
+
+    switch (whence) {
+        case SEEK_SET:
+        {
+            uint8_t *base_ptr = (bd->ptr + bd->size) - bd->original_size;
+            bd->ptr = base_ptr + offset;
+            bd->size = bd->original_size - offset;
+            return 0;
+        }
+        case SEEK_CUR:
+            bd->ptr += offset;
+            bd->size -= offset;
+            return 0;
+        case SEEK_END:
+            bd->ptr += bd->size;
+            bd->size = 0;
+            return 0;
+    }
+    return -1;
+}
+
 int main(int argc, char *argv[])
 {
     AVFormatContext *fmt_ctx = NULL;
@@ -82,6 +116,7 @@ int main(int argc, char *argv[])
     /* fill opaque structure used by the AVIOContext read callback */
     bd.ptr  = buffer;
     bd.size = buffer_size;
+    bd.original_size = buffer_size;
 
     if (!(fmt_ctx = avformat_alloc_context())) {
         ret = AVERROR(ENOMEM);
@@ -94,11 +129,12 @@ int main(int argc, char *argv[])
         goto end;
     }
     avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
-                                  0, &bd, &read_packet, NULL, NULL);
+                                  0, &bd, &read_packet, NULL, &seek_packet);
     if (!avio_ctx) {
         ret = AVERROR(ENOMEM);
         goto end;
     }
+    avio_ctx->direct = AVIO_FLAG_DIRECT;
     fmt_ctx->pb = avio_ctx;
 
     ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL);

Next, build the example and then run it with any ogg file:

[nix-shell:~/Downloads/ffmpeg]$ make examples
CC	doc/examples/avio_reading.o
LD	doc/examples/avio_reading_g
STRIP	doc/examples/avio_reading
[nix-shell:~/Downloads/ffmpeg]$ ./doc/examples/avio_reading_g ~/tmp/danse1.ogg 
ptr:0x7fb9605df000 size:88407
ptr:0x7fb9605df000 size:88407
ptr:0x7fb9605df004 size:88403
ptr:0x7fb9605df016 size:88385
[ogg @ 0x264a040] CRC mismatch!
ptr:0x7fb9605df000 size:88407
ptr:0x7fb9605df004 size:88403
ptr:0x7fb9605df008 size:88399
Segmentation fault (core dumped)

[nix-shell:~/Downloads/ffmpeg]$ gdb ./doc/examples/avio_reading_g ~/tmp/danse1.ogg  -ex 'run'
GNU gdb (GDB) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./doc/examples/avio_reading_g...
"/home/andy/tmp/danse1.ogg" is not a core dump: file format not recognized
Starting program: /home/andy/Downloads/ffmpeg/doc/examples/avio_reading_g 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/nix/store/4nlgxhb09sdr51nc9hdm8az5b08vzkgx-glibc-2.35-163/lib/libthread_db.so.1".
usage: /home/andy/Downloads/ffmpeg/doc/examples/avio_reading_g input_file
API example program to show how to read from a custom buffer accessed through AVIOContext.
[Inferior 1 (process 1486710) exited with code 01]
(gdb) 
quit

[nix-shell:~/Downloads/ffmpeg]$ gdb ./doc/examples/avio_reading_g -ex 'run ~/tmp/danse1.ogg '
GNU gdb (GDB) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./doc/examples/avio_reading_g...
Starting program: /home/andy/Downloads/ffmpeg/doc/examples/avio_reading_g ~/tmp/danse1.ogg
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/nix/store/4nlgxhb09sdr51nc9hdm8az5b08vzkgx-glibc-2.35-163/lib/libthread_db.so.1".
ptr:0x7ffff7c9f000 size:88407
ptr:0x7ffff7c9f000 size:88407
ptr:0x7ffff7c9f004 size:88403
ptr:0x7ffff7c9f016 size:88385
[ogg @ 0x1a5e040] CRC mismatch!
ptr:0x7ffff7c9f000 size:88407
ptr:0x7ffff7c9f004 size:88403
ptr:0x7ffff7c9f008 size:88399

Program received signal SIGSEGV, Segmentation fault.
av_crc (ctx=0x1956f40 <av_crc_table+12288>, crc=853835309, crc@entry=1336520799, buffer=0x1a7f000 <error: Cannot access memory at address 0x1a7f000>, buffer@entry=0x1a605f6 "", length=length@entry=4294967242) at libavutil/crc.c:403
403	            crc ^= av_le2ne32(*(const uint32_t *) buffer); buffer += 4;
(gdb) bt
#0  av_crc (ctx=0x1956f40 <av_crc_table+12288>, crc=853835309, crc@entry=1336520799, buffer=0x1a7f000 <error: Cannot access memory at address 0x1a7f000>, buffer@entry=0x1a605f6 "", length=length@entry=4294967242) at libavutil/crc.c:403
#1  0x000000000046b965 in ff_crc04C11DB7_update (checksum=1336520799, buf=0x1a605f6 "", len=4294967242) at libavformat/aviobuf.c:597
#2  0x000000000046b9e4 in ffio_get_checksum (s=s@entry=0x1a5f800) at libavformat/aviobuf.c:614
#3  0x00000000005614d1 in ogg_read_page (s=s@entry=0x1a5e040, sid=sid@entry=0x7fffffffaabc, probing=probing@entry=0) at libavformat/oggdec.c:375
#4  0x0000000000561d1f in ogg_packet (s=s@entry=0x1a5e040, sid=sid@entry=0x0, dstart=dstart@entry=0x0, dsize=dsize@entry=0x0, fpos=fpos@entry=0x0) at libavformat/oggdec.c:515
#5  0x000000000056261a in ogg_read_header (s=0x1a5e040) at libavformat/oggdec.c:734
#6  0x0000000000472f19 in avformat_open_input (ps=ps@entry=0x7fffffffac00, filename=filename@entry=0x0, fmt=fmt@entry=0x0, options=options@entry=0x0) at libavformat/demux.c:310
#7  0x0000000000466c08 in main (argc=<optimized out>, argv=<optimized out>) at doc/examples/avio_reading.c:140
(gdb) up
#1  0x000000000046b965 in ff_crc04C11DB7_update (checksum=1336520799, buf=0x1a605f6 "", len=4294967242) at libavformat/aviobuf.c:597
597	    return av_crc(av_crc_get_table(AV_CRC_32_IEEE), checksum, buf, len);
(gdb) 
#2  0x000000000046b9e4 in ffio_get_checksum (s=s@entry=0x1a5f800) at libavformat/aviobuf.c:614
614	    s->checksum = s->update_checksum(s->checksum, s->checksum_ptr,
(gdb) p s[0]
$1 = {
  av_class = 0x0,
  buffer = 0x1a605c0 "",
  buffer_size = 4096,
  buf_ptr = 0x1a605c0 "",
  buf_end = 0x1a605c0 "",
  opaque = 0x7fffffffac20,
  read_packet = 0x466e40 <read_packet>,
  write_packet = 0x0,
  seek = 0x466eb0 <seek_packet>,
  pos = 80,
  eof_reached = 0,
  error = 0,
  write_flag = 0,
  max_packet_size = 0,
  min_packet_size = 0,
  checksum = 1336520799,
  checksum_ptr = 0x1a605f6 "",
  update_checksum = 0x46b940 <ff_crc04C11DB7_update>,
  read_pause = 0x0,
  read_seek = 0x0,
  seekable = 1,
  direct = 32768,
  protocol_whitelist = 0x0,
  protocol_blacklist = 0x0,
  write_data_type = 0x0,
  ignore_boundary_point = 0,
  written = 0,
  buf_ptr_max = 0x1a605c0 "",
  bytes_read = 18440,
  bytes_written = 0
}
(gdb) 

I have included some gdb output as a clue: checksum_ptr illegally becomes greater than buf_ptr, causing the subtraction in ffio_get_checksum to overflow.

Change History (0)

Note: See TracTickets for help on using tickets.