Skip to content

Commit 604f0cd

Browse files
torokati44avarga
authored andcommitted
RLC: a D2D UM TX entity must withdraw from the mode controller before it dies
D2DModeController::perPeerTxEntities_ holds raw pointers to RLC UM TX entity *modules*, filed by peer when the entity learns its flow. Nothing ever removed them, while BearerManagement::deleteLocalRlcQueues() deletes those modules on every handover and on bearer teardown, so the registry accumulated dangling pointers. The next registration for the same peer walks the set and calls the virtual isEmptyingBuffer() on freed memory: Invalid read of size 8 at D2DModeController::isEmptyingTxBuffer() D2DModeController.cc:41 by D2DModeController::registerD2DPeerTxEntity() D2DModeController.cc:18 by RlcUmTxEntityD2D<LteRlcUmTxEntity>::setFlowControlInfo() by BearerManagement::installRlcTxSide() BearerManagement.cc:417 Address is 344 bytes inside a block of size 464 free'd at LteRlcUmTxEntityD2D::~LteRlcUmTxEntityD2D() by BearerManagement::deleteLocalRlcQueues() BearerManagement.cc:688 by HandoverController::deleteOldBuffers() by HandoverController::doHandover() Once the freed block is recycled the vptr slot reads back as 0 and the call jumps to address 0. That is the SIGSEGV that killed lte/cars VoIP-D2D at t~19.1s -- with no usable stack, since nothing valid is left on it. The registration moved into the entity itself when the D2D code was split out (it used to be done by BearerManagement for every UM entity), so the withdrawal belongs there too, rather than putting a D2DModeController reference back into the core RLC/RRC code. The entity records the peer it was filed under, so it withdraws under exactly that key, and re-setting the flow re-files it. The ModuleRefByPar is already null when the controller itself is gone, which covers the whole-NIC teardown case. Only reachable when entities are deleted while the simulation runs -- a handover or a bearer teardown -- so the static scenarios never build up a stale pointer: full fingerprint suite 182/182 PASS, CSVs unchanged. lte/cars VoIP-D2D now runs to completion (100s) where it died in a SIGSEGV at t~19.1s, both on this branch and on plain inet/dev. The registry has never had a withdrawal path (the pre-split code did not either), so this is a long- standing defect whose visible outcome depended on allocator luck.
1 parent 59b8305 commit 604f0cd

3 files changed

Lines changed: 43 additions & 2 deletions

File tree

src/simu5g/stack/d2d/rlc/RlcUmEntityD2D.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ class RlcUmTxEntityD2D : public Base, public ID2dRlcUmTxEntity
5656
// D2D mode-switch controller of this NIC (absent on non-D2D stacks)
5757
inet::ModuleRefByPar<D2DModeController> d2dModeController_;
5858

59+
// peer this entity is currently registered under at the controller, so that it can
60+
// withdraw itself under exactly the key it was filed under (see ~RlcUmTxEntityD2D)
61+
MacNodeId registeredPeerId_ = NODEID_NONE;
62+
5963
// if true, the entity watches for its TX buffer becoming empty
6064
bool notifyEmptyBuffer_ = false;
6165

@@ -79,8 +83,26 @@ class RlcUmTxEntityD2D : public Base, public ID2dRlcUmTxEntity
7983
// D2D peer tracking: register with the mode controller now that the peer id
8084
// is known, so mode switches can coordinate holding/draining of the buffers
8185
// (this used to be done by BearerManagement for every UM entity).
82-
if (d2dModeController_)
83-
d2dModeController_->registerD2DPeerTxEntity(MacNodeId(this->flowControlInfo_->getD2dRxPeerId()), this);
86+
if (d2dModeController_) {
87+
unregisterFromD2DModeController(); // no-op unless the flow is being re-set
88+
registeredPeerId_ = MacNodeId(this->flowControlInfo_->getD2dRxPeerId());
89+
d2dModeController_->registerD2DPeerTxEntity(registeredPeerId_, this);
90+
}
91+
}
92+
93+
/**
94+
* Withdraw from the controller's per-peer registry. That registry holds raw pointers to
95+
* these entity modules, and BearerManagement::deleteLocalRlcQueues() deletes them on
96+
* handover and on bearer teardown, so an entity that goes away without withdrawing
97+
* leaves a dangling pointer behind -- which the next registration for the same peer then
98+
* calls isEmptyingBuffer() on. The ModuleRefByPar is null once the controller itself is
99+
* gone, which is the case when the whole NIC is being torn down.
100+
*/
101+
void unregisterFromD2DModeController()
102+
{
103+
if (d2dModeController_ && registeredPeerId_ != NODEID_NONE)
104+
d2dModeController_->unregisterD2DPeerTxEntity(registeredPeerId_, this);
105+
registeredPeerId_ = NODEID_NONE;
84106
}
85107

86108
// park the SDU while the old-mode entity is still draining
@@ -113,6 +135,8 @@ class RlcUmTxEntityD2D : public Base, public ID2dRlcUmTxEntity
113135
virtual void resetTxNumbering() = 0;
114136

115137
public:
138+
~RlcUmTxEntityD2D() override { unregisterFromD2DModeController(); }
139+
116140
void startHoldingDownstreamInPackets() override { holdingDownstreamInPackets_ = true; }
117141

118142
bool isHoldingDownstreamInPackets() override { return holdingDownstreamInPackets_; }

src/simu5g/stack/d2d/rrc/D2DModeController.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ void D2DModeController::registerD2DPeerTxEntity(MacNodeId peerId, ID2dRlcUmTxEnt
1919
umTxEnt->startHoldingDownstreamInPackets();
2020
}
2121

22+
void D2DModeController::unregisterD2DPeerTxEntity(MacNodeId peerId, ID2dRlcUmTxEntity *umTxEnt)
23+
{
24+
auto it = perPeerTxEntities_.find(peerId);
25+
if (it == perPeerTxEntities_.end())
26+
return;
27+
28+
it->second.erase(umTxEnt);
29+
if (it->second.empty())
30+
perPeerTxEntities_.erase(it);
31+
}
32+
2233
void D2DModeController::resumeDownstreamInPackets(MacNodeId peerId)
2334
{
2435
if (peerId == NODEID_NONE || (perPeerTxEntities_.find(peerId) == perPeerTxEntities_.end()))

src/simu5g/stack/d2d/rrc/D2DModeController.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ class D2DModeController : public cSimpleModule
2323

2424
public:
2525
virtual void registerD2DPeerTxEntity(MacNodeId peerId, ID2dRlcUmTxEntity *umTxEnt);
26+
/**
27+
* Counterpart of registerD2DPeerTxEntity(). This map holds raw pointers to entity
28+
* *modules*, which BearerManagement::deleteLocalRlcQueues() deletes on handover and on
29+
* bearer teardown, so every registered entity has to withdraw itself before it goes.
30+
*/
31+
virtual void unregisterD2DPeerTxEntity(MacNodeId peerId, ID2dRlcUmTxEntity *umTxEnt);
2632
virtual void resumeDownstreamInPackets(MacNodeId peerId);
2733
virtual bool isEmptyingTxBuffer(MacNodeId peerId);
2834
};

0 commit comments

Comments
 (0)