From 6c08355d2d22859e9b7667c280139dcd182bfebf Mon Sep 17 00:00:00 2001 From: Sang Woo Kim Date: Sat, 18 Jul 2026 23:17:35 +0900 Subject: [PATCH 1/2] NDPluginProcess: guard the two divide-by-zero paths in processCallbacks Both divisors in processCallbacks come from user-writable PVs with no enforcement, so an operator or autosave restore can drive them to zero: 1. NumFilter has no DRVL. With NumFilter == 0 the auto-reset sets numFiltered = 0 (:210), the ramp `if (numFiltered < numFilter)` is `0 < 0` == false so it stays 0, and O1/O2/F1/F2 divide by this->numFiltered == 0 on every frame -- every output element and the persistent filter buffer become inf/NaN. Floor numFilter at 1 (a filter over zero samples is meaningless), matching the numFiltered ramp so the divisor is always >= 1. 2. AutoOffsetScale divides by (maxValue - minValue). A uniform frame (dark, saturated, or closed shutter -- routine at start-up) leaves maxValue == minValue, so scale = maxScale/0 = +inf, which is then latched into the Scale PV with EnableOffsetScale forced on -- every later frame is multiplied by inf until an operator intervenes. Only compute and latch the scale/offset when maxValue > minValue. --- ADApp/pluginSrc/NDPluginProcess.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ADApp/pluginSrc/NDPluginProcess.cpp b/ADApp/pluginSrc/NDPluginProcess.cpp index 4e900cfbf..6c2031e90 100644 --- a/ADApp/pluginSrc/NDPluginProcess.cpp +++ b/ADApp/pluginSrc/NDPluginProcess.cpp @@ -92,6 +92,7 @@ void NDPluginProcess::processCallbacks(NDArray *pArray) } if (enableFilter) { getIntegerParam(NDPluginProcessNumFilter, &numFilter); + if (numFilter < 1) numFilter = 1; getDoubleParam (NDPluginProcessOOffset, &oOffset); getDoubleParam (NDPluginProcessOScale, &oScale); getDoubleParam (NDPluginProcessOC1, &oc1); @@ -235,7 +236,7 @@ void NDPluginProcess::processCallbacks(NDArray *pArray) this->pNDArrayPool->convert(pScratch, &pArrayOut, (NDDataType_t)dataType); } - if (autoOffsetScale && (NULL != pArrayOut)) { + if (autoOffsetScale && (NULL != pArrayOut) && (maxValue > minValue)) { pArrayOut->getInfo(&arrayInfo); double maxScale = pow(2., arrayInfo.bytesPerElement*8) - 1; scale = maxScale /(maxValue-minValue); From 94e847784c2f67dcbcec7bfbe97bf8f3faa3a8b5 Mon Sep 17 00:00:00 2001 From: Sang Woo Kim Date: Sat, 18 Jul 2026 23:17:35 +0900 Subject: [PATCH 2/2] NDPluginStats: guard the histogram scale against equal HIST_MIN/HIST_MAX doComputeHistogramT computes scale = (histSize - 1) / (histMax - histMin) with no check that histMax > histMin. HIST_MIN and HIST_MAX are user-writable PVs; setting them equal (a natural mistake for a narrow window, or via an autosave restore) makes the denominator 0, so scale is +/-inf and, for a pixel equal to histMin, (value - histMin) * scale is 0 * inf = NaN, then (int)NaN is undefined behaviour. The sibling computeHistX already clamps its divisor (`if (histSize < 1) histSize = 1`); apply the same inline guard here so the degenerate case collapses to a defined 1-unit window instead of dividing by zero. --- ADApp/pluginSrc/NDPluginStats.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ADApp/pluginSrc/NDPluginStats.cpp b/ADApp/pluginSrc/NDPluginStats.cpp index ce04be707..7cd0d8359 100644 --- a/ADApp/pluginSrc/NDPluginStats.cpp +++ b/ADApp/pluginSrc/NDPluginStats.cpp @@ -39,6 +39,7 @@ asynStatus NDPluginStats::doComputeHistogramT(NDArray *pArray, NDStats_t *pStats pArray->getInfo(&arrayInfo); nElements = arrayInfo.nElements; + if (pStats->histMax <= pStats->histMin) pStats->histMax = pStats->histMin + 1; scale = (pStats->histSize - 1) / (pStats->histMax - pStats->histMin); pStats->histBelow = 0;