@cryptotaxi247 / netdata-1 / commits / a56ad2c5a

Fix crash in plugin function callback (#21713)

* Handle dictionary destruction in plugins: clean up and notify caller on error * One more check

Stelios Fragkakis committed Feb 11, 2026 at 23:58 UTC a56ad2c5a3606e494476dceef09dde6d7a07f1bc
2 files changed +29
src/database/rrdfunctions-inflight.c
+13
@@ -531,6 +531,19 @@ int rrd_function_run(RRDHOST *host, BUFFER *result_wb, int timeout_s,
531 uuid_copy(t.transaction_uuid, uuid);
532
533 struct rrd_function_inflight *r = dictionary_set(rrd_functions_inflight_requests, transaction, &t, sizeof(t));
534 + if(!r) {
535 + // dictionary_set() returns NULL when the dictionary is destroyed (shutdown in progress)
536 + code = rrd_call_function_error(result_wb, "Service is shutting down.", HTTP_RESP_SERVICE_UNAVAILABLE);
537 +
538 + rrd_functions_inflight_cleanup(&t);
539 + dictionary_acquired_item_release(host->functions, t.host_function_acquired);
540 +
541 + if(result_cb)
542 + result_cb(result_wb, code, result_cb_data);
543 +
544 + return code;
545 + }
546 +
547 if(r->used) {
548 nd_log(NDLS_DAEMON, NDLP_NOTICE,
549 "FUNCTIONS: duplicate transaction '%s', function: '%s'",
src/plugins.d/pluginsd_functions.c
+16
@@ -270,6 +270,22 @@ int pluginsd_function_execute_cb(struct rrd_function_execute *rfe, void *data) {
270 // if there is any error, our dictionary callbacks will call the caller callback to notify
271 // the caller about the error - no need for error handling here.
272 struct inflight_function *t = dictionary_set(parser->inflight.functions, transaction_str, &tmp, sizeof(struct inflight_function));
273 + if(!t) {
274 + // dictionary_set() returns NULL when the dictionary is destroyed
275 + // (e.g., the plugin has exited). Clean up and notify the caller.
276 + dictionary_write_unlock(parser->inflight.functions);
277 +
278 + int code = HTTP_RESP_SERVICE_UNAVAILABLE;
279 + rrd_call_function_error(rfe->result.wb, "The plugin is not available.", code);
280 + rfe->result.cb(rfe->result.wb, code, rfe->result.data);
281 +
282 + string_freez(tmp.function);
283 + buffer_free(tmp.payload);
284 + freez((void *)tmp.source);
285 +
286 + return code;
287 + }
288 +
289 if(!t->sent_successfully) {
290 int code = t->code;
291 dictionary_write_unlock(parser->inflight.functions);