From abc95982ed5d750b727e1ace7b944e7964b8ab9f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 3 Jul 2026 18:54:35 -0700 Subject: [PATCH] bound deserialize() recursion --- src/factory/FieldCreateFactory.cpp | 22 +++++++++++++++++++++- src/factory/PVStructure.cpp | 3 +++ src/factory/PVStructureArray.cpp | 3 +++ src/factory/PVUnion.cpp | 3 +++ src/factory/PVUnionArray.cpp | 3 +++ src/factory/factoryPvt.h | 14 ++++++++++++++ testApp/misc/testSerialization.cpp | 24 +++++++++++++++++++++++- 7 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/factory/factoryPvt.h diff --git a/src/factory/FieldCreateFactory.cpp b/src/factory/FieldCreateFactory.cpp index b584ad9..c4ed717 100644 --- a/src/factory/FieldCreateFactory.cpp +++ b/src/factory/FieldCreateFactory.cpp @@ -28,6 +28,8 @@ #include #include +#include "factoryPvt.h" + using std::tr1::static_pointer_cast; using std::size_t; using std::string; @@ -36,7 +38,6 @@ namespace epics { namespace pvData { size_t Field::num_instances; - struct Field::Helper { static unsigned hash(Field *fld) { std::ostringstream key; @@ -1476,6 +1477,7 @@ static int decodeScalar(int8 code) FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl* control) const { + RecurseGuard RG; control->ensureData(1); int8 code = buffer->getByte(); if (code == -1) @@ -1604,6 +1606,7 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl namespace detail { struct field_factory { FieldCreatePtr fieldCreate; + epicsThreadPrivate deserDepth; field_factory() :fieldCreate(new FieldCreate()) { registerRefCounter("Field", &Field::num_instances); registerRefCounter("Thread", &Thread::num_instances); @@ -1614,6 +1617,23 @@ struct field_factory { static detail::field_factory* field_factory_s; static epicsThreadOnceId field_factory_once = EPICS_THREAD_ONCE_INIT; +#if defined(__rtems__) || defined(vxWorks) +// lower limit on embedded targets +const unsigned maxDepth = 10; +#else +const unsigned maxDepth = 20; +#endif +RecurseGuard::RecurseGuard() { + size_t current = (size_t)field_factory_s->deserDepth.get(); + if(current>=maxDepth) + throw std::logic_error("FieldCreateFactory recursion depth exceeded"); + field_factory_s->deserDepth.set((unsigned*)(current+1)); +} +RecurseGuard::~RecurseGuard() { + size_t current = (size_t)field_factory_s->deserDepth.get(); + field_factory_s->deserDepth.set((unsigned*)(current-1)); +} + static void field_factory_init(void*) { try { diff --git a/src/factory/PVStructure.cpp b/src/factory/PVStructure.cpp index ff14b81..0a8d91c 100644 --- a/src/factory/PVStructure.cpp +++ b/src/factory/PVStructure.cpp @@ -18,6 +18,8 @@ #include #include +#include "factoryPvt.h" + using std::tr1::static_pointer_cast; using std::size_t; using std::string; @@ -224,6 +226,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer, void PVStructure::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol) { + RecurseGuard G; size_t fieldsSize = pvFields.size(); for(size_t i = 0; ideserialize(pbuffer, pcontrol); diff --git a/src/factory/PVStructureArray.cpp b/src/factory/PVStructureArray.cpp index 66577b5..45fdf18 100644 --- a/src/factory/PVStructureArray.cpp +++ b/src/factory/PVStructureArray.cpp @@ -16,6 +16,8 @@ #include #include +#include "factoryPvt.h" + using std::tr1::static_pointer_cast; using std::size_t; @@ -158,6 +160,7 @@ void PVStructureArray::serialize(ByteBuffer *pbuffer, void PVStructureArray::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol) { + RecurseGuard G; svector data(reuse()); size_t size = this->getArray()->getArraySizeType() == Array::fixed ? diff --git a/src/factory/PVUnion.cpp b/src/factory/PVUnion.cpp index a52687b..8d917d4 100644 --- a/src/factory/PVUnion.cpp +++ b/src/factory/PVUnion.cpp @@ -18,6 +18,8 @@ #include #include +#include "factoryPvt.h" + using std::tr1::static_pointer_cast; using std::size_t; using std::string; @@ -143,6 +145,7 @@ void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) cons void PVUnion::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol) { + RecurseGuard G; if (variant) { FieldConstPtr field = pcontrol->cachedDeserialize(pbuffer); diff --git a/src/factory/PVUnionArray.cpp b/src/factory/PVUnionArray.cpp index 151bc5b..1b4240b 100644 --- a/src/factory/PVUnionArray.cpp +++ b/src/factory/PVUnionArray.cpp @@ -16,6 +16,8 @@ #include #include +#include "factoryPvt.h" + using std::tr1::static_pointer_cast; using std::size_t; @@ -157,6 +159,7 @@ void PVUnionArray::serialize(ByteBuffer *pbuffer, void PVUnionArray::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol) { + RecurseGuard G; svector data(reuse()); size_t size = this->getArray()->getArraySizeType() == Array::fixed ? diff --git a/src/factory/factoryPvt.h b/src/factory/factoryPvt.h new file mode 100644 index 0000000..baa08de --- /dev/null +++ b/src/factory/factoryPvt.h @@ -0,0 +1,14 @@ +#ifndef FACTORYPVT_H +#define FACTORYPVT_H + +namespace epics { namespace pvData { + +// use to bound recursion in FieldCreate and PVDataCreator singletons +struct RecurseGuard { + RecurseGuard(); + ~RecurseGuard(); +}; + +}} // namespace epics::pvData + +#endif // FACTORYPVT_H diff --git a/testApp/misc/testSerialization.cpp b/testApp/misc/testSerialization.cpp index 617586a..97bf244 100644 --- a/testApp/misc/testSerialization.cpp +++ b/testApp/misc/testSerialization.cpp @@ -874,13 +874,35 @@ void testMalformed() // a union array where ... CASE("\x89\x83\x2b\x01\x01"); #undef CASE + { + std::vector inp(500u); + for(size_t i=0; i inp(500u); + for(size_t i=0; i