util: fix SegmentFetcher undefined behavior caused by uncanceled pending interest

Change-Id: I4ca80ad00fc52024f35908b21d7c42bffc8a5396
Refs: #4770
diff --git a/ndn-cxx/util/segment-fetcher.cpp b/ndn-cxx/util/segment-fetcher.cpp
index 01419c1..71799dd 100644
--- a/ndn-cxx/util/segment-fetcher.cpp
+++ b/ndn-cxx/util/segment-fetcher.cpp
@@ -229,8 +229,6 @@
   if (shouldStop(weakSelf))
     return;
 
-  afterSegmentReceived(data);
-
   BOOST_ASSERT(m_nSegmentsInFlight > 0);
   m_nSegmentsInFlight--;
 
@@ -250,6 +248,12 @@
     pendingSegmentIt = m_pendingSegments.begin();
   }
 
+  if (pendingSegmentIt == m_pendingSegments.end()) {
+    return;
+  }
+
+  afterSegmentReceived(data);
+
   // Cancel timeout event
   m_scheduler.cancelEvent(pendingSegmentIt->second.timeoutEvent);
   pendingSegmentIt->second.timeoutEvent = nullptr;
@@ -479,6 +483,7 @@
   BOOST_ASSERT(pendingSegmentIt != m_pendingSegments.end());
   BOOST_ASSERT(pendingSegmentIt->second.state == SegmentState::InRetxQueue);
   pendingSegmentIt->second.state = SegmentState::Retransmitted;
+  m_face.removePendingInterest(pendingSegmentIt->second.id);
   pendingSegmentIt->second.id = pendingInterest;
   pendingSegmentIt->second.timeoutEvent = timeoutEvent;
 }
diff --git a/tests/unit/util/segment-fetcher.t.cpp b/tests/unit/util/segment-fetcher.t.cpp
index 22aa0b7..f870030 100644
--- a/tests/unit/util/segment-fetcher.t.cpp
+++ b/tests/unit/util/segment-fetcher.t.cpp
@@ -855,6 +855,28 @@
   BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 2);
 }
 
+BOOST_AUTO_TEST_CASE(UncanceledPendingInterestBug) // Bug #4770
+{
+  DummyValidator acceptValidator;
+  auto fetcher = SegmentFetcher::start(face, Interest("/hello/world"), acceptValidator);
+  connectSignals(fetcher);
+
+  // Fetcher will send the first interest immediately
+  // and the second interest after 1 second by default
+  advanceClocks(1100_ms);
+
+  // Face will give data to the fetcher twice if the first interest is not canceled.
+  // isFinal=false to keep fetcher alive so that it can receive the second data.
+  face.receive(*makeDataSegment("/hello/world/version0", 0, false));
+
+  advanceClocks(1100_ms);
+
+  face.receive(*makeDataSegment("/hello/world/version0", 1, true));
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 1);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestSegmentFetcher
 BOOST_AUTO_TEST_SUITE_END() // Util