Repository navigation
Memory Safety
SparkEngine implements a multi-layered memory safety strategy using C++23 features with forward-compatible bridges to C++26. This page covers the type-safe utilities, contract annotations, safe cast wrappers, and compiler hardening that protect against common memory errors.
Source: SparkEngine/Source/Core/Contracts.h, NonNull.h, SafeCast.h
Compile-Time Safety
┌─────────────────────────┐
│ NonNull<T*> │ Type-level null rejection
│ NarrowCast<To> │ Checked numeric narrowing
│ CheckedCast<Derived*> │ Validated pointer downcast
└───────────┬─────────────┘
│
Debug-Time Safety
┌───────────┴─────────────┐
│ SPARK_EXPECTS(expr) │ Precondition assertions
│ SPARK_ENSURES(expr) │ Postcondition assertions
│ SPARK_INVARIANT(expr) │ Class invariant checks
│ ASSERT_BOUNDS / NOT_NULL│ Existing assert macros
└───────────┬─────────────┘
│
Runtime / CI Safety
┌───────────┴─────────────┐
│ ASan + UBSan + LSan │ Address/undefined/leak
│ TSan │ Thread race detection
│ MSan │ Uninitialized memory
│ MemoryDebugger │ Allocation tracking
│ MemoryMonitor │ Anomaly detection
│ MemoryIntegrity │ Anti-tamper scanning
└───────────┬─────────────┘
│
Compiler Hardening
┌───────────┴─────────────┐
│ -fstack-protector-strong│ Stack overflow detection
│ _FORTIFY_SOURCE=2 │ libc buffer checks
│ -Wl,-z,relro,-z,now │ Full RELRO (GOT protection)
│ /GS (MSVC default) │ Buffer security check
└─────────────────────────┘
| File | Responsibility |
|---|---|
Core/Contracts.h |
C++26 contract bridge macros (SPARK_EXPECTS/ENSURES/INVARIANT) |
Core/NonNull.h |
Non-null pointer wrapper with debug enforcement |
Core/SafeCast.h |
NarrowCast and CheckedCast type-safe utilities |
Core/Platform.h |
SPARK_HAS_CONTRACTS feature detection macro |
Utils/Assert.h |
13 assertion macros (fallback for contracts) |
Utils/MemoryDebugger.h |
Allocation tracking, leak detection, double-free detection |
Utils/MemoryMonitor.h |
Budget enforcement, growth tracking, anomaly detection |
Engine/Security/MemoryIntegrity.h |
Runtime code scanning and branch guards |
CMakeLists.txt |
Compiler hardening flags |
Source: Core/Contracts.h
Contract macros annotate function preconditions, postconditions, and class invariants. When C++26 compiler support arrives (SPARK_HAS_CONTRACTS), they automatically switch to standard [[pre:]]/[[post:]]/[[assert:]] attributes. Until then, they fall back to debug-mode ASSERT_MSG macros.
| Macro | Purpose | C++26 Equivalent |
|---|---|---|
SPARK_EXPECTS(expr) |
Precondition — checked at function entry | [[pre: expr]] |
SPARK_ENSURES(expr) |
Postcondition — checked before return | [[post: expr]] |
SPARK_INVARIANT(expr) |
Class invariant — checked at key points | [[assert: expr]] |
#include "Core/Contracts.h"
void FixedTimestepAccumulator::Initialize(float fixedDt)
{
SPARK_EXPECTS(fixedDt > 0.0f); // precondition: valid timestep
// ... implementation ...
SPARK_ENSURES(m_initialized); // postcondition: state is valid
}
void FrameAllocator::AllocRaw(size_t bytes, size_t alignment)
{
SPARK_EXPECTS(alignment > 0 && (alignment & (alignment - 1)) == 0); // power of 2
// ... implementation ...
}
void ModuleManager::ResizeAll(int width, int height)
{
SPARK_EXPECTS(width > 0 && height > 0);
// ... implementation ...
}| System | File | Contract |
|---|---|---|
| EngineContext | Core/EngineContext.h |
RegisterSystem() — system ptr not null |
| FrameAllocator | Utils/FrameAllocator.h |
AllocRaw() — alignment is power of 2 |
| FixedTimestep | Core/FixedTimestepAccumulator.cpp |
Initialize/SetFixedTimestep/Advance — dt > 0, frameDt >= 0 |
| ModuleManager | Core/ModuleManager.cpp |
LoadModule — path not empty; InitializeAll — context not null; ResizeAll — dimensions > 0 |
| ObjectPool | Utils/ObjectPool.h |
Constructor — capacity > 0 |
| RingBuffer | Utils/RingBuffer.h |
operator[] — index < count |
| ConstantBufferRing | Graphics/ConstantBufferRing.h |
Initialize — device not null, capacity > 0 |
| RHIAdapter | Graphics/RHI/RHIAdapter.cpp |
Initialize — device not null |
| MaterialSystem | Graphics/MaterialSystem.cpp |
Initialize — device and context not null |
Source: Core/NonNull.h
Spark::NonNull<T*> documents non-null intent at the type level and enforces it with a debug-mode assertion. In release builds, it compiles to a raw pointer with zero overhead.
namespace Spark
{
template <typename T> class NonNull
{
public:
constexpr NonNull(T ptr); // debug-asserts non-null
NonNull(std::nullptr_t) = delete; // compile error on nullptr
constexpr operator T() const; // implicit conversion to raw ptr
constexpr element_type& operator*() const;
constexpr T operator->() const;
constexpr T Get() const;
};
template <typename T> NonNull(T) -> NonNull<T>; // CTAD
}#include "Core/NonNull.h"
// Function signature documents that graphics must not be null
RenderSystem(Spark::NonNull<GraphicsEngine*> graphics)
: m_graphics(graphics) {}
// Callers pass raw pointers — NonNull checks in debug
auto* gfx = context->GetGraphics();
auto renderSys = RenderSystem(gfx); // asserts non-null in debug| System | File | Parameter |
|---|---|---|
| RenderSystem | ECS/Systems/ECSystems.h |
Constructor graphics parameter |
| PhysicsUpdateSystem | ECS/Systems/ECSystems.h |
Constructor physics parameter |
| AudioUpdateSystem | ECS/Systems/ECSystems.h |
Constructor audio parameter |
- Constructor parameters stored as non-owning member pointers
- Functions that immediately dereference their pointer parameter
- Functions documented as requiring non-null ("must not be null")
-
SetXxx(T* ptr)methods where null is invalid
- Return types (keep as raw
T*with nullable semantics) - Optional parameters that accept nullptr
- Internal/private methods where null is already impossible
Source: Core/SafeCast.h
Checked narrowing numeric conversion. In debug builds, asserts that the round-trip cast preserves the value (catches overflow, truncation, and sign changes). In release builds, compiles to static_cast.
#include "Core/SafeCast.h"
// Safe narrowing — asserts in debug if value is lost
int count = Spark::NarrowCast<int>(m_points.size());
// Signed/unsigned mismatch — also checks sign preservation
auto idx = Spark::NarrowCast<int>(unsignedValue);Validated pointer downcast. In debug builds, verifies the cast via dynamic_cast and asserts on mismatch. In release builds, compiles to static_cast.
#include "Core/SafeCast.h"
// Safe downcast — verifies type in debug
auto* wander = Spark::CheckedCast<RandomWanderGenerator*>(gen);
auto* eventTrack = Spark::CheckedCast<EventTrack*>(track.get());| System | File | Replacement |
|---|---|---|
| SplinePath | Utils/SplinePath.cpp |
9x NarrowCast<int>(m_points.size())
|
| MovementSystem | Engine/AI/MovementSystem.cpp |
10x CheckedCast<XxxGenerator*>(gen)
|
| Sequencer | Engine/Cinematic/Sequencer.cpp |
4x CheckedCast<XxxTrack*>(track.get())
|
NarrowCast: Any static_cast between numeric types where the destination is narrower than the source (size_t to int, double to float, int to uint8_t). Avoid in hot loops.
CheckedCast: Any static_cast downcast in a class hierarchy where you "know" the type but want debug verification. Not for reinterpret_cast (socket/GPU/serialization code).
The build system enables multiple layers of compiler-level protection:
| Flag | Purpose |
|---|---|
-fstack-protector-strong |
Detects stack buffer overflows via canary values |
-D_FORTIFY_SOURCE=2 |
Compile-time + runtime buffer overflow checks for libc functions (Release only) |
-Wl,-z,relro,-z,now |
Full RELRO — GOT becomes read-only after relocation (Linux) |
-fno-omit-frame-pointer |
Preserves frame pointers for backtrace resolution |
_GLIBCXX_ASSERTIONS |
STL container bounds checking (Debug only) |
| Flag | Purpose |
|---|---|
/GS |
Buffer security check (enabled by default) |
/RTC1 |
Stack frame validation + uninitialized variable detection (Debug) |
_ITERATOR_DEBUG_LEVEL=1 |
Iterator bounds checking without extreme slowdown |
| Sanitizer | What It Detects | CI Job |
|---|---|---|
| ASan + UBSan + LSan | Buffer overflows, undefined behavior, memory leaks | build-linux-asan |
| TSan | Data races, deadlocks | build-linux-tsan |
| MSan | Uninitialized memory reads | build-linux-msan |
SparkEngine detects C++26 features via macros in Core/Platform.h:
| Macro | C++26 Feature | Current Status |
|---|---|---|
SPARK_HAS_CONTRACTS |
[[pre:]] / [[post:]] / [[assert:]]
|
Detected, not yet available |
SPARK_HAS_REFLECTION |
std::meta::reflect |
Detected, not yet available |
SPARK_HAS_PACK_INDEXING |
Pack indexing expressions | Detected, not yet available |
When compilers ship C++26 contract support, the SPARK_EXPECTS/SPARK_ENSURES/SPARK_INVARIANT macros will automatically switch to standard attributes with no code changes required.
Profiles (safe subset): The C++26 Profiles proposal aims to add opt-in safety modes (bounds checking, null checking, lifetime analysis). If accepted, SparkEngine's existing safety utilities will complement — not conflict with — profiles.
- Use SPARK_EXPECTS for public API boundaries. Document what callers must guarantee.
-
Use NonNull for constructor parameters. If a system requires a pointer at construction, make it
NonNull. - Use NarrowCast for size_t-to-int conversions. These are the most common source of silent truncation.
- Use CheckedCast for type-switch downcasts. Anywhere you switch on a type enum then downcast.
- Don't replace reinterpret_cast. Socket, GPU, and serialization casts are inherently low-level.
-
Don't use NonNull for return types. Return raw
T*— callers check for null. - Don't add contracts to hot loops. They are for API boundaries, not per-element checks.
The safety utilities have dedicated tests:
| Test File | Tests |
|---|---|
Tests/TestContracts.cpp |
Contract macros pass on valid conditions, complex expressions |
Tests/TestSafetyCoreUtils.cpp |
NonNull construction, conversion, CTAD; NarrowCast lossless conversions; CheckedCast valid/null/const |
Run with:
cd build && ctest --output-on-failure --no-tests=error- Memory Management Patterns — Ownership rules, allocators, debugging tools
- Memory Integrity — Anti-tamper system, branch guards
- Error Handling Patterns — SPARK_VALIDATE, SPARK_CHECK patterns
- Threading Model — Thread safety rules and mutex conventions
- Testing — CI sanitizer configuration and test infrastructure
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers