Add a max string len param to deserializeString()

Defaults to INT32_MAX if not provided, but several places in
pvAccess can set limits, e.g. MAX_CHANNEL_NAME_LENGTH, and
this is the best way to harden them against huge values.

Document that SerializeHelper::readSize() returns -1 for NULL.
This commit is contained in:
Andrew Johnson authored and Michael Davidsaver committed 2026-08-09 18:26:52 -07:00
1 parent 5012ff95b2
commit 2c5854a7ec
2 files changed
+50 -46

No files matched your search

+17 -7
View File
@@ -50,7 +50,7 @@ namespace epics {
*
* @param[in] buffer deserialization buffer.
* @param[in] control the DeserializableControl.
* @returns array size.
* @returns array size, or (size_t) -1.
*/
static std::size_t readSize(ByteBuffer* buffer,
DeserializableControl* control);
@@ -80,20 +80,22 @@ namespace epics {
/**
* std::string deserialization helper method.
* TODO This method cannot return "null", but Java implementation
* could have serialized "null" value as well. We need to decide
* how to deserialize "null".
*
* @param[in] buffer deserialization buffer
* @param[in] control control
* @param[in] max length limit
* @returns deserialized string
*
* @todo This method cannot return "null", but Java implementation
* could have serialized "null" value as well. We need to decide
* how to deserialize "null".
* could have serialized "null" value as well. This implementation
* returns an empty string in that event.
*
* @since UNRELEASED Added the max argument.
* Test for the DESERIALIZE_STRING_HAS_LIMIT macro to detect.
* Retroactive default is 2 GiB
*/
static std::string deserializeString(ByteBuffer* buffer,
DeserializableControl* control);
DeserializableControl* control, std::size_t max = 0x7fffffff);
private:
SerializeHelper() {};
@@ -112,4 +114,12 @@ namespace epics {
}
}
/** Feature test macro for max argument
*
* of SerializeHelper::deserializeString()
*
* @since UNRELEASED added
*/
#define DESERIALIZE_STRING_HAS_LIMIT
#endif /* SERIALIZEHELPER_H_ */
+33 -39
View File
@@ -47,12 +47,13 @@ namespace epics {
DeserializableControl* control) {
control->ensureData(1);
int8 b = buffer->getByte();
if(b==-1)
return -1;
else if(b==-2) {
if (b == -1)
return -1; // NULL
else if (b == -2) {
control->ensureData(sizeof(int32));
int32 s = buffer->getInt();
if(s<0) THROW_BASE_EXCEPTION("negative size");
if (s < 0)
THROW_BASE_EXCEPTION("negative size");
return s;
}
else
@@ -100,44 +101,37 @@ namespace epics {
}
string SerializeHelper::deserializeString(ByteBuffer* buffer,
DeserializableControl* control) {
DeserializableControl* control, std::size_t max) {
std::size_t size = SerializeHelper::readSize(buffer, control);
if(size!=(size_t)-1) // TODO null strings check, to be removed in the future
{
if (buffer->getRemaining()>=size)
{
// entire string is in buffer, simply create a string out of it (copy)
std::size_t pos = buffer->getPosition();
string str(buffer->getBuffer()+pos, size);
buffer->setPosition(pos+size);
return str;
}
else
{
string str;
str.reserve(size);
try {
std::size_t i = 0;
while(true) {
std::size_t toRead = min(size-i, buffer->getRemaining());
std::size_t pos = buffer->getPosition();
str.append(buffer->getBuffer()+pos, toRead);
buffer->setPosition(pos+toRead);
i += toRead;
if(i<size)
control->ensureData(1); // at least one
else
break;
}
return str;
} catch(...) {
throw;
}
}
if (size == (std::size_t) -1)
return std::string(); // NULL from old implementations
if (size > max)
THROW_BASE_EXCEPTION("string too long");
if (buffer->getRemaining() >= size) { // All present
std::size_t pos = buffer->getPosition();
string str(buffer->getBuffer() + pos, size);
buffer->setPosition(pos + size);
return str;
}
else
return std::string();
string str;
str.reserve(size);
std::size_t i = 0;
while (true) {
std::size_t toRead = min(size-i, buffer->getRemaining());
std::size_t pos = buffer->getPosition();
str.append(buffer->getBuffer() + pos, toRead);
buffer->setPosition(pos + toRead);
i += toRead;
if (i < size)
control->ensureData(1); // at least one
else
break;
}
return str;
}
}
}