Four defects in how a raw reading becomes the value a threshold is compared against.
A counter reading of exactly 0 is treated as "no previous reading" (thold_functions.php:796)
case 2: // COUNTER
if ($thold_data['oldvalue'] != 0 && is_numeric($thold_data['oldvalue'])) {
... compute the delta ...
} else {
$currentval = 0;
}
oldvalue of 0 is a legitimate previous reading, not an absent one. After a device reboot, or any interval where a counter genuinely sits at 0, the next poll reports a rate of 0. A thold_low on interface utilisation fires spuriously; a thold_high under-reports the first interval of traffic.
Distinguishing "no previous reading" (NULL or '') from "previous reading was 0", and testing only is_numeric(), would fix it. The DERIVE arm at :817 has the same shape.
Counter wrap is off by one, and the 64-bit case loses precision (thold_functions.php:806-810)
if ($thold_data['oldvalue'] > 4294967295) {
$currentval = (18446744073709551615 - $thold_data['oldvalue']) + $item[...];
} else {
$currentval = (4294967295 - $thold_data['oldvalue']) + $item[...];
}
The wrap modulus is 2^32 = 4294967296, not 4294967295, so every 32-bit wrap under-counts by exactly 1. Same for 2^64.
More seriously, 18446744073709551615 is above PHP_INT_MAX (9223372036854775807) and is parsed as a float:
$v = 18446744073709551615; => 1.8446744073709552E+19 (double)
so the 64-bit branch loses roughly 11 bits before the subtraction.
Cacti core's rrd.php and rrdtool itself use the correct moduli.
The percent-of denominator is cast to int (thold_functions.php:1303)
$t = (int) $rrd_reindexed[$thold['local_data_id']][$thold['percent_ds']];
if ($t > 0) {
$currentval = ($currentval / $t) * 100;
} else {
$currentval = 0;
}
A denominator below 1 truncates to 0 and the percentage is forced to 0; a negative denominator gives 0 rather than a negative percentage; a large float denominator is truncated. A percent threshold on a ratio with a sub-unit denominator reports 0% permanently, so a configured thold_low alerts forever.
The daemon stores a timestamp in oldvalue (thold_process.php:210-223)
if (isset($item[$thold_data['name']])) {
$lasttime = $item[$thold_data['name']]; // a value
} else {
$lasttime = $currenttime - $thold_data['rrd_step']; // a timestamp
}
...
SET tcheck = 1, lastread = ?, lasttime = FROM_UNIXTIME(?), oldvalue = ?
[$currentval, $currenttime, $lasttime, ...]
$lasttime is bound to oldvalue. When the data source has no reading this cycle, a Unix timestamp (around 1.7e9) is stored as the previous counter reading. On the next poll the delta is a large negative number, which takes the overflow branch above and fabricates a rate in the billions — a bogus high alert on the poll after any missed sample.
The non-daemon path at includes/polling.php:214-217 correctly falls back to $thold_data['oldvalue'].
Four defects in how a raw reading becomes the value a threshold is compared against.
A counter reading of exactly 0 is treated as "no previous reading" (
thold_functions.php:796)oldvalueof 0 is a legitimate previous reading, not an absent one. After a device reboot, or any interval where a counter genuinely sits at 0, the next poll reports a rate of 0. Athold_lowon interface utilisation fires spuriously; athold_highunder-reports the first interval of traffic.Distinguishing "no previous reading" (NULL or
'') from "previous reading was 0", and testing onlyis_numeric(), would fix it. The DERIVE arm at:817has the same shape.Counter wrap is off by one, and the 64-bit case loses precision (
thold_functions.php:806-810)The wrap modulus is 2^32 = 4294967296, not 4294967295, so every 32-bit wrap under-counts by exactly 1. Same for 2^64.
More seriously, 18446744073709551615 is above
PHP_INT_MAX(9223372036854775807) and is parsed as a float:so the 64-bit branch loses roughly 11 bits before the subtraction.
Cacti core's
rrd.phpand rrdtool itself use the correct moduli.The percent-of denominator is cast to
int(thold_functions.php:1303)A denominator below 1 truncates to 0 and the percentage is forced to 0; a negative denominator gives 0 rather than a negative percentage; a large float denominator is truncated. A percent threshold on a ratio with a sub-unit denominator reports 0% permanently, so a configured
thold_lowalerts forever.The daemon stores a timestamp in
oldvalue(thold_process.php:210-223)$lasttimeis bound tooldvalue. When the data source has no reading this cycle, a Unix timestamp (around 1.7e9) is stored as the previous counter reading. On the next poll the delta is a large negative number, which takes the overflow branch above and fabricates a rate in the billions — a bogus high alert on the poll after any missed sample.The non-daemon path at
includes/polling.php:214-217correctly falls back to$thold_data['oldvalue'].