Skip to content

Add protection against some network issues - #226

Open
anjohnson wants to merge 9 commits into
epics-base:masterfrom
anjohnson:add-protection
Open

Add protection against some network issues#226
anjohnson wants to merge 9 commits into
epics-base:masterfrom
anjohnson:add-protection

Conversation

@anjohnson

@anjohnson anjohnson commented Jul 10, 2026

Copy link
Copy Markdown
Member

Moved from the private area.

By commit:

  1. Reject negative payload sizes in messages over UDP and TCP, with tests.
    It looks like this module can't handle TCP or UDP messages over 2^31 Bytes, although the TCP code apparently understands joining segmented messages.

  2. Ignore over-long channel names in search messages.
    This commit skips over names longer than would be accepted by the channel create code. A better response might be to immediately discard the search message, research needed on how to implement that.

  3. Uncomment Lock guards that were improperly disabled long ago.
    See the gist here (2nd document) for Claude's assessment of the lock guard history.

  4. Prevent deadlocks in destroy() methods.
    See the gist here (1st document) for Claude's explanation of fixes like one by @mdavidsaver in 2018.

  5. Limit strings accepted as channel names to MAX_CHANNEL_NAME_LENGTH, ignoring anything larger. This requires a change implemented in my pvData add-protection branch, Add protection from some network issues pvDataCPP#107

@codacy-production

codacy-production Bot commented Jul 10, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 8 complexity · 0 duplication

Metric Results
Complexity 8
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@anjohnson
anjohnson marked this pull request as draft July 10, 2026 04:43
@AppVeyorBot

Copy link
Copy Markdown

Control messages treat this int32 as an opaque value, but application
messages use it as a length, so must reject it.
Applies to both UDP and TCP streams.

Test added to testCodec.cpp
They're already validated in the channel-creation path.
I had AI analyse these commented-out guards and produce a report,
which said they should all be uncommented. Happy to discuss them
or provide a copy on request.
@anjohnson

Copy link
Copy Markdown
Member Author

Oops, that last commit needs a change from my pvDataCPP add-protection branch.

@mdavidsaver

Copy link
Copy Markdown
Member
  1. Reject negative payload sizes in messages over UDP and TCP, with tests.
    It looks like this module can't handle TCP or UDP messages over 2^31 Bytes, although the TCP code apparently understands joining segmented messages.

No problem with UDP.

I have some hesitation about TCP. Have you instead tried to switch _payloadSize to uint32?

The semantics around segmented messages has never been clear to me. Currently PVXS can receive segmented messages, but will never send them. It does handle message length as unsigned 32-bit.

@kasemir Does core.pva do anything with the segmentation flags?

@AppVeyorBot

Copy link
Copy Markdown

@kasemir

kasemir commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

@kasemir Does core.pva do anything with the segmentation flags?

There is code to detect a segmented message and assemble the pieces, but I don't think I've ever been able to check that against a PVA server which generates such messages.

https://github.com/ControlSystemStudio/phoebus/blob/51a9bf6e4f882b21830f94531ba795a42f915c58/core/pva/src/main/java/org/epics/pva/common/TCPHandler.java#L426

@anjohnson

Copy link
Copy Markdown
Member Author

Have you instead tried to switch _payloadSize to uint32?

Why? Doing that would require much more complicated changes to the code and tests.

The PVA protocol Wiki explicitly says about sizes:

  1. If the number of elements is less than 2^31-1, then the size MUST be encoded as an unsigned 8-bit integer with value 254, followed by a positive signed 32-bit integer indicating the number of elements.
  2. Values greater than or equal to 2^31-1 are currently not implemented.

It does go on to suggest how larger sizes would be encoded if they were supported though. Have you implemented them in PVXS without updating the spec? Does the new Java implementation also support that?

@anjohnson
anjohnson marked this pull request as ready for review July 29, 2026 23:11
@AppVeyorBot

Copy link
Copy Markdown

@AppVeyorBot

Copy link
Copy Markdown

@anjohnson anjohnson left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core comments with detail fixes.

Comment thread src/client/client.cpp
{
if(impl) {
strm<<typeid(*impl->channel.get()).name()<<" : ";
pva::Channel& channel = *impl->channel;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to call get() to get the type-id of the contained object. Ditto below.

Move to a non-hardening PR.

POINTER_DEFINITIONS(PipelineControl);

virtual ~PipelineControl() {};
virtual ~PipelineControl();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not hardening.


// command ID and paylaod
int8 command = receiveBuffer->getByte();
// TODO check this cast (size_t must be 32-bit)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove TODO comment.

Comment on lines 366 to +375
size_t payloadSize = receiveBuffer->getInt();

// control message check (skip message)
if (flags & 0x01)
continue;

// reject negative payload size (sign-extends to a huge size_t)
if (static_cast<int32>(payloadSize) < 0)
return false;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core: Add an int32 payload size var above which is assigned from the buffer, then exclude negatives and assign to the size_t version (with no renames or casts below).

Comment thread src/remote/codec.cpp
_command = _socketBuffer.getByte();

// read payload size
_payloadSize = _socketBuffer.getInt();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the –ve checks for payload size with the magicCode check on 193 below, not later on.

Comment on lines +1190 to +1192

if (channelGet)
channelGet->destroy();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAD checked and is good with this.

Comment on lines -1749 to 1755
//Lock guard(_mutex);
Lock guard(_mutex);
_pvPutBitSet->serialize(buffer, control);
_pvPutStructure->serialize(buffer, control, _pvPutBitSet.get());
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What data is being guarded here?

Comment on lines +2373 to 2375
Lock guard(_mutex);
ScopedLock lock(channelArray);
_pvArray->serialize(buffer, control, 0, _pvArray->getLength());

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Locking order here is different than above QOS stuff, is this correct?

Comment on lines +106 to +108
Channel& channel = *_channel;
fprintf(fd,"CLASS : %s\n", typeid(*this).name());
fprintf(fd,"CHANNEL : %s\n", typeid(*_channel).name());
fprintf(fd,"CHANNEL : %s\n", typeid(channel).name());

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack, but not hardening.

Comment thread src/remote/codec.cpp
Comment on lines +245 to +251
// reject negative payload size (sign-extends to a huge size_t)
if (_payloadSize < 0)
{
invalidDataStreamHandler();
throw invalid_data_stream_exception("negative payload size");
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this change, and the next one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants