1 /******************************************************************************
2  *
3  * Copyright (C) 2020 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  *****************************************************************************
18  * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
19  */
20 
21 #include <stdint.h>
22 #include <string.h>
23 extern "C" {
24 #include <Tremolo/codec_internal.h>
25 
26 int _vorbis_unpack_books(vorbis_info *vi, oggpack_buffer *opb);
27 int _vorbis_unpack_info(vorbis_info *vi, oggpack_buffer *opb);
28 int _vorbis_unpack_comment(vorbis_comment *vc, oggpack_buffer *opb);
29 }
30 
31 constexpr int16_t kMaxNumSamplesPerChannel = 8192;
32 constexpr size_t kVorbisHeaderlength = 7;
33 
34 class Codec {
35  public:
36   Codec() = default;
~Codec()37   ~Codec() { deInitDecoder(); }
38   bool initDecoder();
39   void decodeFrames(const uint8_t *data, size_t size);
40   void deInitDecoder();
41 
42  private:
43   bool mInfoUnpacked = false;
44   bool mBooksUnpacked = false;
45   int32_t mNumFramesLeftOnPage = -1;
46   vorbis_dsp_state *mState = nullptr;
47   vorbis_info *mVi = nullptr;
48 };
49 
initDecoder()50 bool Codec::initDecoder() {
51   mVi = new vorbis_info{};
52   if (!mVi) {
53     return false;
54   }
55   vorbis_info_clear(mVi);
56 
57   mState = new vorbis_dsp_state{};
58   if (!mState) {
59     return false;
60   }
61   vorbis_dsp_clear(mState);
62 
63   mNumFramesLeftOnPage = -1;
64   mInfoUnpacked = false;
65   mBooksUnpacked = false;
66 
67   return true;
68 }
69 
makeBitReader(const uint8_t * data,size_t size,ogg_buffer * buf,ogg_reference * ref,oggpack_buffer * bits)70 static void makeBitReader(const uint8_t *data, size_t size, ogg_buffer *buf, ogg_reference *ref,
71                           oggpack_buffer *bits) {
72   buf->data = const_cast<uint8_t *>(data);
73   buf->size = size;
74   buf->refcount = 1;
75   buf->ptr.owner = nullptr;
76 
77   ref->buffer = buf;
78   ref->begin = 0;
79   ref->length = size;
80   ref->next = nullptr;
81 
82   oggpack_readinit(bits, ref);
83 }
84 
decodeFrames(const uint8_t * data,size_t size)85 void Codec::decodeFrames(const uint8_t *data, size_t size) {
86   /* Decode vorbis headers only once */
87   while (size > 0) {
88     if (size > kVorbisHeaderlength && (!memcmp(&data[1], "vorbis", 6)) &&
89         (!mInfoUnpacked || !mBooksUnpacked)) {
90       if ((data[0] == 1) || (data[0] == 5)) {
91         ogg_buffer buf;
92         ogg_reference ref;
93         oggpack_buffer bits;
94         /* skip kVorbisHeaderlength <type + "vorbis"> bytes */
95         makeBitReader(data + kVorbisHeaderlength, size - kVorbisHeaderlength, &buf, &ref, &bits);
96         if (data[0] == 1) {
97           // release any memory that vorbis_info_init will blindly overwrite
98           vorbis_info_clear(mVi);
99           vorbis_info_init(mVi);
100           if (0 != _vorbis_unpack_info(mVi, &bits)) {
101             return;
102           }
103           mInfoUnpacked = true;
104         } else { /* data[0] == 5*/
105           if (!mInfoUnpacked) {
106             return;
107           }
108           if (0 != _vorbis_unpack_books(mVi, &bits)) {
109             return;
110           }
111           // release any memory that vorbis_dsp_init will blindly overwrite
112           vorbis_dsp_clear(mState);
113           if (0 != vorbis_dsp_init(mState, mVi)) {
114             return;
115           }
116           mBooksUnpacked = true;
117           data += kVorbisHeaderlength;
118           size -= kVorbisHeaderlength;
119           break;
120         }
121       }
122     }
123     ++data;
124     --size;
125   }
126 
127   if (!mInfoUnpacked || !mBooksUnpacked) {
128     return;
129   }
130 
131   int32_t numPageFrames = 0;
132   if (size < sizeof(numPageFrames)) {
133     return;
134   }
135   memcpy(&numPageFrames, data + size - sizeof(numPageFrames), sizeof(numPageFrames));
136   size -= sizeof(numPageFrames);
137   if (numPageFrames >= 0) {
138     mNumFramesLeftOnPage = numPageFrames;
139   }
140 
141   ogg_buffer buf;
142   buf.data = const_cast<unsigned char *>(data);
143   buf.size = size;
144   buf.refcount = 1;
145   buf.ptr.owner = nullptr;
146 
147   ogg_reference ref;
148   ref.buffer = &buf;
149   ref.begin = 0;
150   ref.length = buf.size;
151   ref.next = nullptr;
152 
153   ogg_packet pack;
154   pack.packet = &ref;
155   pack.bytes = ref.length;
156   pack.b_o_s = 0;
157   pack.e_o_s = 0;
158   pack.granulepos = 0;
159   pack.packetno = 0;
160 
161   int ret = vorbis_dsp_synthesis(mState, &pack, 1);
162   if (0 == ret) {
163     size_t maxSamplesInBuffer = kMaxNumSamplesPerChannel * mVi->channels;
164     size_t outCapacity = maxSamplesInBuffer * sizeof(int16_t);
165     int16_t outputBuf[outCapacity];
166     vorbis_dsp_pcmout(mState, outputBuf, kMaxNumSamplesPerChannel);
167   }
168 }
169 
deInitDecoder()170 void Codec::deInitDecoder() {
171   if (mState) {
172     vorbis_dsp_clear(mState);
173     delete mState;
174     mState = nullptr;
175   }
176 
177   if (mVi) {
178     vorbis_info_clear(mVi);
179     delete mVi;
180     mVi = nullptr;
181   }
182 }
183 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)184 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
185   if (size < kVorbisHeaderlength + 1) { /* 7 bytes for header , at least 1 byte for data */
186     return 0;
187   }
188   Codec *codec = new Codec();
189   if (!codec) {
190     return 0;
191   }
192   if (codec->initDecoder()) {
193     codec->decodeFrames(data, size);
194   }
195   delete codec;
196   return 0;
197 }
198