-
Notifications
You must be signed in to change notification settings - Fork 0
[WIP] Fix division-by-zero error in power management #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # Changelog | ||
|
|
||
| All notable changes to HLV-RAPS are documented in this file. | ||
|
|
||
| ## [2.4.0] - 2026-03-04 | ||
|
|
||
| ### Security | ||
| - Fix division-by-zero in power & resource management when `elapsed_ms == 0` | ||
| - Fix PID controller parameter type (`float` → `uint64_t`) and add proportional-only fallback for zero dt | ||
| - Fix Monte Carlo division by zero in PDT engine when `monte_carlo_runs == 0` | ||
| - Clamp exponential argument in field coupling stress model to prevent `+inf` propagation | ||
| - Add request size limit (`MAX_REQUEST_SIZE = 8192`) and CORS risk comment to REST API server | ||
| - Add compile-time guard to prevent stub cryptography in production builds | ||
|
|
||
| ### Robustness | ||
| - Add NaN/Inf state sanitizer (`include/safety/state_sanitizer.hpp`) with check in APCU safety update | ||
| - Fix rollback store bounds: evict oldest entry instead of resetting counter to zero | ||
| - Fix telemetry report truncated-line detection for JSONL lines exceeding buffer size | ||
| - Fix implicit narrowing in artificial gravity rate-limit computation | ||
|
|
||
| ### Architecture | ||
| - Deduplicate `RAPSConfig` struct and conflicting constants from `hlv_field_dynamics.hpp` | ||
| - Add non-production compile guard to reference integrator header | ||
| - Fix broken include paths in `advanced_propulsion_control_unit.hpp` | ||
|
|
||
| ### Maintainability | ||
| - Add `VERSION` file (`2.4.0`) | ||
| - Add `CHANGELOG.md` | ||
| - Add `RAPSVersion` namespace constants to `raps_core_types.hpp` | ||
| - Update REST API health endpoint to use `RAPSVersion::STRING` | ||
|
|
||
| ## [2.3.0] - (previous release) | ||
|
|
||
| <!-- placeholder: details for v2.3.0 not available --> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 2.4.0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| #pragma once | ||
| #include <cmath> | ||
| #include "hlv/spacetime_modulation_types.hpp" | ||
|
|
||
| inline bool is_finite_and_valid(float v) { | ||
| return std::isfinite(v); | ||
| } | ||
|
|
||
| inline bool sanitize_spacetime_state(const SpacetimeModulationState& s) { | ||
| return is_finite_and_valid(s.warp_field_strength) | ||
| && is_finite_and_valid(s.gravito_flux_bias) | ||
| && is_finite_and_valid(s.spacetime_curvature_magnitude) | ||
| && is_finite_and_valid(s.time_dilation_factor) | ||
| && is_finite_and_valid(s.induced_gravity_g) | ||
| && is_finite_and_valid(s.spacetime_stability_index) | ||
| && is_finite_and_valid(s.power_draw_GW) | ||
| && is_finite_and_valid(s.remaining_antimatter_kg) | ||
| && is_finite_and_valid(s.quantum_fluid_level) | ||
| && is_finite_and_valid(s.field_coupling_stress) | ||
| && is_finite_and_valid(s.control_authority_remaining); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,8 @@ | |
| #include <iomanip> | ||
| #include <algorithm> | ||
|
|
||
| #include "raps/core/raps_core_types.hpp" | ||
|
|
||
| // POSIX sockets | ||
| #include <sys/socket.h> | ||
| #include <netinet/in.h> | ||
|
|
@@ -16,10 +18,15 @@ namespace raps::api { | |
|
|
||
| namespace { | ||
|
|
||
| // Maximum HTTP request size in bytes — protects against oversized/malformed requests | ||
| constexpr size_t MAX_REQUEST_SIZE = 8192; | ||
|
|
||
| // HTTP response templates | ||
| constexpr const char* HTTP_200_HEADER = | ||
| "HTTP/1.1 200 OK\r\n" | ||
| "Content-Type: application/json\r\n" | ||
| // Access-Control-Allow-Origin: * is intentional for an internal-only observability API. | ||
| // RISK ACCEPTED: This server must only be bound to a loopback/trusted interface. | ||
| "Access-Control-Allow-Origin: *\r\n" | ||
| "Connection: close\r\n" | ||
| "Content-Length: "; | ||
|
|
@@ -177,13 +184,20 @@ void RestApiServer::server_thread_main() { | |
| } | ||
|
|
||
| void RestApiServer::handle_client(int client_sock) { | ||
| // Read request | ||
| char buffer[4096]; | ||
| ssize_t bytes_read = ::recv(client_sock, buffer, sizeof(buffer) - 1, 0); | ||
| // Read request with size limit to prevent oversized/malicious requests | ||
| char buffer[MAX_REQUEST_SIZE + 1]; | ||
| ssize_t bytes_read = ::recv(client_sock, buffer, MAX_REQUEST_SIZE, 0); | ||
|
|
||
| if (bytes_read <= 0) { | ||
| return; | ||
| } | ||
|
|
||
| // Reject requests that exceed the size limit | ||
| if (static_cast<size_t>(bytes_read) >= MAX_REQUEST_SIZE) { | ||
| std::string rejection = json_error(413, "Request Too Large"); | ||
| ::send(client_sock, rejection.c_str(), rejection.length(), 0); | ||
| return; | ||
| } | ||
|
Comment on lines
+195
to
+200
|
||
|
|
||
| buffer[bytes_read] = '\0'; | ||
| std::string request(buffer); | ||
|
|
@@ -235,7 +249,7 @@ std::string RestApiServer::handle_health() { | |
| json << "{" | ||
| << "\"status\":\"ok\"," | ||
| << "\"service\":\"HLV-RAPS Flight Middleware\"," | ||
| << "\"api_version\":\"1.0\"," | ||
| << "\"api_version\":\"" << RAPSVersion::STRING << "\"," | ||
| << "\"observability_only\":true" | ||
| << "}"; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,26 +11,21 @@ inline float compute_pid_output( | |
| float ki, | ||
| float kd, | ||
| float integral_limit, | ||
| float elapsed_ms) { | ||
| uint64_t elapsed_ms) { | ||
|
|
||
| // Integral term with anti-windup | ||
| float dt_s = 0.0f; | ||
| if (elapsed_ms > 0.0f) { | ||
| dt_s = elapsed_ms / 1000.0f; | ||
| if (elapsed_ms == 0) { | ||
| // Zero-time tick: return proportional-only to avoid division by zero | ||
| return kp * error; | ||
| } | ||
| float dt_s = static_cast<float>(elapsed_ms) / 1000.0f; | ||
|
Comment on lines
+14
to
+20
|
||
|
|
||
| if (dt_s > 0.0f) { | ||
| integral += error * dt_s; | ||
| integral = std::max( | ||
| -integral_limit, | ||
| std::min(integral_limit, integral) | ||
| ); | ||
| } | ||
| integral += error * dt_s; | ||
| integral = std::max( | ||
| -integral_limit, | ||
| std::min(integral_limit, integral) | ||
| ); | ||
|
|
||
| float derivative = 0.0f; | ||
| if (dt_s > 0.0f) { | ||
| derivative = (error - previous_error) / dt_s; | ||
| } | ||
| float derivative = (error - previous_error) / dt_s; | ||
|
|
||
| float output = | ||
| (kp * error) + | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,9 +13,11 @@ inline void update_power_and_resources( | |
|
|
||
| // Power Draw & Resource Consumption (Section 7) | ||
|
|
||
| // Guard against zero-time delta to prevent division by zero (startup/rollover) | ||
| float dt_ms = static_cast<float>(std::max(elapsed_ms, uint64_t{1})); | ||
| state.power_draw_GW = compute_power_draw( | ||
| warp_change_request / static_cast<float>(elapsed_ms), | ||
| flux_change_request / static_cast<float>(elapsed_ms) | ||
| warp_change_request / dt_ms, | ||
| flux_change_request / dt_ms | ||
|
Comment on lines
+16
to
+20
|
||
| ); | ||
|
|
||
| // Apply effective power budget (constrained by resource capability) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -36,6 +36,14 @@ int main(int argc, char** argv) { | |||||||||||
|
|
||||||||||||
| char buf[8192]; | ||||||||||||
| while (std::fgets(buf, sizeof(buf), f)) { | ||||||||||||
| size_t len = std::strlen(buf); | ||||||||||||
| if (len > 0 && buf[len - 1] != '\n') { | ||||||||||||
| // Line was truncated; skip remainder to avoid corrupting counters | ||||||||||||
| int c; | ||||||||||||
| while ((c = std::fgetc(f)) != '\n' && c != EOF) {} | ||||||||||||
| std::fprintf(stderr, "warning: truncated line at record %llu\n", | ||||||||||||
| (unsigned long long)total); | ||||||||||||
|
||||||||||||
| (unsigned long long)total); | |
| (unsigned long long)(total + 1)); | |
| // Count this line but skip processing the partial JSON record | |
| ++total; | |
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The production-build guard here uses
#ifdef RAPS_PRODUCTION_BUILD, which will fire even if the macro is defined as0. Elsewhere in this PR (e.g.,src/platform/platform_hal.cpp) production gating is done with#if defined(RAPS_PRODUCTION_BUILD) && RAPS_PRODUCTION_BUILD == 1. To keep semantics consistent and avoid accidental build breaks when the macro is defined-but-false, consider using the samedefined(...) && ...==1pattern here.