Do up-front validation of multistream packets
authorJean-Marc Valin <jmvalin@jmvalin.ca>
Fri, 11 Oct 2013 22:06:00 +0000 (18:06 -0400)
committerJean-Marc Valin <jmvalin@jmvalin.ca>
Fri, 11 Oct 2013 22:13:01 +0000 (18:13 -0400)
Prevents the decoder from being out-of-sync on an invalid packet. Also
returns OPUS_INVALID_PACKET on a corrupted FEC packet.

src/opus_decoder.c
src/opus_multistream_decoder.c
src/opus_private.h

index 5b56ac1..ee0c9f5 100644 (file)
@@ -355,7 +355,7 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
                  pcm_ptr[i] = 0;
            } else {
              RESTORE_STACK;
-             return OPUS_INVALID_PACKET;
+             return OPUS_INTERNAL_ERROR;
            }
         }
         pcm_ptr += silk_frame_size * st->channels;
@@ -581,7 +581,7 @@ static int parse_size(const unsigned char *data, opus_int32 len, opus_int16 *siz
    }
 }
 
-static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
+int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
       int self_delimited, unsigned char *out_toc,
       const unsigned char *frames[48], opus_int16 size[48], int *payload_offset)
 {
@@ -710,6 +710,9 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
       size[count-1] = (opus_int16)last_size;
    }
 
+   if (payload_offset)
+      *payload_offset = (int)(data-data0);
+
    if (frames)
    {
       for (i=0;i<count;i++)
@@ -722,9 +725,6 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
    if (out_toc)
       *out_toc = toc;
 
-   if (payload_offset)
-      *payload_offset = (int)(data-data0);
-
    return count;
 }
 
@@ -777,6 +777,9 @@ int opus_decode_native(OpusDecoder *st, const unsigned char *data,
 
    count = opus_packet_parse_impl(data, len, self_delimited, &toc, NULL, size, &offset);
 
+   if (count<0)
+      return count;
+
    data += offset;
 
    if (decode_fec)
@@ -814,11 +817,7 @@ int opus_decode_native(OpusDecoder *st, const unsigned char *data,
          return frame_size;
       }
    }
-   tot_offset = 0;
-   if (count < 0)
-      return count;
-
-   tot_offset += offset;
+   tot_offset = offset;
 
    if (count*packet_frame_size > frame_size)
       return OPUS_BUFFER_TOO_SMALL;
index f3d2311..d04a99f 100644 (file)
@@ -152,6 +152,37 @@ typedef void (*opus_copy_channel_out_func)(
   int frame_size
 );
 
+static int opus_multistream_packet_validate(const unsigned char *data,
+      opus_int32 len, int nb_streams)
+{
+   int s;
+   int i;
+   int count;
+   unsigned char toc;
+   opus_int16 size[48];
+   int offset;
+   int samples=0;
+
+   for (s=0;s<nb_streams;s++)
+   {
+      int tmp_samples;
+      if (len<=0)
+         return OPUS_INVALID_PACKET;
+      count = opus_packet_parse_impl(data, len, s!=nb_streams-1, &toc, NULL, size, &offset);
+      if (count<0)
+         return count;
+      for (i=0;i<count;i++)
+         offset += size[i];
+      tmp_samples = opus_packet_get_nb_samples(data, offset, 48000);
+      if (s!=0 && samples != tmp_samples)
+         return OPUS_INVALID_PACKET;
+      samples = tmp_samples;
+      data += offset;
+      len -= offset;
+   }
+   return OPUS_OK;
+}
+
 static int opus_multistream_decode_native(
       OpusMSDecoder *st,
       const unsigned char *data,
@@ -183,9 +214,24 @@ static int opus_multistream_decode_native(
    if (len==0)
       do_plc = 1;
    if (len < 0)
+   {
+      RESTORE_STACK;
       return OPUS_BAD_ARG;
+   }
    if (!do_plc && len < 2*st->layout.nb_streams-1)
+   {
+      RESTORE_STACK;
       return OPUS_INVALID_PACKET;
+   }
+   if (!do_plc)
+   {
+      int ret = opus_multistream_packet_validate(data, len, st->layout.nb_coupled_streams);
+      if (ret < 0)
+      {
+         RESTORE_STACK;
+         return ret;
+      }
+   }
    for (s=0;s<st->layout.nb_streams;s++)
    {
       OpusDecoder *dec;
index 0e739eb..aaf3cbf 100644 (file)
@@ -112,6 +112,10 @@ static inline int align(int i)
     return (i+(int)sizeof(void *)-1)&-(int)sizeof(void *);
 }
 
+int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
+      int self_delimited, unsigned char *out_toc,
+      const unsigned char *frames[48], opus_int16 size[48], int *payload_offset);
+
 opus_int32 opus_repacketizer_out_range_impl(OpusRepacketizer *rp, int begin, int end, unsigned char *data, opus_int32 maxlen, int self_delimited);
 
 #endif /* OPUS_PRIVATE_H */