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
💡 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++ nuevoUsar 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 recursosFileHandle, 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