2 Commits

Author SHA1 Message Date
anth64 fb0d8adb8f chore: bump version to 0.0.4 2026-02-11 00:22:59 +01:00
anth64 2c4d27f915 fix(linux): prevent segfault from invalid module indices during rapid reloads
When spamming file changes rapidly, inotify can report stale UNLOAD/RELOAD
events for modules that were already unloaded by previous events in the same
poll cycle. This caused is_mod_loaded() to return -1, which was then cast to
size_t (18446744073709551615) and used as an array index, causing segfaults.

Additionally, event counts were calculated before validation, causing loops to
run more iterations than valid indices were populated, reading garbage values.

Changes:
- stk.c: Check if is_mod_loaded() returns valid index (>= 0) before adding
  to unload/reload lists
- stk.c: Reset and recalculate counts after populating arrays with only valid
  indices to prevent loop overrun
- Skip processing events for modules that are no longer loaded

This completes the Linux stability fixes started in v0.0.2.
2026-02-11 00:17:33 +01:00
4 changed files with 31 additions and 12 deletions
+9
View File
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.0.4] - 2026-02-11
### Fixed
- **Linux**: Fixed segfault from invalid module indices during extremely rapid file changes
- Added validation check to skip stale UNLOAD/RELOAD events for already-unloaded modules
- Prevents is_mod_loaded() returning -1 from being used as array index (SIZE_MAX)
- Fixed event count mismatch where loops would run more iterations than valid indices populated
- Completes the Linux hot-reload stability fixes from v0.0.2
## [0.0.3] - 2026-02-10 ## [0.0.3] - 2026-02-10
### Fixed ### Fixed
+3 -3
View File
@@ -173,15 +173,15 @@ stk_init();
## Project Status ## Project Status
**Current Version:** 0.0.3 (Pre-release) **Current Version:** 0.0.4 (Pre-release)
This is an early bugfix release improving compilation warnings on Linux. Phase 1 is still in progress. This is a bugfix release completing the Linux hot-reload stability improvements. Phase 1 is still in progress.
### What Works ### What Works
- Cross-platform module loading and hot-reloading - Cross-platform module loading and hot-reloading
- File watching (inotify/kqueue/FindFirstFile) - File watching (inotify/kqueue/FindFirstFile)
- Basic error handling - Basic error handling
- Stable hot-reload even during rapid file changes - Robust hot-reload even during extremely rapid file changes (Linux fixes in 0.0.2-0.0.4)
### In Progress (Phase 1) ### In Progress (Phase 1)
- Complete logging system (log levels, verbosity, output configuration) - Complete logging system (log levels, verbosity, output configuration)
+1 -1
View File
@@ -3,7 +3,7 @@
#define STK_VERSION_MAJOR 0 #define STK_VERSION_MAJOR 0
#define STK_VERSION_MINOR 0 #define STK_VERSION_MINOR 0
#define STK_VERSION_PATCH 3 #define STK_VERSION_PATCH 4
#define STK_STRINGIFY_HELPER(x) #x #define STK_STRINGIFY_HELPER(x) #x
#define STK_STRINGIFY(x) STK_STRINGIFY_HELPER(x) #define STK_STRINGIFY(x) STK_STRINGIFY_HELPER(x)
+18 -8
View File
@@ -172,8 +172,7 @@ size_t stk_poll(void)
char (*file_list)[STK_PATH_MAX] = NULL; char (*file_list)[STK_PATH_MAX] = NULL;
stk_module_event_t *events = NULL; stk_module_event_t *events = NULL;
size_t i, file_count = 0, reload_count = 0, load_count = 0, size_t i, file_count = 0, reload_count = 0, load_count = 0,
unload_count = 0, reload_index = 0, load_index = 0, unload_count = 0;
unload_index = 0;
int *reloaded_mod_indices = NULL, *reloaded_mod_file_indices = NULL, int *reloaded_mod_indices = NULL, *reloaded_mod_file_indices = NULL,
*unloaded_mod_indices = NULL, *loaded_mod_indices = NULL; *unloaded_mod_indices = NULL, *loaded_mod_indices = NULL;
size_t remaining_loads, new_capacity, holes_to_fill; size_t remaining_loads, new_capacity, holes_to_fill;
@@ -208,20 +207,31 @@ size_t stk_poll(void)
unloaded_mod_indices = malloc(unload_count * sizeof(int)); unloaded_mod_indices = malloc(unload_count * sizeof(int));
loaded_mod_indices = malloc(load_count * sizeof(int)); loaded_mod_indices = malloc(load_count * sizeof(int));
reload_count = 0;
unload_count = 0;
load_count = 0;
for (i = 0; i < file_count; ++i) { for (i = 0; i < file_count; ++i) {
int mod_index;
extract_module_id(file_list[i], mod_id); extract_module_id(file_list[i], mod_id);
switch (events[i]) { switch (events[i]) {
case STK_MOD_LOAD: case STK_MOD_LOAD:
loaded_mod_indices[load_index++] = i; loaded_mod_indices[load_count++] = i;
break; break;
case STK_MOD_RELOAD: case STK_MOD_RELOAD:
reloaded_mod_file_indices[reload_index] = i; mod_index = is_mod_loaded(mod_id);
reloaded_mod_indices[reload_index++] = if (mod_index >= 0) {
is_mod_loaded(mod_id); reloaded_mod_file_indices[reload_count] = i;
reloaded_mod_indices[reload_count] = mod_index;
reload_count++;
}
break; break;
case STK_MOD_UNLOAD: case STK_MOD_UNLOAD:
unloaded_mod_indices[unload_index++] = mod_index = is_mod_loaded(mod_id);
is_mod_loaded(mod_id); if (mod_index >= 0) {
unloaded_mod_indices[unload_count] = mod_index;
unload_count++;
}
break; break;
} }
} }