Fix memory-safety and correctness bugs surfaced by Coverity audit (part 7) (#22280)
* ml: lock host stats reset in detection pass Coverity CID 410121 (MISSING_LOCK): ml_host_detect_once() reset host->mls before taking host->mutex, while ml_host_stop() and the public readers access the same struct under that mutex. Move the reset into the existing critical section so host stats updates follow one lock discipline. * ml: narrow host mutex scope in detection stats Coverity CID 393056 (SLEEP): stop holding host->mutex across chart dictionary traversal in ml_host_detect_once(). Aggregate the host stats locally and publish them under the mutex so long chart-deletion waits on the rrdset index do not block ML readers and stop paths for the full walk. * ml: drop host mutex before stop chart walk Coverity CID 393057 (SLEEP): ml_host_stop() held host->mutex while waiting for the host chart dictionary read lock. Reset the host stats under the mutex and release it before walking the chart and dimension dictionaries so lengthy chart-deletion writers do not block ML readers behind the mutex. * ml: reject undersized kmeans training output Coverity CID 451560 (UNINIT): stop publishing a model when preprocessing yields fewer than two training vectors, because k-means returns with no cluster centers in that case. Also value-initialize inlined cluster centers so empty-source conversions stay deterministic instead of carrying indeterminate dlib matrix bytes. * Fix memory-safety issues in ml_kmeans_inlined_t initialization * Address review comments * Address review comments (2) * Address review comments (3) * Address review comments (4) * Address review comments (5) * ml: validate max_training_vectors and add stop generation counter Clamp max_training_vectors to [2, 86400] so a misconfigured value cannot force the undersampled early-return every cycle. Add ml_stop_generation on ml_host_t, bumped by ml_host_stop and sampled by ml_host_detect_once before and after the unlocked chart walk. A stop+start that completes within the walk window now invalidates the snapshot via the generation change even though ml_running is back to true at the re-check. * Adjust comment * ml: match master's post-cycle state in finalize helper Make ts = TRAINED unconditional in ml_dimension_finalize_constant_state to match ml_dimension_update_models. The earlier conditional was a no-op for stats anyway — ml_chart_update_dimension short-circuits on mt = CONSTANT before reading ts. Simplify the test accordingly. * ml: tighten stop generation ordering and review fixups Move ml_stop_generation.fetch_add to the end of ml_host_stop, after all chart->mls and dim resets, so a concurrent ml_host_detect_once that observes the new generation is guaranteed to also see the resets via seq_cst. The earlier ordering let detect sample the post-bump value mid-stop and miss the race. Also: take dim->slock in test_dimension_finalize_constant_state to match the helper's contract, and document the 86400 ceiling on max_training_vectors. * Fix compilation after rebase * ml: move ml_stop_generation initialization to ml_host_create * ml: simplify context_anomaly_rate iteration by removing unnecessary structured bindings * ml: add start_stop_mutex to synchronize ml_host_start and ml_host_stop operations Introduce `start_stop_mutex` to serialize `ml_host_start()` and `ml_host_stop()` operations, ensuring proper state handling during concurrent starts/stops. Update relevant mutex initialization and destruction, and adjust critical sections to prevent race conditions and ensure data consistency. --------- Co-authored-by: Costa Tsaousis <costa@netdata.cloud>