Skip to content

Commit b5d7a7f

Browse files
committed
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 <[email protected]> Change-Id: I69b005fe3dad7ddf21d1762731f9cdfd2408cae1
1 parent 0b1ae9f commit b5d7a7f

File tree

3 files changed

+63
-30
lines changed

3 files changed

+63
-30
lines changed

include/NVMeBasicContext.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ class NVMeBasicContext : public NVMeContext
1111
NVMeBasicContext(boost::asio::io_service& io, int rootBus);
1212
~NVMeBasicContext() override = default;
1313
void pollNVMeDevices() override;
14-
void readAndProcessNVMeSensor(
15-
std::list<std::shared_ptr<NVMeSensor>>::iterator iter) override;
14+
void readAndProcessNVMeSensor() override;
1615
void processResponse(std::shared_ptr<NVMeSensor>& sensor, void* msg,
1716
size_t len) override;
1817

include/NVMeContext.hpp

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class NVMeContext : public std::enable_shared_from_this<NVMeContext>
1212
{
1313
public:
1414
NVMeContext(boost::asio::io_service& io, int rootBus) :
15-
scanTimer(io), rootBus(rootBus)
15+
scanTimer(io), rootBus(rootBus), pollCursor(sensors.end())
1616
{
1717
if (rootBus < 0)
1818
{
@@ -45,9 +45,44 @@ class NVMeContext : public std::enable_shared_from_this<NVMeContext>
4545
return std::nullopt;
4646
}
4747

48+
// Post-condition: The sensor list does not contain the provided sensor
49+
// Post-condition: pollCursor is a valid iterator for the sensor list
4850
void removeSensor(const std::shared_ptr<NVMeSensor>& sensor)
4951
{
50-
sensors.remove(sensor);
52+
// Locate the sensor that we're removing in the sensor list
53+
auto found = std::find(sensors.begin(), sensors.end(), sensor);
54+
55+
// If we failed to find the sensor in the list the post-condition is
56+
// already satisfied
57+
if (found == sensors.end())
58+
{
59+
return;
60+
}
61+
62+
// We've found the sensor in the list
63+
64+
// If we're not actively polling the sensor list, then remove the sensor
65+
if (pollCursor == sensors.end())
66+
{
67+
sensors.erase(found);
68+
return;
69+
}
70+
71+
// We're actively polling the sensor list
72+
73+
// If we're not polling the specific sensor that has been removed, then
74+
// remove the sensor
75+
if (*pollCursor != *found)
76+
{
77+
sensors.erase(found);
78+
return;
79+
}
80+
81+
// We're polling the sensor that is being removed
82+
83+
// Remove the sensor and update the poll cursor so the cursor remains
84+
// valid
85+
pollCursor = sensors.erase(found);
5186
}
5287

5388
virtual void close()
@@ -57,16 +92,16 @@ class NVMeContext : public std::enable_shared_from_this<NVMeContext>
5792

5893
virtual void pollNVMeDevices() = 0;
5994

60-
virtual void readAndProcessNVMeSensor(
61-
std::list<std::shared_ptr<NVMeSensor>>::iterator iter) = 0;
95+
virtual void readAndProcessNVMeSensor() = 0;
6296

6397
virtual void processResponse(std::shared_ptr<NVMeSensor>& sensor, void* msg,
6498
size_t len) = 0;
6599

66100
protected:
67101
boost::asio::deadline_timer scanTimer;
68-
int rootBus; // Root bus for this drive
69-
std::list<std::shared_ptr<NVMeSensor>> sensors; // used as a poll queue
102+
int rootBus; // Root bus for this drive
103+
std::list<std::shared_ptr<NVMeSensor>> sensors;
104+
std::list<std::shared_ptr<NVMeSensor>>::iterator pollCursor;
70105
};
71106

72107
using NVMEMap = boost::container::flat_map<int, std::shared_ptr<NVMeContext>>;

src/NVMeBasicContext.cpp

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -246,29 +246,28 @@ NVMeBasicContext::NVMeBasicContext(boost::asio::io_service& io, int rootBus) :
246246
thread.detach();
247247
}
248248

249-
void NVMeBasicContext::readAndProcessNVMeSensor(
250-
std::list<std::shared_ptr<NVMeSensor>>::iterator iter)
249+
void NVMeBasicContext::readAndProcessNVMeSensor()
251250
{
252-
if (iter == sensors.end())
251+
if (pollCursor == sensors.end())
253252
{
254253
this->pollNVMeDevices();
255254
return;
256255
}
257256

258-
std::shared_ptr<NVMeSensor> sensor = *iter++;
257+
std::shared_ptr<NVMeSensor> sensor = *pollCursor++;
259258

260259
if (!sensor->readingStateGood())
261260
{
262261
sensor->markAvailable(false);
263262
sensor->updateValue(std::numeric_limits<double>::quiet_NaN());
264-
readAndProcessNVMeSensor(iter);
263+
readAndProcessNVMeSensor();
265264
return;
266265
}
267266

268267
/* Potentially defer sampling the sensor sensor if it is in error */
269268
if (!sensor->sample())
270269
{
271-
readAndProcessNVMeSensor(iter);
270+
readAndProcessNVMeSensor();
272271
return;
273272
}
274273

@@ -326,7 +325,7 @@ void NVMeBasicContext::readAndProcessNVMeSensor(
326325
response->prepare(len);
327326
return len;
328327
},
329-
[self{shared_from_this()}, iter, sensor, response](
328+
[self{shared_from_this()}, sensor, response](
330329
const boost::system::error_code& ec, std::size_t length) mutable {
331330
if (ec)
332331
{
@@ -350,30 +349,30 @@ void NVMeBasicContext::readAndProcessNVMeSensor(
350349
self->processResponse(sensor, data.data(), data.size());
351350

352351
/* Enqueue processing of the next sensor */
353-
self->readAndProcessNVMeSensor(iter);
352+
self->readAndProcessNVMeSensor();
354353
});
355354
}
356355

357356
void NVMeBasicContext::pollNVMeDevices()
358357
{
359-
auto scan = sensors.begin();
358+
pollCursor = sensors.begin();
360359

361360
scanTimer.expires_from_now(boost::posix_time::seconds(1));
362-
scanTimer.async_wait([self{shared_from_this()},
363-
scan](const boost::system::error_code errorCode) {
364-
if (errorCode == boost::asio::error::operation_aborted)
365-
{
366-
return;
367-
}
361+
scanTimer.async_wait(
362+
[self{shared_from_this()}](const boost::system::error_code errorCode) {
363+
if (errorCode == boost::asio::error::operation_aborted)
364+
{
365+
return;
366+
}
368367

369-
if (errorCode)
370-
{
371-
std::cerr << errorCode.message() << "\n";
372-
return;
373-
}
368+
if (errorCode)
369+
{
370+
std::cerr << errorCode.message() << "\n";
371+
return;
372+
}
374373

375-
self->readAndProcessNVMeSensor(scan);
376-
});
374+
self->readAndProcessNVMeSensor();
375+
});
377376
}
378377

379378
static double getTemperatureReading(int8_t reading)

0 commit comments

Comments
 (0)