From a40689e6efccb13065a0e2db22c06208482dea6f Mon Sep 17 00:00:00 2001
From: "Timothy B. Terriberry" <tterribe@xiph.org>
Date: Fri, 7 Sep 2012 06:01:53 -0700
Subject: [PATCH] Remove large multistream stack buffers.

This avoids allocating any buffers on the stack that depend on the
 total channel count.
Such buffers could easily exceed the size of the
 NONTHREADSAFE_PSEUDOSTACK.
It also checks the frame_size argument in both the encoder and
 decoder to avoid allocating large stack buffers for opus_encode()
 calls that would fail anyway or opus_decode() calls that would
 never use all that space anyway.
---
 include/opus_defines.h |  12 +-
 src/opus_decoder.c     |  13 ++
 src/opus_encoder.c     |  11 ++
 src/opus_multistream.c | 274 +++++++++++++++++++++++++++++------------
 4 files changed, 230 insertions(+), 80 deletions(-)

diff --git a/include/opus_defines.h b/include/opus_defines.h
index d86fbccf0..830d225f1 100644
--- a/include/opus_defines.h
+++ b/include/opus_defines.h
@@ -111,7 +111,8 @@ extern "C" {
 #endif
 
 /** These are the actual Encoder CTL ID numbers.
-  * They should not be used directly by applications. */
+  * They should not be used directly by applications.
+  * In general, SETs should be even and GETs should be odd.*/
 #define OPUS_SET_APPLICATION_REQUEST         4000
 #define OPUS_GET_APPLICATION_REQUEST         4001
 #define OPUS_SET_BITRATE_REQUEST             4002
@@ -138,6 +139,7 @@ extern "C" {
 #define OPUS_GET_SIGNAL_REQUEST              4025
 #define OPUS_GET_LOOKAHEAD_REQUEST           4027
 /* #define OPUS_RESET_STATE 4028 */
+#define OPUS_GET_SAMPLE_RATE_REQUEST         4029
 #define OPUS_GET_FINAL_RANGE_REQUEST         4031
 #define OPUS_GET_PITCH_REQUEST               4033
 #define OPUS_SET_GAIN_REQUEST                4034
@@ -422,6 +424,14 @@ extern "C" {
   * @hideinitializer */
 #define OPUS_GET_APPLICATION(x) OPUS_GET_APPLICATION_REQUEST, __opus_check_int_ptr(x)
 
+/** Gets the sampling rate the encoder or decoder was initialized with.
+  * This simply returns the <code>Fs</code> value passed to opus_encoder_init()
+  * or opus_decoder_init().
+  * @param[out] x <tt>opus_int32 *</tt>: Sampling rate of encoder or decoder.
+  * @hideinitializer
+  */
+#define OPUS_GET_SAMPLE_RATE(x) OPUS_GET_SAMPLE_RATE_REQUEST, __opus_check_int_ptr(x)
+
 /** Gets the total samples of delay added by the entire codec.
   * This can be queried by the encoder and then the provided number of samples can be
   * skipped on from the start of the decoder's output to provide time aligned input
diff --git a/src/opus_decoder.c b/src/opus_decoder.c
index 966ca8721..161bd0262 100644
--- a/src/opus_decoder.c
+++ b/src/opus_decoder.c
@@ -228,6 +228,8 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
       RESTORE_STACK;
       return OPUS_BUFFER_TOO_SMALL;
    }
+   /* Limit frame_size to avoid excessive stack allocations. */
+   frame_size = IMIN(frame_size, st->Fs/25*3);
    /* Payloads of 1 (2 including ToC) or 0 trigger the PLC/DTX */
    if (len<=1)
    {
@@ -856,6 +858,17 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...)
       st->frame_size = st->Fs/400;
    }
    break;
+   case OPUS_GET_SAMPLE_RATE_REQUEST:
+   {
+      opus_int32 *value = va_arg(ap, opus_int32*);
+      if (value==NULL)
+      {
+         ret = OPUS_BAD_ARG;
+         break;
+      }
+      *value = st->Fs;
+   }
+   break;
    case OPUS_GET_PITCH_REQUEST:
    {
       opus_int32 *value = va_arg(ap, opus_int32*);
diff --git a/src/opus_encoder.c b/src/opus_encoder.c
index 1fcbf0f55..b77e48e62 100644
--- a/src/opus_encoder.c
+++ b/src/opus_encoder.c
@@ -1531,6 +1531,17 @@ int opus_encoder_ctl(OpusEncoder *st, int request, ...)
                 *value += st->delay_compensation;
         }
         break;
+        case OPUS_GET_SAMPLE_RATE_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (value==NULL)
+            {
+                ret = OPUS_BAD_ARG;
+                break;
+            }
+            *value = st->Fs;
+        }
+        break;
         case OPUS_GET_FINAL_RANGE_REQUEST:
         {
             opus_uint32 *value = va_arg(ap, opus_uint32*);
diff --git a/src/opus_multistream.c b/src/opus_multistream.c
index 623e0835c..955dc818a 100644
--- a/src/opus_multistream.c
+++ b/src/opus_multistream.c
@@ -55,7 +55,6 @@ struct OpusMSDecoder {
    /* Decoder states go here */
 };
 
-
 #ifdef FIXED_POINT
 #define opus_encode_native opus_encode
 #else
@@ -221,24 +220,31 @@ OpusMSEncoder *opus_multistream_encoder_create(
    return st;
 }
 
+typedef void (*opus_copy_channel_in_func)(
+  opus_val16 *dst,
+  int dst_stride,
+  const void *src,
+  int src_stride,
+  int src_channel,
+  int frame_size
+);
+
 /* Max size in case the encoder decides to return three frames */
 #define MS_FRAME_TMP (3*1275+7)
-#ifdef FIXED_POINT
-int opus_multistream_encode
-#else
-int opus_multistream_encode_float
-#endif
+static int opus_multistream_encode_native
 (
     OpusMSEncoder *st,
-    const opus_val16 *pcm,
+    opus_copy_channel_in_func copy_channel_in,
+    const void *pcm,
     int frame_size,
     unsigned char *data,
     opus_int32 max_data_bytes
 )
 {
+   opus_int32 Fs;
    int coupled_size;
    int mono_size;
-   int s, i;
+   int s;
    char *ptr;
    int tot_size;
    VARDECL(opus_val16, buf);
@@ -246,8 +252,18 @@ int opus_multistream_encode_float
    OpusRepacketizer rp;
    ALLOC_STACK;
 
-   ALLOC(buf, 2*frame_size, opus_val16);
    ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_SAMPLE_RATE(&Fs));
+   /* Validate frame_size before using it to allocate stack space.
+      This mirrors the checks in opus_encode[_float](). */
+   if (400*frame_size != Fs && 200*frame_size != Fs &&
+       100*frame_size != Fs &&  50*frame_size != Fs &&
+        25*frame_size != Fs &&  50*frame_size != 3*Fs)
+   {
+      RESTORE_STACK;
+      return OPUS_BAD_ARG;
+   }
+   ALLOC(buf, 2*frame_size, opus_val16);
    coupled_size = opus_encoder_get_size(2);
    mono_size = opus_encoder_get_size(1);
 
@@ -271,16 +287,15 @@ int opus_multistream_encode_float
          int left, right;
          left = get_left_channel(&st->layout, s, -1);
          right = get_right_channel(&st->layout, s, -1);
-         for (i=0;i<frame_size;i++)
-         {
-            buf[2*i] = pcm[st->layout.nb_channels*i+left];
-            buf[2*i+1] = pcm[st->layout.nb_channels*i+right];
-         }
+         (*copy_channel_in)(buf, 2,
+            pcm, st->layout.nb_channels, left, frame_size);
+         (*copy_channel_in)(buf+1, 2,
+            pcm, st->layout.nb_channels, right, frame_size);
          ptr += align(coupled_size);
       } else {
          int chan = get_mono_channel(&st->layout, s, -1);
-         for (i=0;i<frame_size;i++)
-            buf[i] = pcm[st->layout.nb_channels*i+chan];
+         (*copy_channel_in)(buf, 1,
+            pcm, st->layout.nb_channels, chan, frame_size);
          ptr += align(mono_size);
       }
       /* number of bytes left (+Toc) */
@@ -307,7 +322,60 @@ int opus_multistream_encode_float
 
 }
 
+#if !defined(DISABLE_FLOAT_API)
+static void opus_copy_channel_in_float(
+  opus_val16 *dst,
+  int dst_stride,
+  const void *src,
+  int src_stride,
+  int src_channel,
+  int frame_size
+)
+{
+   const float *float_src;
+   int i;
+   float_src = (const float *)src;
+   for (i=0;i<frame_size;i++)
+#if defined(FIXED_POINT)
+      dst[i*dst_stride] = FLOAT2INT16(float_src[i*src_stride+src_channel]);
+#else
+      dst[i*dst_stride] = float_src[i*src_stride+src_channel];
+#endif
+}
+#endif
+
+static void opus_copy_channel_in_short(
+  opus_val16 *dst,
+  int dst_stride,
+  const void *src,
+  int src_stride,
+  int src_channel,
+  int frame_size
+)
+{
+   const opus_int16 *short_src;
+   int i;
+   short_src = (const opus_int16 *)src;
+   for (i=0;i<frame_size;i++)
+#if defined(FIXED_POINT)
+      dst[i*dst_stride] = short_src[i*src_stride+src_channel];
+#else
+      dst[i*dst_stride] = (1/32768.f)*short_src[i*src_stride+src_channel];
+#endif
+}
+
 #ifdef FIXED_POINT
+int opus_multistream_encode(
+    OpusMSEncoder *st,
+    const opus_val16 *pcm,
+    int frame_size,
+    unsigned char *data,
+    opus_int32 max_data_bytes
+)
+{
+   return opus_multistream_encode_native(st, opus_copy_channel_in_short,
+      pcm, frame_size, data, max_data_bytes);
+}
 
 #ifndef DISABLE_FLOAT_API
 int opus_multistream_encode_float(
@@ -318,22 +386,26 @@ int opus_multistream_encode_float(
     opus_int32 max_data_bytes
 )
 {
-   int i, ret;
-   VARDECL(opus_int16, in);
-   ALLOC_STACK;
-
-   ALLOC(in, frame_size*st->layout.nb_channels, opus_int16);
-
-   for (i=0;i<frame_size*st->layout.nb_channels;i++)
-      in[i] = FLOAT2INT16(pcm[i]);
-   ret = opus_multistream_encode(st, in, frame_size, data, max_data_bytes);
-   RESTORE_STACK;
-   return ret;
+   return opus_multistream_encode_native(st, opus_copy_channel_in_float,
+      pcm, frame_size, data, max_data_bytes);
 }
 #endif
 
 #else
 
+int opus_multistream_encode_float
+(
+    OpusMSEncoder *st,
+    const opus_val16 *pcm,
+    int frame_size,
+    unsigned char *data,
+    opus_int32 max_data_bytes
+)
+{
+   return opus_multistream_encode_native(st, opus_copy_channel_in_float,
+      pcm, frame_size, data, max_data_bytes);
+}
+
 int opus_multistream_encode(
     OpusMSEncoder *st,
     const opus_int16 *pcm,
@@ -342,19 +414,9 @@ int opus_multistream_encode(
     opus_int32 max_data_bytes
 )
 {
-   int i, ret;
-   VARDECL(float, in);
-   ALLOC_STACK;
-
-   ALLOC(in, frame_size*st->layout.nb_channels, float);
-
-   for (i=0;i<frame_size*st->layout.nb_channels;i++)
-      in[i] = (1.f/32768.f)*pcm[i];
-   ret = opus_multistream_encode_float(st, in, frame_size, data, max_data_bytes);
-   RESTORE_STACK;
-   return ret;
+   return opus_multistream_encode_native(st, opus_copy_channel_in_short,
+      pcm, frame_size, data, max_data_bytes);
 }
-
 #endif
 
 int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
@@ -419,6 +481,7 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
    case OPUS_GET_VBR_CONSTRAINT_REQUEST:
    case OPUS_GET_SIGNAL_REQUEST:
    case OPUS_GET_LOOKAHEAD_REQUEST:
+   case OPUS_GET_SAMPLE_RATE_REQUEST:
    case OPUS_GET_INBAND_FEC_REQUEST:
    {
       OpusEncoder *enc;
@@ -604,23 +667,37 @@ OpusMSDecoder *opus_multistream_decoder_create(
 
 }
 
+typedef void (*opus_copy_channel_out_func)(
+  void *dst,
+  int dst_stride,
+  int dst_channel,
+  const opus_val16 *src,
+  int src_stride,
+  int frame_size
+);
+
 static int opus_multistream_decode_native(
       OpusMSDecoder *st,
       const unsigned char *data,
       opus_int32 len,
-      opus_val16 *pcm,
+      void *pcm,
+      opus_copy_channel_out_func copy_channel_out,
       int frame_size,
       int decode_fec
 )
 {
+   opus_int32 Fs;
    int coupled_size;
    int mono_size;
-   int s, i, c;
+   int s, c;
    char *ptr;
    int do_plc=0;
    VARDECL(opus_val16, buf);
    ALLOC_STACK;
 
+   /* Limit frame_size to avoid excessive stack allocations. */
+   opus_multistream_decoder_ctl(st, OPUS_GET_SAMPLE_RATE(&Fs));
+   frame_size = IMIN(frame_size, Fs/25*3);
    ALLOC(buf, 2*frame_size, opus_val16);
    ptr = (char*)st + align(sizeof(OpusMSDecoder));
    coupled_size = opus_decoder_get_size(2);
@@ -672,16 +749,16 @@ static int opus_multistream_decode_native(
          /* Copy "left" audio to the channel(s) where it belongs */
          while ( (chan = get_left_channel(&st->layout, s, prev)) != -1)
          {
-            for (i=0;i<frame_size;i++)
-               pcm[st->layout.nb_channels*i+chan] = buf[2*i];
+            (*copy_channel_out)(pcm, st->layout.nb_channels, chan,
+               buf, 2, frame_size);
             prev = chan;
          }
          prev = -1;
          /* Copy "right" audio to the channel(s) where it belongs */
          while ( (chan = get_right_channel(&st->layout, s, prev)) != -1)
          {
-            for (i=0;i<frame_size;i++)
-               pcm[st->layout.nb_channels*i+chan] = buf[2*i+1];
+            (*copy_channel_out)(pcm, st->layout.nb_channels, chan,
+               buf+1, 2, frame_size);
             prev = chan;
          }
       } else {
@@ -690,8 +767,8 @@ static int opus_multistream_decode_native(
          /* Copy audio to the channel(s) where it belongs */
          while ( (chan = get_mono_channel(&st->layout, s, prev)) != -1)
          {
-            for (i=0;i<frame_size;i++)
-               pcm[st->layout.nb_channels*i+chan] = buf[i];
+            (*copy_channel_out)(pcm, st->layout.nb_channels, chan,
+               buf, 1, frame_size);
             prev = chan;
          }
       }
@@ -701,14 +778,74 @@ static int opus_multistream_decode_native(
    {
       if (st->layout.mapping[c] == 255)
       {
-         for (i=0;i<frame_size;i++)
-            pcm[st->layout.nb_channels*i+c] = 0;
+         (*copy_channel_out)(pcm, st->layout.nb_channels, c,
+            NULL, 0, frame_size);
       }
    }
    RESTORE_STACK;
    return frame_size;
 }
 
+#if !defined(DISABLE_FLOAT_API)
+static void opus_copy_channel_out_float(
+  void *dst,
+  int dst_stride,
+  int dst_channel,
+  const opus_val16 *src,
+  int src_stride,
+  int frame_size
+)
+{
+   float *float_dst;
+   int i;
+   float_dst = (float*)dst;
+   if (src != NULL)
+   {
+      for (i=0;i<frame_size;i++)
+#if defined(FIXED_POINT)
+         float_dst[i*dst_stride+dst_channel] = (1/32768.f)*src[i*src_stride];
+#else
+         float_dst[i*dst_stride+dst_channel] = src[i*src_stride];
+#endif
+   }
+   else
+   {
+      for (i=0;i<frame_size;i++)
+         float_dst[i*dst_stride+dst_channel] = 0;
+   }
+}
+#endif
+
+static void opus_copy_channel_out_short(
+  void *dst,
+  int dst_stride,
+  int dst_channel,
+  const opus_val16 *src,
+  int src_stride,
+  int frame_size
+)
+{
+   opus_int16 *short_dst;
+   int i;
+   short_dst = (opus_int16*)dst;
+   if (src != NULL)
+   {
+      for (i=0;i<frame_size;i++)
+#if defined(FIXED_POINT)
+         short_dst[i*dst_stride+dst_channel] = src[i*src_stride];
+#else
+         short_dst[i*dst_stride+dst_channel] = FLOAT2INT16(src[i*src_stride]);
+#endif
+   }
+   else
+   {
+      for (i=0;i<frame_size;i++)
+         short_dst[i*dst_stride+dst_channel] = 0;
+   }
+}
+
+
+
 #ifdef FIXED_POINT
 int opus_multistream_decode(
       OpusMSDecoder *st,
@@ -719,27 +856,16 @@ int opus_multistream_decode(
       int decode_fec
 )
 {
-   return opus_multistream_decode_native(st, data, len, pcm, frame_size, decode_fec);
+   return opus_multistream_decode_native(st, data, len,
+       pcm, opus_copy_channel_out_short, frame_size, decode_fec);
 }
 
 #ifndef DISABLE_FLOAT_API
 int opus_multistream_decode_float(OpusMSDecoder *st, const unsigned char *data,
       opus_int32 len, float *pcm, int frame_size, int decode_fec)
 {
-   VARDECL(opus_int16, out);
-   int ret, i;
-   ALLOC_STACK;
-
-   ALLOC(out, frame_size*st->layout.nb_channels, opus_int16);
-
-   ret = opus_multistream_decode_native(st, data, len, out, frame_size, decode_fec);
-   if (ret > 0)
-   {
-      for (i=0;i<ret*st->layout.nb_channels;i++)
-         pcm[i] = (1.f/32768.f)*(out[i]);
-   }
-   RESTORE_STACK;
-   return ret;
+   return opus_multistream_decode_native(st, data, len,
+       pcm, opus_copy_channel_out_float, frame_size, decode_fec);
 }
 #endif
 
@@ -748,20 +874,8 @@ int opus_multistream_decode_float(OpusMSDecoder *st, const unsigned char *data,
 int opus_multistream_decode(OpusMSDecoder *st, const unsigned char *data,
       opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec)
 {
-   VARDECL(float, out);
-   int ret, i;
-   ALLOC_STACK;
-
-   ALLOC(out, frame_size*st->layout.nb_channels, float);
-
-   ret = opus_multistream_decode_native(st, data, len, out, frame_size, decode_fec);
-   if (ret > 0)
-   {
-      for (i=0;i<ret*st->layout.nb_channels;i++)
-         pcm[i] = FLOAT2INT16(out[i]);
-   }
-   RESTORE_STACK;
-   return ret;
+   return opus_multistream_decode_native(st, data, len,
+       pcm, opus_copy_channel_out_short, frame_size, decode_fec);
 }
 
 int opus_multistream_decode_float(
@@ -773,7 +887,8 @@ int opus_multistream_decode_float(
       int decode_fec
 )
 {
-   return opus_multistream_decode_native(st, data, len, pcm, frame_size, decode_fec);
+   return opus_multistream_decode_native(st, data, len,
+       pcm, opus_copy_channel_out_float, frame_size, decode_fec);
 }
 #endif
 
@@ -792,6 +907,7 @@ int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...)
    switch (request)
    {
        case OPUS_GET_BANDWIDTH_REQUEST:
+       case OPUS_GET_SAMPLE_RATE_REQUEST:
        {
           OpusDecoder *dec;
           /* For int32* GET params, just query the first stream */
-- 
GitLab