fix(platform): correct filename buffer handling and prevent overflows

- Replace undefined 'len' variable references with 'name_len'
- Add explicit null terminators after memcpy operations
- Move name_len declaration to Windows-specific scope to eliminate unused variable warning on BSD
- Standardize string copy pattern across all platforms: strlen -> clamp -> memcpy -> null terminate
- Fix increment operator placement for clarity (i++ moved outside array indexing)
- Remove duplicate name_len declaration in platform_directory_watch_check

This ensures proper bounds checking and null termination for filenames
on both Windows and BSD systems, preventing potential buffer overflows
when handling long filenames (approaching STK_PATH_MAX).
This commit is contained in:
2026-02-01 02:38:45 +01:00
parent 1919287c39
commit 28dfc89b15
+129 -113
View File
@@ -286,126 +286,128 @@ void *platform_get_symbol(void *h, const char *s)
#endif #endif
} }
char (*platform_directory_init_scan(const char *dir_path, size_t *out_count)) char (*platform_directory_init_scan(const char *dir_path,
[STK_PATH_MAX] { size_t *out_count))[STK_PATH_MAX]
size_t count = 0, i = 0, name_len; {
char (*list)[STK_PATH_MAX] = NULL; size_t count = 0, i = 0, name_len;
char(*list)[STK_PATH_MAX] = NULL;
#ifdef _WIN32 #ifdef _WIN32
WIN32_FIND_DATAA fd; WIN32_FIND_DATAA fd;
HANDLE h; HANDLE h;
char s[STK_PATH_MAX_OS]; char s[STK_PATH_MAX_OS];
sprintf(s, "%s\\*", dir_path); sprintf(s, "%s\\*", dir_path);
h = FindFirstFileA(s, &fd); h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE) if (h == INVALID_HANDLE_VALUE)
goto create_and_exit; goto create_and_exit;
do { do {
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
continue; continue;
if (is_valid_module_file(fd.cFileName)) if (is_valid_module_file(fd.cFileName))
count++; count++;
} while (FindNextFileA(h, &fd)); } while (FindNextFileA(h, &fd));
FindClose(h); FindClose(h);
if (count == 0) if (count == 0)
goto exit; goto exit;
list = malloc(count * sizeof(*list)); list = malloc(count * sizeof(*list));
if (!list) if (!list)
goto exit; goto exit;
h = FindFirstFileA(s, &fd); h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE) if (h == INVALID_HANDLE_VALUE)
goto exit; goto exit;
do { do {
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
continue; continue;
if (is_valid_module_file(fd.cFileName) && i < count) { if (is_valid_module_file(fd.cFileName) && i < count) {
name_len = strlen(fd.cFileName); name_len = strlen(fd.cFileName);
if (name_len >= STK_PATH_MAX) if (name_len >= STK_PATH_MAX)
len = STK_PATH_MAX - 1; name_len = STK_PATH_MAX - 1;
memcpy(list[i], fd.cFileName, name_len); memcpy(list[i], fd.cFileName, name_len);
list[i++][len] = '\0'; list[i][name_len] = '\0';
} i++;
} while (FindNextFileA(h, &fd)); }
} while (FindNextFileA(h, &fd));
FindClose(h); FindClose(h);
goto exit; goto exit;
create_and_exit: create_and_exit:
platform_mkdir(dir_path); platform_mkdir(dir_path);
exit: exit:
*out_count = i; *out_count = i;
return list; return list;
#else #else
DIR *d; DIR *d;
struct dirent *e; struct dirent *e;
struct stat st; struct stat st;
char f[STK_PATH_MAX_OS]; char f[STK_PATH_MAX_OS];
d = opendir(dir_path); d = opendir(dir_path);
if (!d) if (!d)
goto create_and_exit; goto create_and_exit;
count_loop: count_loop:
e = readdir(d); e = readdir(d);
if (!e) if (!e)
goto count_done; goto count_done;
sprintf(f, "%s/%s", dir_path, e->d_name); sprintf(f, "%s/%s", dir_path, e->d_name);
if (!is_valid_module_file(e->d_name)) if (!is_valid_module_file(e->d_name))
goto count_loop; goto count_loop;
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode)) if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto count_loop; goto count_loop;
count++; count++;
goto count_loop; goto count_loop;
count_done: count_done:
if (count == 0) if (count == 0)
goto close_and_exit; goto close_and_exit;
rewinddir(d); rewinddir(d);
list = malloc(count * sizeof(*list)); list = malloc(count * sizeof(*list));
if (!list) if (!list)
goto close_and_exit; goto close_and_exit;
fill_loop: fill_loop:
e = readdir(d); e = readdir(d);
if (!e || i >= count) if (!e || i >= count)
goto close_and_exit; goto close_and_exit;
sprintf(f, "%s/%s", dir_path, e->d_name); sprintf(f, "%s/%s", dir_path, e->d_name);
if (!is_valid_module_file(e->d_name)) if (!is_valid_module_file(e->d_name))
goto fill_loop; goto fill_loop;
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode)) if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto fill_loop; goto fill_loop;
name_len = strlen(e->d_name); name_len = strlen(e->d_name);
if (name_len >= STK_PATH_MAX) { if (name_len >= STK_PATH_MAX) {
name_len = STK_PATH_MAX - 1; name_len = STK_PATH_MAX - 1;
} }
memcpy(list[i++], e->d_name, name_len); memcpy(list[i++], e->d_name, name_len);
list[i - 1][name_len] = '\0'; list[i - 1][name_len] = '\0';
goto fill_loop; goto fill_loop;
create_and_exit: create_and_exit:
platform_mkdir(dir_path); platform_mkdir(dir_path);
*out_count = 0; *out_count = 0;
return NULL; return NULL;
close_and_exit: close_and_exit:
closedir(d); closedir(d);
*out_count = i; *out_count = i;
return list; return list;
#endif #endif
} }
#if !defined(__linux__) && !defined(_WIN32) #if !defined(__linux__) && !defined(_WIN32)
static void update_watches(platform_watch_context_t *ctx) static void update_watches(platform_watch_context_t *ctx)
@@ -489,12 +491,11 @@ void *platform_directory_watch_start(const char *path)
fd, path, IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM); fd, path, IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM);
return (void *)(long)fd; return (void *)(long)fd;
#else #else
size_t name_len;
#ifdef _WIN32 #ifdef _WIN32
WIN32_FIND_DATAA fd; WIN32_FIND_DATAA fd;
HANDLE h; HANDLE h;
char s[STK_PATH_MAX_OS]; char s[STK_PATH_MAX_OS];
size_t count = 0, i = 0; size_t count = 0, i = 0, name_len;
#else #else
DIR *d; DIR *d;
struct dirent *e; struct dirent *e;
@@ -545,10 +546,12 @@ void *platform_directory_watch_start(const char *path)
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
is_valid_module_file(fd.cFileName) && i < count) { is_valid_module_file(fd.cFileName) && i < count) {
name_len = strlen(fd.cFileName); name_len = strlen(fd.cFileName);
if (len >= STK_PATH_MAX) if (name_len >= STK_PATH_MAX)
len = STK_PATH_MAX - 1; name_len = STK_PATH_MAX - 1;
memcpy(ctx->snaps[i].filename, fd.cFileName, len); memcpy(ctx->snaps[i].filename, fd.cFileName, name_len);
ctx->snaps[i++].mtime = fd.ftLastWriteTime; ctx->snaps[i].filename[name_len] = '\0';
ctx->snaps[i].mtime = fd.ftLastWriteTime;
i++;
} }
} while (FindNextFileA(h, &fd)); } while (FindNextFileA(h, &fd));
@@ -773,7 +776,7 @@ stk_module_event_t *platform_directory_watch_check(
WIN32_FIND_DATAA fd; WIN32_FIND_DATAA fd;
HANDLE h; HANDLE h;
char s[STK_PATH_MAX_OS]; char s[STK_PATH_MAX_OS];
size_t count = 0, name_len; size_t count = 0;
sprintf(s, "%s\\*", ctx->path); sprintf(s, "%s\\*", ctx->path);
h = FindFirstFileA(s, &fd); h = FindFirstFileA(s, &fd);
@@ -805,12 +808,12 @@ stk_module_event_t *platform_directory_watch_check(
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
is_valid_module_file(fd.cFileName) && new_count < count) { is_valid_module_file(fd.cFileName) && new_count < count) {
name_len = strlen(fd.cFileName); name_len = strlen(fd.cFileName);
if (len >= STK_PATH_MAX) if (name_len >= STK_PATH_MAX)
len = STK_PATH_MAX - 1; name_len = STK_PATH_MAX - 1;
memcpy(new_snaps[new_count].filename, fd.cFileName, memcpy(new_snaps[new_count].filename, fd.cFileName,
len); name_len);
new_snaps[new_count].filename[len] = '\0'; new_snaps[new_count].filename[name_len] = '\0';
new_snaps[new_count].mtime = fd.ftLastWriteTime; new_snaps[new_count].mtime = fd.ftLastWriteTime;
new_count++; new_count++;
} }
@@ -904,9 +907,13 @@ build_diff:
&new_snaps[j].mtime) != 0) { &new_snaps[j].mtime) != 0) {
if (is_file_ready(ctx->path, if (is_file_ready(ctx->path,
new_snaps[j].filename)) { new_snaps[j].filename)) {
strncpy((*file_list)[ev_index], name_len =
new_snaps[j].filename, strlen(new_snaps[j].filename);
STK_PATH_MAX - 1); if (name_len >= STK_PATH_MAX)
name_len = STK_PATH_MAX - 1;
memcpy((*file_list)[ev_index],
new_snaps[j].filename, name_len);
(*file_list)[ev_index][name_len] = '\0';
evs[ev_index++] = STK_MOD_RELOAD; evs[ev_index++] = STK_MOD_RELOAD;
} else { } else {
new_snaps[j].mtime = new_snaps[j].mtime =
@@ -920,10 +927,11 @@ build_diff:
name_len = name_len =
strlen(ctx->snaps[i].filename); strlen(ctx->snaps[i].filename);
if (name_len >= STK_PATH_MAX) if (name_len >= STK_PATH_MAX)
len = STK_PATH_MAX; name_len = STK_PATH_MAX - 1;
memcpy((*file_list)[ev_index], memcpy((*file_list)[ev_index],
ctx->snaps[i].filename, ctx->snaps[i].filename,
name_len); name_len);
(*file_list)[ev_index][name_len] = '\0';
evs[ev_index++] = STK_MOD_RELOAD; evs[ev_index++] = STK_MOD_RELOAD;
} else { } else {
new_snaps[j].mtime = new_snaps[j].mtime =
@@ -935,8 +943,12 @@ build_diff:
} }
if (!found) { if (!found) {
strncpy((*file_list)[ev_index], ctx->snaps[i].filename, name_len = strlen(ctx->snaps[i].filename);
STK_PATH_MAX - 1); if (name_len >= STK_PATH_MAX)
name_len = STK_PATH_MAX - 1;
memcpy((*file_list)[ev_index], ctx->snaps[i].filename,
name_len);
(*file_list)[ev_index][name_len] = '\0';
evs[ev_index++] = STK_MOD_UNLOAD; evs[ev_index++] = STK_MOD_UNLOAD;
} }
} }
@@ -952,8 +964,12 @@ build_diff:
} }
if (!found) { if (!found) {
strncpy((*file_list)[ev_index], new_snaps[j].filename, name_len = strlen(new_snaps[j].filename);
STK_PATH_MAX - 1); if (name_len >= STK_PATH_MAX)
name_len = STK_PATH_MAX - 1;
memcpy((*file_list)[ev_index], new_snaps[j].filename,
name_len);
(*file_list)[ev_index][name_len] = '\0';
evs[ev_index++] = STK_MOD_LOAD; evs[ev_index++] = STK_MOD_LOAD;
} }
} }