NeuralEdge Systems · Confidencial

Guía de Seguridad de Memoria

Patrones RAII · Smart Pointers · Ownership · Data Race Prevention — Rust, C++ y C

Nivel: Avanzado
Stack: C++ 17 · Rust 1.78 · GCC 13
Junio 2026
3 crashes en producción en 14 días
⚠️
Contexto del incidente
Se han identificado use-after-free en TensorPool, memory leaks en sesiones >6h y data races intermitentes en ModelRegistry. Esta guía aplica los patrones de seguridad de memoria necesarios para eliminar estos bugs en el motor de inferencia IoT.
1
Taxonomía de Bugs de Memoria Detectados
Tipo de Bug
Descripción
Detectado en
Prevención
🔴 Use-after-free
Acceso a memoria ya liberada por el destructor del pool
TensorPool (C++)
unique_ptr + move semantics
🟠 Memory leak
Buffers no liberados en rutas de error del motor
InferenceEngine (C++)
RAII + destructores garantizados
🟡 Data race
Escritura concurrente sin sincronización al cache
ModelRegistry (C++)
shared_mutex + unique_lock
🔵 Dangling pointer
Raw pointer a tensor liberado por otro thread
Código junior (C++)
Prohibir raw pointers, usar smart ptrs
⚪ Double-free
Riesgo potencial al copiar handles de buffers
Riesgo latente
Delete copy ctor / = delete
2
Espectro de Seguridad por Lenguaje
El stack de NeuralEdge cubre el rango completo — entender el espectro determina qué garantías puede dar el compilador
C Control total
Sin garantías
C++ Smart pointers
RAII manual
Rust Ownership
Seguro en compile-time
Go/Java GC automático
Latencia impredecible
Menos seguro · Más control Más seguro · Menos control
💡 Recomendación para NeuralEdge: mantener C++ con RAII estricto para InferenceEngine, migrar gradualmente TensorPool a Rust. SensorBridge (ya en Rust) es el componente más seguro del stack.
3
Patrones Aplicados — Antes y Después
3.1 · TensorPool — Eliminar use-after-free con unique_ptr
❌ Antes — Raw pointers (crash en producción) C++
// PELIGRO: raw pointer, sin ownership claro
class TensorPool {
public:
  float* allocate(size_t n) {
    return new float[n]; // ¿quién libera?
  }

  void release(float* ptr) {
    delete[] ptr; // ¿ya fue liberado?
  }

  // BUG: si release() ya se llamó,
  // el caller puede seguir usando ptr
  // → USE-AFTER-FREE
};
✅ Después — unique_ptr (ownership explícito) C++
class TensorPool {
public:
  // Ownership TRANSFERIDO al caller
  std::unique_ptr<float[]> allocate(size_t n) {
    return std::make_unique<float[]>(n);
  }

  // No hace falta release():
  // destructor libera automáticamente
  // Use-after-free: imposible
  // Double-free: imposible
  // Leak en ruta de error: imposible
};
3.2 · ModelRegistry — Data race con shared_mutex
❌ Antes — Sin sincronización C++
class ModelRegistry {
  std::map<string, Model*> cache_;

public:
  // BUG: lectura y escritura sin lock
  // → DATA RACE si hay >1 thread
  Model* get(const string& id) {
    return cache_[id]; // race!
  }

  void set(const string& id, Model* m) {
    cache_[id] = m; // race!
  }
};
✅ Después — shared_mutex (lectores concurrentes) C++
class ModelRegistry {
  std::map<string, shared_ptr<Model>> cache_;
  mutable std::shared_mutex mutex_;

public:
  // Múltiples lectores simultáneos OK
  shared_ptr<Model> get(const string& id) const {
    std::shared_lock lock(mutex_);
    auto it = cache_.find(id);
    return it != cache_.end() ? it->second : nullptr;
  }

  void set(const string& id, shared_ptr<Model> m) {
    std::unique_lock lock(mutex_); // escritor exclusivo
    cache_[id] = std::move(m);
  }
};
3.3 · SensorBridge (Rust) — Ownership en adquisición de datos
❌ Patrón a evitar — Referencias colgantes Rust
// COMPILE ERROR: Rust no lo permite
fn get_sensor_ref() -> &SensorData {
  let data = SensorData::new(); // local
  &data // ERROR: dangling reference
  // data se destruye al salir de scope
}

// En C++ esto compila pero es UB
// En Rust el borrow checker lo rechaza
✅ Correcto — Arc para estado compartido Rust
use std::sync::{Arc, RwLock};

pub struct SensorBridge {
  buffer: Arc<RwLock<Vec<SensorFrame>>>,
}

impl SensorBridge {
  pub fn push(&self, frame: SensorFrame) {
    let mut buf = self.buffer.write().unwrap();
    buf.push(frame); // sin race, garantizado
  }

  // Clone barato: incrementa ref-count
  pub fn reader(&self) -> Arc<RwLock<...>> {
    Arc::clone(&self.buffer)
  }
}
3.4 · InferenceEngine — RAII para rollback en error
❌ Antes — Leak en ruta de excepción C++
void runInference(Model* model) {
  float* input  = new float[1024];
  float* output = new float[512];

  // Si esto lanza excepción:
  model->forward(input, output);

  // ← NUNCA llega aquí → LEAK
  delete[] input;
  delete[] output;
}
✅ Después — RAII garantiza limpieza C++
void runInference(Model& model) {
  // Destructor llamado SIEMPRE,
  // incluso con excepciones
  auto input  = std::make_unique<float[]>(1024);
  auto output = std::make_unique<float[]>(512);

  // excepción aquí → destructores
  // liberan input y output
  model.forward(input.get(), output.get());
  // Sin leak posible
}
4
Reglas de Oro para el Equipo
📦
C++ Smart Pointers
Reglas para InferenceEngine y TensorPool
Usa make_unique para ownership único — el buffer tiene un solo dueño
Usa shared_ptr cuando el modelo se comparte entre threads del motor
Siempre = delete copy constructor en handles de recursos
Nunca new / delete raw en código nuevo — prohibido en code review
Nunca devolver &local_var — dangling reference segura en C++ moderno
🦀
Rust Ownership
Reglas para SensorBridge
Preferir &T (borrow inmutable) o &mut T sobre transferir ownership
Para estado compartido entre threads: Arc<RwLock<T>>
Anotar lifetimes explícitas cuando el compilador lo exige — no silenciar
Minimizar bloques unsafe — documentar TODO unsafe con // SAFETY: razón
No usar .unwrap() en producción — propagar errores con ?
5
Checklist de Code Review — Memory Safety
🔴 Bloqueantes (impiden merge)
No hay new / delete raw en código C++ nuevo
Usar make_unique o make_shared. Excepción: interfaz con C legacy documentada con // RAW-NEEDED
Todo acceso compartido a datos mutables tiene lock (unique_lock o shared_lock)
Verificar en ModelRegistry, ConfigStore y cualquier map/vector accedido desde múltiples threads
No hay retorno de referencias a variables locales
El compilador avisa pero no siempre bloquea. Activar -Wall -Werror en CI
Los bloques unsafe en Rust tienen comentario // SAFETY: explicación
Obligatorio en PR que toquen SensorBridge o FFI con drivers de hardware
🟠 Advertencias (resolver antes de siguiente sprint)
Copy constructor y assignment operator marcados = delete en handles de recursos
FileHandle, SocketHandle, TensorBuffer — ya corregido en PR #47
weak_ptr usado para romper ciclos de shared_ptr en grafos de nodos
Revisar NodeGraph del pipeline de preprocesado
AddressSanitizer habilitado en build de CI/staging
Añadido -fsanitize=address,undefined al CMake Debug target. PR #49
Rust: .unwrap() reemplazado por ? o match en rutas críticas
Especialmente en handlers de USB/CAN con datos mal formados
🟢 Buenas prácticas (objetivo a 30 días)
Valgrind ejecutado en tests de integración de sesión larga (>6h)
Automatizar en pipeline nocturno para detectar leaks graduales
ThreadSanitizer habilitado en build ThreadSafe separado
Rust: cargo miri ejecutado en lógica de FFI
Detecta UB en código unsafe antes de que llegue a hardware
6
Herramientas de Diagnóstico — Comandos Listos
AddressSanitizer
Detecta use-after-free, leaks, buffer overflow en runtime
clang++ -fsanitize=address,undefined -g -O1 *.cpp && ./engine
Valgrind
Memoria dinámica — ideal para sesiones largas del motor IoT
valgrind --leak-check=full --track-origins=yes ./neuralengine
ThreadSanitizer
Detecta data races en ModelRegistry y InferenceEngine multi-thread
clang++ -fsanitize=thread -g *.cpp && ./engine --threads=4
Rust Miri
UB detector para bloques unsafe en SensorBridge y FFI
cargo +nightly miri test -- sensor_bridge
cargo clippy
Linter Rust — sugiere Arc/Rc, detecta unwrap en prod, unsafe sin doc
cargo clippy -- -D warnings -D clippy::unwrap_used
clang-tidy
Análisis estático C++ — reglas de modernización y seguridad
clang-tidy src/*.cpp --checks=modernize*,bugprone*
7
Plan de Acción por Prioridad
🔴 Crítico — Esta semana
  • Migrar TensorPool a unique_ptr (elimina crashes)
  • Añadir shared_mutex a ModelRegistry (elimina data races)
  • Habilitar -fsanitize=address en CI ahora mismo
🟠 Alto — Próximos 7 días
  • Refactorizar InferenceEngine con RAII buffers
  • Code review de todo código del junior con checklist
  • Integrar Valgrind en test de sesión larga CI
🔵 Medio — Sprint siguiente
  • Documentar todos los bloques unsafe de SensorBridge
  • Reemplazar .unwrap() por ? en Rust
  • Ejecutar clang-tidy en todo el codebase C++
🟢 Mejora — 30 días
  • POC: migrar TensorPool a Rust (seguridad compile-time)
  • ThreadSanitizer en build separado de CI
  • cargo miri en pipeline nocturno