Type: bug
Component: src/inet/common/packet/tag
Version: INET 4.7.0 (dfe270b21f, 2026-07-07)
The bug
SharingRegionTagSet::removeTagsWherePresent<T>() filters its return value by T, but not its removal:
template<typename T>
inline std::vector<SharingRegionTagSet::RegionTag<T>> SharingRegionTagSet::removeTagsWherePresent(b offset, b length)
{
auto result = getAllTags<T>(offset, length);
clearTags(offset, length); // <-- not templated
return result;
}
clearTags() (SharingRegionTagSet.cc) truncates, splits or erases every region intersecting [offset, offset+length) with no type check anywhere in the loop. So the call removes all region tags over the range, returns only those of type T, and the caller never learns the rest are gone.
It reaches users through Chunk::removeTagsWherePresent<T>() (Chunk.h:810) and Packet::removeRegionTagsWherePresent<T>() (Packet.h:1469), which both forward straight to it.
The single-tag operations next to it are type-correct - removeTag<T>() and removeTagIfPresent<T>() go through getTagIndex(typeid(T), ...) - so the inconsistency looks accidental.
Reproduction
SharingRegionTagSet tags;
tags.addTag<CreationTimeTag>(b(0), b(1000))->setCreationTime(42);
tags.addTag<PropagationTimeTag>(b(0), b(1000)); // any second type
auto removed = tags.removeTagsWherePresent<CreationTimeTag>(b(0), b(1000));
ASSERT(removed.size() == 1); // passes
ASSERT(tags.getNumTags() == 1); // FAILS: 0 -- the PropagationTimeTag is gone too
Affected call sites
src/inet/common/ResidenceTimeMeasurer.cc:55 and :75
packet->removeRegionTagsWherePresent<ResidenceTimeTag>(offset, length);
with offset = b(0), length = packet->getDataLength(), so it clears the packet's whole region tag set over the data range. PacketEventTag (FlowMeasurementStarter.cc:90) and FlowTag (FlowTag.cc:17) live in that same set, so running residence-time measurement alongside flow measurement destroys the flow tags at every measurementEnd and every packetDropped.
src/inet/applications/tcpapp/TcpEchoApp.cc:124
content->removeTagsWherePresent<CreationTimeTag>(b(0), content->getChunkLength());
Any other region tag on the echoed content goes with it.
Why no test catches it
tests/packet/UnitTest.cc:1793 ("11. removeTagsWherePresent") only ever puts CreationTimeTag in the set, so the type-blindness is invisible to it. The reproduction above is that test with a second tag type added.
Suggested fix
Add a type-aware clearTags() overload and call it from removeTagsWherePresent<T>(). The four intersection cases are already written; they need the typeInfo != typeid(*tagObject) -> continue guard that getTagIndex() already uses:
void SharingRegionTagSet::clearTags(const std::type_info& typeInfo, b offset, b length);
template<typename T>
inline std::vector<SharingRegionTagSet::RegionTag<T>> SharingRegionTagSet::removeTagsWherePresent(b offset, b length)
{
auto result = getAllTags<T>(offset, length);
clearTags(typeid(T), offset, length);
return result;
}
The untyped clearTags(offset, length) stays as it is - it is type-blind by design, backing the public clearTags() / clearRegionTags().
This is behaviour-changing for anyone relying on the wipe, so it probably wants a WHATSNEW line.
Workaround until then: iterate getAllTags<T>(offset, length) and call removeTag<T>(region.getOffset(), region.getLength()) per region.
Type: bug
Component:
src/inet/common/packet/tagVersion: INET 4.7.0 (
dfe270b21f, 2026-07-07)The bug
SharingRegionTagSet::removeTagsWherePresent<T>()filters its return value byT, but not its removal:clearTags()(SharingRegionTagSet.cc) truncates, splits or erases every region intersecting[offset, offset+length)with no type check anywhere in the loop. So the call removes all region tags over the range, returns only those of typeT, and the caller never learns the rest are gone.It reaches users through
Chunk::removeTagsWherePresent<T>()(Chunk.h:810) andPacket::removeRegionTagsWherePresent<T>()(Packet.h:1469), which both forward straight to it.The single-tag operations next to it are type-correct -
removeTag<T>()andremoveTagIfPresent<T>()go throughgetTagIndex(typeid(T), ...)- so the inconsistency looks accidental.Reproduction
Affected call sites
src/inet/common/ResidenceTimeMeasurer.cc:55and:75with
offset = b(0),length = packet->getDataLength(), so it clears the packet's whole region tag set over the data range.PacketEventTag(FlowMeasurementStarter.cc:90) andFlowTag(FlowTag.cc:17) live in that same set, so running residence-time measurement alongside flow measurement destroys the flow tags at everymeasurementEndand everypacketDropped.src/inet/applications/tcpapp/TcpEchoApp.cc:124Any other region tag on the echoed content goes with it.
Why no test catches it
tests/packet/UnitTest.cc:1793("11. removeTagsWherePresent") only ever putsCreationTimeTagin the set, so the type-blindness is invisible to it. The reproduction above is that test with a second tag type added.Suggested fix
Add a type-aware
clearTags()overload and call it fromremoveTagsWherePresent<T>(). The four intersection cases are already written; they need thetypeInfo != typeid(*tagObject) -> continueguard thatgetTagIndex()already uses:The untyped
clearTags(offset, length)stays as it is - it is type-blind by design, backing the publicclearTags()/clearRegionTags().This is behaviour-changing for anyone relying on the wipe, so it probably wants a WHATSNEW line.
Workaround until then: iterate
getAllTags<T>(offset, length)and callremoveTag<T>(region.getOffset(), region.getLength())per region.