Skip to content

Commit 3f811e8

Browse files
stats: Fix crash with dangling pointers.
1 parent 0dc96e0 commit 3f811e8

File tree

10 files changed

+106
-67
lines changed

10 files changed

+106
-67
lines changed

src/stats/stats_printer.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
#include "vma/util/utils.h"
1212
#include "vma/util/vma_stats.h"
1313
#include "vma/lwip/tcp.h"
14-
#include "vma/vma_extra.h"
15-
#include "vma/util/sys_vars.h"
14+
#include "vma/util/vtypes.h"
1615

1716
typedef enum {
1817
e_K = 1024,

src/stats/stats_publisher.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ void stats_data_reader::handle_timer_expired(void *ctx)
102102

103103
void stats_data_reader::register_to_timer()
104104
{
105-
m_timer_handler = g_p_event_handler_manager->register_timer_event(STATS_PUBLISHER_TIMER_PERIOD, g_p_stats_data_reader, PERIODIC_TIMER, 0);
105+
m_timer_handler = g_p_event_handler_manager->register_timer_event(STATS_PUBLISHER_TIMER_PERIOD, this, PERIODIC_TIMER, 0);
106106
}
107107

108108
void stats_data_reader::add_data_reader(void* local_addr, void* shm_addr, int size)
@@ -150,6 +150,8 @@ void vma_shmem_stats_open(vlog_levels_t** p_p_vma_log_level, uint8_t** p_p_vma_l
150150
}
151151
BULLSEYE_EXCLUDE_BLOCK_END
152152

153+
vlog_printf(VLOG_DEBUG,"%s:%d: Allocated g_p_stats_data_reader pointer as '%p'\n", __func__, __LINE__, g_p_stats_data_reader);
154+
153155
shmem_size = SHMEM_STATS_SIZE(safe_mce_sys().stats_fd_num_max);
154156
buf = malloc(shmem_size);
155157
if (buf == NULL)
@@ -277,8 +279,12 @@ void vma_shmem_stats_close()
277279
g_sh_mem = NULL;
278280
g_p_vlogger_level = NULL;
279281
g_p_vlogger_details = NULL;
280-
delete g_p_stats_data_reader;
281-
g_p_stats_data_reader = NULL;
282+
if (g_p_stats_data_reader != NULL) {
283+
stats_data_reader* p = g_p_stats_data_reader;
284+
g_p_stats_data_reader = NULL;
285+
p->set_destroying_state(true);
286+
delete p;
287+
}
282288
}
283289

284290
void vma_stats_instance_create_socket_block(socket_stats_t* local_stats_addr)

src/stats/stats_reader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
#include <fcntl.h>
1616
#include <errno.h>
1717
#include <list>
18+
#include <netinet/in.h>
1819
#include <signal.h>
1920
#include <getopt.h> /* getopt()*/
20-
#include <errno.h>
2121
#include <dirent.h>
2222
#include <string.h>
2323
#include <vector>

src/vma/dev/time_converter_ptp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#define TIME_CONVERTER_PTP_H
1010

1111
#include <infiniband/verbs.h>
12+
#include "vma/ib/base/verbs_extra.h"
1213
#include "vma/event/timer_handler.h"
1314
#include <vma/util/sys_vars.h>
1415
#include "time_converter.h"

src/vma/event/delta_timer.cpp

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ timer::~timer()
5454

5555
void timer::add_new_timer(unsigned int timeout_msec, timer_node_t* node, timer_handler* handler, void* user_data, timer_req_type_t req_type)
5656
{
57-
node->handler = handler;
57+
node->handler.store(handler);
5858
node->req_type = req_type;
5959
node->user_data = user_data;
6060
node->orig_time_msec = milliseconds(timeout_msec);
@@ -94,22 +94,22 @@ void timer::remove_timer(timer_node_t* node, timer_handler *handler)
9494
if (!node) {
9595
node = m_list_head;
9696
while (node) {
97-
if (node->handler == handler) // node found
97+
if (node->handler.load() == handler) // node found
9898
break;
9999
node = node->next;
100100
}
101101
}
102102

103103
// Here we MUST have a valid node pointer
104104
BULLSEYE_EXCLUDE_BLOCK_START
105-
if (IS_NODE_INVALID(node) || (node->handler != handler)) {
105+
if (IS_NODE_INVALID(node) || (node->handler.load() != handler)) {
106106
tmr_logfunc("bad <node,handler> combo for removale (%p,%p)", node, handler);
107107
return;
108108
}
109109
BULLSEYE_EXCLUDE_BLOCK_END
110110

111111
// Invalidate node before freeing it
112-
node->handler = NULL;
112+
node->handler.store(nullptr);
113113
node->req_type = INVALID_TIMER;
114114

115115
// Remove & Free node
@@ -125,18 +125,18 @@ void timer::remove_all_timers(timer_handler *handler)
125125

126126
// Look for handler in the list if node wasen't indicated
127127
while (node) {
128-
if (node->handler == handler) {// node found
128+
if (node->handler.load() == handler) {// node found
129129
node_tmp = node;
130130
node = node->next;
131131
// Here we MUST have a valid node pointer
132132
BULLSEYE_EXCLUDE_BLOCK_START
133-
if (IS_NODE_INVALID(node_tmp) || (node_tmp->handler != handler)) {
133+
if (IS_NODE_INVALID(node_tmp) || (node_tmp->handler.load() != handler)) {
134134
tmr_logfunc("bad <node,handler> combo for removale (%p,%p)", node_tmp, handler);
135135
continue;
136136
}
137137
BULLSEYE_EXCLUDE_BLOCK_END
138138
// Invalidate node before freeing it
139-
node_tmp->handler = NULL;
139+
node_tmp->handler.store(nullptr);
140140
node_tmp->req_type = INVALID_TIMER;
141141
remove_from_list(node_tmp);
142142
// Remove & Free node
@@ -200,19 +200,11 @@ void timer::process_registered_timers()
200200
timer_node_t* iter = m_list_head;
201201
timer_node_t* next_iter;
202202
while (iter && (iter->delta_time_msec == milliseconds(0))) {
203-
tmr_logfuncall("timer expired on %p", iter->handler);
204-
205-
/* Special check is need to protect
206-
* from using destroyed object pointed by handler
207-
* See unregister_timer_event()
208-
* Object can be destoyed from another thread (lock protection)
209-
* and from current thread (lock and lock count condition)
210-
*/
211-
if (iter->handler &&
212-
!iter->lock_timer.trylock() &&
213-
(1 == iter->lock_timer.is_locked_by_me())) {
214-
iter->handler->handle_timer_expired(iter->user_data);
215-
iter->lock_timer.unlock();
203+
timer_handler * handler = iter->handler.load();
204+
tmr_logfuncall("timer expired on %p", handler);
205+
206+
if (handler) {
207+
handler->safe_handle_timer_expired(iter->user_data);
216208
}
217209
next_iter = iter->next;
218210

@@ -225,13 +217,13 @@ void timer::process_registered_timers()
225217
break;
226218

227219
case ONE_SHOT_TIMER:
228-
remove_timer(iter, iter->handler);
220+
remove_timer(iter, handler);
229221
break;
230222

231223
BULLSEYE_EXCLUDE_BLOCK_START
232224
case INVALID_TIMER:
233225
default:
234-
tmr_logwarn("invalid timer expired on %p", iter->handler);
226+
tmr_logwarn("invalid timer expired on %p", handler);
235227
break;
236228
}
237229
BULLSEYE_EXCLUDE_BLOCK_END

src/vma/event/delta_timer.h

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#ifndef DELTA_TIMER_H
99
#define DELTA_TIMER_H
1010

11+
#include <atomic>
1112
#include <chrono>
1213
#include "utils/lock_wrapper.h"
1314

@@ -32,18 +33,13 @@ struct timer_node_t {
3233
std::chrono::milliseconds delta_time_msec;
3334
/* the orig timer requested (saved in order to re-register periodic timers) */
3435
std::chrono::milliseconds orig_time_msec;
35-
/* control thread-safe access to handler. Recursive because unregister_timer_event()
36-
* can be called from handle_timer_expired()
37-
* that is under trylock() inside process_registered_timers
38-
*/
39-
lock_spin_recursive lock_timer;
4036
/* link to the context registered */
41-
timer_handler* handler;
42-
void* user_data;
43-
timers_group* group;
44-
timer_req_type_t req_type;
45-
struct timer_node_t* next;
46-
struct timer_node_t* prev;
37+
std::atomic<timer_handler*> handler;
38+
void* user_data;
39+
timers_group* group;
40+
timer_req_type_t req_type;
41+
struct timer_node_t* next;
42+
struct timer_node_t* prev;
4743
}; // used by the list
4844

4945
class timer

src/vma/event/event_handler_manager.cpp

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,6 @@ void* event_handler_manager::register_timer_event(int timeout_msec, timer_handle
7474
}
7575
BULLSEYE_EXCLUDE_BLOCK_END
7676

77-
timer_node_t* timer_node = (timer_node_t*)node;
78-
timer_node->lock_timer=lock_spin_recursive("timer");
79-
8077
reg_action_t reg_action;
8178
memset(&reg_action, 0, sizeof(reg_action));
8279
reg_action.type = REGISTER_TIMER;
@@ -116,30 +113,19 @@ void event_handler_manager::unregister_timer_event(timer_handler* handler, void*
116113
reg_action.type = UNREGISTER_TIMER;
117114
reg_action.info.timer.handler = handler;
118115
reg_action.info.timer.node = node;
119-
120-
/* Special protection is needed to avoid scenario when deregistration is done
121-
* during timer_handler object destruction, timer node itself is not removed
122-
* and time for this timer node is expired. In this case there is no guarantee
123-
* to operate with timer_handler object.
124-
* See timer::process_registered_timers()
125-
* Do just lock() to protect timer_handler inside process_registered_timers()
126-
*/
127-
if (node) {
128-
timer_node_t* timer_node = (timer_node_t*)node;
129-
timer_node->lock_timer.lock();
130-
}
131-
132116
post_new_reg_action(reg_action);
133117
}
134118

135119
void event_handler_manager::unregister_timers_event_and_delete(timer_handler* handler)
136120
{
137121
evh_logdbg("timer handler '%p'", handler);
138-
reg_action_t reg_action;
139-
memset(&reg_action, 0, sizeof(reg_action));
140-
reg_action.type = UNREGISTER_TIMERS_AND_DELETE;
141-
reg_action.info.timer.handler = handler;
142-
post_new_reg_action(reg_action);
122+
if( handler != nullptr && !handler->set_destroying_state()) {
123+
reg_action_t reg_action;
124+
memset(&reg_action, 0, sizeof(reg_action));
125+
reg_action.type = UNREGISTER_TIMERS_AND_DELETE;
126+
reg_action.info.timer.handler = handler;
127+
post_new_reg_action(reg_action);
128+
}
143129
}
144130

145131
void event_handler_manager::register_ibverbs_event(int fd, event_handler_ibverbs *handler,

src/vma/event/timer_handler.h

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,71 @@
88
#ifndef TIMER_HANDLER_H
99
#define TIMER_HANDLER_H
1010

11+
#include <atomic>
12+
13+
#include "vlogger/vlogger.h"
14+
#include "utils/lock_wrapper.h"
15+
1116
/**
1217
* simple timer notification.
1318
* Any class that inherit timer_handler should also inherit cleanable_obj, and use clean_obj instead of delete.
1419
* It must implement the clean_obj method to delete the object from the internal thread.
1520
*/
1621
class timer_handler
1722
{
18-
public:
19-
virtual ~timer_handler() {};
23+
private:
24+
lock_spin m_handle_mutex{"timer_handler"};
25+
std::atomic<bool> m_destroy_in_progress{false};
26+
protected:
2027
virtual void handle_timer_expired(void* user_data) = 0;
28+
29+
public:
30+
timer_handler() = default;
31+
32+
virtual ~timer_handler() {
33+
if( !m_destroy_in_progress.load()) {
34+
m_destroy_in_progress.store(true);
35+
vlog_printf(VLOG_DEBUG, "Destroying timer_handler without destroy in progress.\n");
36+
}
37+
{
38+
m_handle_mutex.lock();
39+
m_handle_mutex.unlock();
40+
}
41+
}
42+
43+
void safe_handle_timer_expired(void* user_data) {
44+
if (m_handle_mutex.trylock() == 0) {
45+
if(!m_destroy_in_progress.load())
46+
{
47+
handle_timer_expired(user_data);
48+
}
49+
m_handle_mutex.unlock();
50+
}
51+
}
52+
53+
/**
54+
* Sets the destroying state of the object to indicate that destruction is in progress.
55+
*
56+
* If `wait_for_handler` is set to true, this method will ensure that the mutex
57+
* used for handling operations (m_handle_mutex) is acquired and released, effectively
58+
* waiting for any pending handler operation to complete.
59+
*
60+
* @param wait_for_handler If true, waits for the handler mutex to ensure
61+
* handler finish before proceeding.
62+
* Defaults to false.
63+
*
64+
* @return The previous state of the destruction flag.
65+
* Returns true if a destruction process was already in progress before
66+
* this method was called, otherwise false.
67+
*/
68+
bool set_destroying_state(bool wait_for_handler = false) {
69+
bool result = m_destroy_in_progress.exchange(true);
70+
if( wait_for_handler ) {
71+
m_handle_mutex.lock();
72+
m_handle_mutex.unlock();
73+
}
74+
return result;
75+
}
2176
};
2277

2378
#endif

src/vma/sock/fd_collection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ void fd_collection::handle_timer_expired(void* user_data)
635635
if (si_tcp) {
636636
//In case of TCP socket progress the TCP connection
637637
fdcoll_logfunc("Call to handler timer of TCP socket:%d", (*itr)->get_fd());
638-
si_tcp->handle_timer_expired(NULL);
638+
si_tcp->safe_handle_timer_expired(NULL);
639639
}
640640
itr++;
641641
}

src/vma/sock/sockinfo_tcp.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4798,8 +4798,12 @@ void tcp_timers_collection::handle_timer_expired(void* user_data)
47984798
NOT_IN_USE(user_data);
47994799
timer_node_t* iter = m_p_intervals[m_n_location];
48004800
while (iter) {
4801-
__log_funcall("timer expired on %p", iter->handler);
4802-
iter->handler->handle_timer_expired(iter->user_data);
4801+
timer_handler * handler = iter->handler.load();
4802+
__log_funcall("timer expired on %p", handler);
4803+
if ( handler )
4804+
{
4805+
handler->safe_handle_timer_expired(iter->user_data);
4806+
}
48034807
iter = iter->next;
48044808
}
48054809
m_n_location = (m_n_location + 1) % m_n_intervals_size;
@@ -4810,7 +4814,7 @@ void tcp_timers_collection::handle_timer_expired(void* user_data)
48104814

48114815
void tcp_timers_collection::add_new_timer(timer_node_t* node, timer_handler* handler, void* user_data)
48124816
{
4813-
node->handler = handler;
4817+
node->handler.store(handler);
48144818
node->user_data = user_data;
48154819
node->group = this;
48164820
node->next = NULL;
@@ -4859,7 +4863,7 @@ void tcp_timers_collection::remove_timer(timer_node_t* node)
48594863
}
48604864
}
48614865

4862-
__log_dbg("TCP timer handler [%p] was removed", node->handler);
4866+
__log_dbg("TCP timer handler [%p] was removed", node->handler.load());
48634867

48644868
free(node);
48654869
}

0 commit comments

Comments
 (0)