Possible out-of-bounds read in PeripheralWrapper::Unsubscribe
I found a possible OOB read (CWE-125) in PeripheralWrapper::Unsubscribe. The function copies a
fixed number of bytes (SIMPLEBLE_UUID_STR_LEN, == 37) out of the caller-supplied service and
characteristic strings with memcpy, regardless of the actual string length. When JavaScript passes
a UUID string shorter than 37 bytes (e.g. a 4-character 16-bit UUID like "180d"), serviceStr.c_str()
points to a heap buffer only strlen+1 bytes long, so the memcpy reads up to ~32 bytes past the end
of the std::string's storage into the destination simpleble_uuid_t. The over-read bytes are then
handed to the C library as a UUID.
File: lib/peripheral.cpp
Function: PeripheralWrapper::Unsubscribe
const std::string serviceStr = info[1].As<Napi::String>();
const std::string charStr = info[2].As<Napi::String>();
simpleble_uuid_t service;
simpleble_uuid_t characteristic;
memcpy(service.value, serviceStr.c_str(), SIMPLEBLE_UUID_STR_LEN); // reads 37 bytes from serviceStr
memcpy(characteristic.value, charStr.c_str(), SIMPLEBLE_UUID_STR_LEN); // reads 37 bytes from charStr
simpleble_uuid_t.value is char[SIMPLEBLE_UUID_STR_LEN] with SIMPLEBLE_UUID_STR_LEN == 37
(SimpleBLE types_c.h).
serviceStr / charStr are arbitrary JS strings; only their type is checked, never their length.
memcpy(..., ..., SIMPLEBLE_UUID_STR_LEN) unconditionally reads 37 source bytes. If the source
string is shorter than 37 bytes, this reads past the end of the std::string heap buffer — an
out-of-bounds read that can crash (ASAN/hardened allocators) or splice adjacent heap contents into
the UUID passed to simpleble_peripheral_unsubscribe.
- The rest of this wrapper deliberately avoids this: the sibling C wrapper (
lib/wrapper.cpp) uses
bounded strncpy(..., SIMPLEBLE_UUID_STR_LEN) / strnlen(...), confirming Unsubscribe's raw
memcpy with a fixed length is the outlier bug.
JS trigger:
const ble = require('node-simpleble'); // obtain a connected peripheral handle first
// short (16-bit) UUIDs -> c_str() buffers are ~5 bytes, memcpy reads 37
addon.simpleble_peripheral_unsubscribe(handle, '180d', '2a37');
Suggested fix: copy only the bytes that exist and NUL-terminate, e.g.
snprintf(service.value, SIMPLEBLE_UUID_STR_LEN, "%s", serviceStr.c_str()); or
strncpy(service.value, serviceStr.c_str(), SIMPLEBLE_UUID_STR_LEN - 1) with an explicit terminator,
mirroring the bounded copies already used in lib/wrapper.cpp.
Possible out-of-bounds read in
PeripheralWrapper::UnsubscribeI found a possible OOB read (CWE-125) in
PeripheralWrapper::Unsubscribe. The function copies afixed number of bytes (
SIMPLEBLE_UUID_STR_LEN, == 37) out of the caller-supplied service andcharacteristic strings with
memcpy, regardless of the actual string length. When JavaScript passesa UUID string shorter than 37 bytes (e.g. a 4-character 16-bit UUID like
"180d"),serviceStr.c_str()points to a heap buffer only
strlen+1bytes long, so thememcpyreads up to ~32 bytes past the endof the
std::string's storage into the destinationsimpleble_uuid_t. The over-read bytes are thenhanded to the C library as a UUID.
File:
lib/peripheral.cppFunction:
PeripheralWrapper::Unsubscribesimpleble_uuid_t.valueischar[SIMPLEBLE_UUID_STR_LEN]withSIMPLEBLE_UUID_STR_LEN == 37(SimpleBLE
types_c.h).serviceStr/charStrare arbitrary JS strings; only their type is checked, never their length.memcpy(..., ..., SIMPLEBLE_UUID_STR_LEN)unconditionally reads 37 source bytes. If the sourcestring is shorter than 37 bytes, this reads past the end of the
std::stringheap buffer — anout-of-bounds read that can crash (ASAN/hardened allocators) or splice adjacent heap contents into
the UUID passed to
simpleble_peripheral_unsubscribe.lib/wrapper.cpp) usesbounded
strncpy(..., SIMPLEBLE_UUID_STR_LEN)/strnlen(...), confirmingUnsubscribe's rawmemcpywith a fixed length is the outlier bug.JS trigger:
Suggested fix: copy only the bytes that exist and NUL-terminate, e.g.
snprintf(service.value, SIMPLEBLE_UUID_STR_LEN, "%s", serviceStr.c_str());orstrncpy(service.value, serviceStr.c_str(), SIMPLEBLE_UUID_STR_LEN - 1)with an explicit terminator,mirroring the bounded copies already used in
lib/wrapper.cpp.