NVMeContext: Rework sensor removal concurrent to polling
Concurrent removal of a sensor's configuration while the sensor list is
being iterated for polling can lead to undefined behaviour via access
through a deleted iterator:
Program terminated with signal SIGSEGV, Segmentation fault.
#0 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count (__r=..., this=<optimised out>, this=<optimised out>, __r=...)
at /usr/include/c++/11.2.0/bits/stl_list.h:224
224 /usr/include/c++/11.2.0/bits/stl_list.h: No such file or directory.
[Current thread is 1 (LWP 6649)]
#0 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count (__r=..., this=<optimised out>, this=<optimised out>, __r=...)
at /usr/include/c++/11.2.0/bits/stl_list.h:224
#1 std::__shared_ptr<NVMeSensor, (__gnu_cxx::_Lock_policy)2>::__shared_ptr (this=<optimised out>, this=<optimised out>)
at /usr/include/c++/11.2.0/bits/shared_ptr_base.h:1152
#2 std::shared_ptr<NVMeSensor>::shared_ptr (this=<optimised out>, this=<optimised out>) at /usr/include/c++/11.2.0/bits/shared_ptr.h:150
#3 NVMeBasicContext::readAndProcessNVMeSensor (this=0x1ac8a90, iter=non-dereferenceable iterator for std::list) at ../git/src/NVMeBasicContext.cpp:299
#4 0x004dd8b8 in NVMeBasicContext::readAndProcessNVMeSensor (this=0x1ac8a90, iter=non-dereferenceable iterator for std::list) at ../git/src/NVMeBasicContext.cpp:312
#5 0x004dd8b8 in NVMeBasicContext::readAndProcessNVMeSensor (this=0x1ac8a90, iter=std::shared_ptr<NVMeSensor> (use count 26, weak count 28153871) = {get() = 0x1ad8db0})
at ../git/src/NVMeBasicContext.cpp:312
Rework polling and sensor removal to uphold the requirement that the
iterator remains valid.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: I69b005fe3dad7ddf21d1762731f9cdfd2408cae1
diff --git a/include/NVMeContext.hpp b/include/NVMeContext.hpp
index 2489815..230ca74 100644
--- a/include/NVMeContext.hpp
+++ b/include/NVMeContext.hpp
@@ -12,7 +12,7 @@
{
public:
NVMeContext(boost::asio::io_service& io, int rootBus) :
- scanTimer(io), rootBus(rootBus)
+ scanTimer(io), rootBus(rootBus), pollCursor(sensors.end())
{
if (rootBus < 0)
{
@@ -45,9 +45,44 @@
return std::nullopt;
}
+ // Post-condition: The sensor list does not contain the provided sensor
+ // Post-condition: pollCursor is a valid iterator for the sensor list
void removeSensor(const std::shared_ptr<NVMeSensor>& sensor)
{
- sensors.remove(sensor);
+ // Locate the sensor that we're removing in the sensor list
+ auto found = std::find(sensors.begin(), sensors.end(), sensor);
+
+ // If we failed to find the sensor in the list the post-condition is
+ // already satisfied
+ if (found == sensors.end())
+ {
+ return;
+ }
+
+ // We've found the sensor in the list
+
+ // If we're not actively polling the sensor list, then remove the sensor
+ if (pollCursor == sensors.end())
+ {
+ sensors.erase(found);
+ return;
+ }
+
+ // We're actively polling the sensor list
+
+ // If we're not polling the specific sensor that has been removed, then
+ // remove the sensor
+ if (*pollCursor != *found)
+ {
+ sensors.erase(found);
+ return;
+ }
+
+ // We're polling the sensor that is being removed
+
+ // Remove the sensor and update the poll cursor so the cursor remains
+ // valid
+ pollCursor = sensors.erase(found);
}
virtual void close()
@@ -57,16 +92,16 @@
virtual void pollNVMeDevices() = 0;
- virtual void readAndProcessNVMeSensor(
- std::list<std::shared_ptr<NVMeSensor>>::iterator iter) = 0;
+ virtual void readAndProcessNVMeSensor() = 0;
virtual void processResponse(std::shared_ptr<NVMeSensor>& sensor, void* msg,
size_t len) = 0;
protected:
boost::asio::deadline_timer scanTimer;
- int rootBus; // Root bus for this drive
- std::list<std::shared_ptr<NVMeSensor>> sensors; // used as a poll queue
+ int rootBus; // Root bus for this drive
+ std::list<std::shared_ptr<NVMeSensor>> sensors;
+ std::list<std::shared_ptr<NVMeSensor>>::iterator pollCursor;
};
using NVMEMap = boost::container::flat_map<int, std::shared_ptr<NVMeContext>>;