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