fix(security): harden RuvSense pipeline against overflow and numerical instability
- tomography.rs: use checked_mul for nx*ny*nz to prevent integer overflow on adversarial grid configurations - phase_align.rs: add defensive bounds check in mean_phase_on_indices to prevent panic on out-of-range subcarrier indices - multistatic.rs: stabilize softmax in attention_weighted_fusion with max-subtraction to prevent exp() overflow on extreme similarity values Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -252,15 +252,23 @@ impl PersonalBaseline {
|
|||||||
|
|
||||||
let mut reports = Vec::new();
|
let mut reports = Vec::new();
|
||||||
|
|
||||||
|
let observation_days = self.observation_days;
|
||||||
|
|
||||||
for &(metric, value) in &summary.metrics {
|
for &(metric, value) in &summary.metrics {
|
||||||
|
// Update stats and extract values before releasing the mutable borrow
|
||||||
|
let (z, baseline_mean, baseline_std) = {
|
||||||
let stats = self.stats_for_mut(metric);
|
let stats = self.stats_for_mut(metric);
|
||||||
stats.update(value);
|
stats.update(value);
|
||||||
|
let z = stats.z_score(value);
|
||||||
|
let mean = stats.mean;
|
||||||
|
let std = stats.std_dev();
|
||||||
|
(z, mean, std)
|
||||||
|
};
|
||||||
|
|
||||||
if !self.is_ready_at(self.observation_days) {
|
if !self.is_ready_at(observation_days) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let z = stats.z_score(value);
|
|
||||||
let idx = Self::metric_index(metric);
|
let idx = Self::metric_index(metric);
|
||||||
|
|
||||||
if z.abs() > 2.0 {
|
if z.abs() > 2.0 {
|
||||||
@@ -288,8 +296,8 @@ impl PersonalBaseline {
|
|||||||
direction,
|
direction,
|
||||||
z_score: z,
|
z_score: z,
|
||||||
current_value: value,
|
current_value: value,
|
||||||
baseline_mean: stats.mean,
|
baseline_mean,
|
||||||
baseline_std: stats.std_dev(),
|
baseline_std,
|
||||||
sustained_days: self.drift_counters[idx],
|
sustained_days: self.drift_counters[idx],
|
||||||
level,
|
level,
|
||||||
timestamp_us,
|
timestamp_us,
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ fn attention_weighted_fusion(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute attention weights based on similarity to consensus
|
// Compute attention weights based on similarity to consensus
|
||||||
let mut weights = vec![0.0_f32; n_nodes];
|
let mut logits = vec![0.0_f32; n_nodes];
|
||||||
for (n, amp) in amplitudes.iter().enumerate() {
|
for (n, amp) in amplitudes.iter().enumerate() {
|
||||||
let mut dot = 0.0_f32;
|
let mut dot = 0.0_f32;
|
||||||
let mut norm_a = 0.0_f32;
|
let mut norm_a = 0.0_f32;
|
||||||
@@ -269,10 +269,15 @@ fn attention_weighted_fusion(
|
|||||||
}
|
}
|
||||||
let denom = (norm_a * norm_b).sqrt().max(1e-12);
|
let denom = (norm_a * norm_b).sqrt().max(1e-12);
|
||||||
let similarity = dot / denom;
|
let similarity = dot / denom;
|
||||||
weights[n] = (similarity / temperature).exp();
|
logits[n] = similarity / temperature;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize weights (softmax-style)
|
// Numerically stable softmax: subtract max to prevent exp() overflow
|
||||||
|
let max_logit = logits.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
|
||||||
|
let mut weights = vec![0.0_f32; n_nodes];
|
||||||
|
for (n, &logit) in logits.iter().enumerate() {
|
||||||
|
weights[n] = (logit - max_logit).exp();
|
||||||
|
}
|
||||||
let weight_sum: f32 = weights.iter().sum::<f32>().max(1e-12);
|
let weight_sum: f32 = weights.iter().sum::<f32>().max(1e-12);
|
||||||
for w in &mut weights {
|
for w in &mut weights {
|
||||||
*w /= weight_sum;
|
*w /= weight_sum;
|
||||||
|
|||||||
@@ -281,8 +281,11 @@ fn mean_phase_on_indices(phase: &[f32], indices: &[usize]) -> f32 {
|
|||||||
let mut sin_sum = 0.0_f32;
|
let mut sin_sum = 0.0_f32;
|
||||||
let mut cos_sum = 0.0_f32;
|
let mut cos_sum = 0.0_f32;
|
||||||
for &i in indices {
|
for &i in indices {
|
||||||
sin_sum += phase[i].sin();
|
// Defensive bounds check: skip out-of-range indices rather than panic
|
||||||
cos_sum += phase[i].cos();
|
if let Some(&p) = phase.get(i) {
|
||||||
|
sin_sum += p.sin();
|
||||||
|
cos_sum += p.cos();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sin_sum.atan2(cos_sum)
|
sin_sum.atan2(cos_sum)
|
||||||
|
|||||||
@@ -199,7 +199,12 @@ impl RfTomographer {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let n_voxels = config.nx * config.ny * config.nz;
|
let n_voxels = config.nx
|
||||||
|
.checked_mul(config.ny)
|
||||||
|
.and_then(|v| v.checked_mul(config.nz))
|
||||||
|
.ok_or_else(|| TomographyError::InvalidGrid(
|
||||||
|
format!("Grid dimensions overflow: {}x{}x{}", config.nx, config.ny, config.nz),
|
||||||
|
))?;
|
||||||
|
|
||||||
// Precompute weight matrix
|
// Precompute weight matrix
|
||||||
let weight_matrix: Vec<Vec<(usize, f64)>> = links
|
let weight_matrix: Vec<Vec<(usize, f64)>> = links
|
||||||
|
|||||||
Reference in New Issue
Block a user