Skip to content

Commit 43bf2f1

Browse files
authored
ggwave : fix out-of-bounds access in ggwave_decode (#53)
Also, provide a memory-safe overload called ggwave_ndecode() The overload takes an extra parameter that specifies the size of the output buffer and thus limits the size of the Rx payload that can be decoded and stored.
1 parent 9cf2d47 commit 43bf2f1

File tree

3 files changed

+77
-8
lines changed

3 files changed

+77
-8
lines changed

include/ggwave/ggwave.h

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,10 @@ extern "C" {
206206
// If the return value is -1 then there was an error during the decoding process.
207207
// Usually can occur if there is a lot of background noise in the audio.
208208
//
209-
// If the return value is greater than 0, then there will be that number of bytes
210-
// decoded in the outputBuffer
209+
// If the return value is greater than 0, then there are that number of bytes decoded.
210+
//
211+
// IMPORTANT:
212+
// Notice that the decoded data written to the outputBuffer is NOT null terminated.
211213
//
212214
// Example:
213215
//
@@ -235,6 +237,22 @@ extern "C" {
235237
int dataSize,
236238
char * outputBuffer);
237239

240+
// Memory-safe overload of ggwave_decode
241+
//
242+
// outputSize - optionally specify the size of the output buffer
243+
//
244+
// If the return value is -2 then the provided outputBuffer was not big enough to
245+
// store the decoded data.
246+
//
247+
// See ggwave_decode for more information
248+
//
249+
GGWAVE_API int ggwave_ndecode(
250+
ggwave_Instance instance,
251+
const char * dataBuffer,
252+
int dataSize,
253+
char * outputBuffer,
254+
int outputSize);
255+
238256
#ifdef __cplusplus
239257
}
240258

@@ -414,6 +432,8 @@ class GGWave {
414432
void setRxProtocols(const RxProtocols & rxProtocols) { m_rxProtocols = rxProtocols; }
415433
const RxProtocols & getRxProtocols() const { return m_rxProtocols; }
416434

435+
int lastRxDataLength() const { return m_lastRxDataLength; }
436+
417437
const TxRxData & getRxData() const { return m_rxData; }
418438
const RxProtocol & getRxProtocol() const { return m_rxProtocol; }
419439
const RxProtocolId & getRxProtocolId() const { return m_rxProtocolId; }

src/ggwave.cpp

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,54 @@ int ggwave_decode(
125125

126126
ggWave->decode(cbWaveformInp);
127127

128-
// todo : avoid allocation
128+
// TODO : avoid allocation
129129
GGWave::TxRxData rxData;
130130

131131
auto rxDataLength = ggWave->takeRxData(rxData);
132132
if (rxDataLength == -1) {
133133
// failed to decode message
134134
return -1;
135135
} else if (rxDataLength > 0) {
136-
std::copy(rxData.begin(), rxData.end(), outputBuffer);
136+
memcpy(outputBuffer, rxData.data(), rxDataLength);
137+
}
138+
139+
return rxDataLength;
140+
}
141+
142+
extern "C"
143+
int ggwave_ndecode(
144+
ggwave_Instance instance,
145+
const char * dataBuffer,
146+
int dataSize,
147+
char * outputBuffer,
148+
int outputSize) {
149+
// TODO : avoid duplicated code
150+
GGWave * ggWave = (GGWave *) g_instances[instance];
151+
152+
GGWave::CBWaveformInp cbWaveformInp = [&](void * data, uint32_t nMaxBytes) -> uint32_t {
153+
uint32_t nCopied = std::min((uint32_t) dataSize, nMaxBytes);
154+
std::copy(dataBuffer, dataBuffer + nCopied, (char *) data);
155+
156+
dataSize -= nCopied;
157+
dataBuffer += nCopied;
158+
159+
return nCopied;
160+
};
161+
162+
ggWave->decode(cbWaveformInp);
163+
164+
// TODO : avoid allocation
165+
GGWave::TxRxData rxData;
166+
167+
auto rxDataLength = ggWave->takeRxData(rxData);
168+
if (rxDataLength == -1) {
169+
// failed to decode message
170+
return -1;
171+
} else if (rxDataLength > outputSize) {
172+
// the outputBuffer is not big enough to store the data
173+
return -2;
174+
} else if (rxDataLength > 0) {
175+
memcpy(outputBuffer, rxData.data(), rxDataLength);
137176
}
138177

139178
return rxDataLength;

tests/test-ggwave.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,27 @@ int main() {
2525

2626
int ret;
2727
const char * payload = "test";
28-
char decoded[256];
28+
char decoded[16];
2929

3030
int n = ggwave_encode(instance, payload, 4, GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, 50, NULL, 1);
3131
char waveform[n];
3232

33-
ret = ggwave_encode(instance, payload, 4, GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, 50, waveform, 0);
34-
CHECK(ret > 0);
33+
int ne = ggwave_encode(instance, payload, 4, GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, 50, waveform, 0);
34+
CHECK(ne > 0);
3535

36-
ret = ggwave_decode(instance, waveform, sizeof(signed short)*ret, decoded);
36+
// not enough output buffer size to store the decoded message
37+
ret = ggwave_ndecode(instance, waveform, sizeof(signed short)*ne, decoded, 3);
38+
CHECK(ret == -2); // fail
39+
40+
// just enough size to store it
41+
ret = ggwave_ndecode(instance, waveform, sizeof(signed short)*ne, decoded, 4);
42+
CHECK(ret == 4); // success
43+
44+
// unsafe method - will write the decoded output to the output buffer regardless of the size
45+
ret = ggwave_decode(instance, waveform, sizeof(signed short)*ne, decoded);
3746
CHECK(ret == 4);
3847

48+
decoded[ret] = 0; // null-terminate the received data
3949
CHECK(strcmp(decoded, payload) == 0);
4050

4151
ggwave_free(instance);

0 commit comments

Comments
 (0)