What
create_file_hook_narrow writes the boot.config override path into the caller's lpFileName buffer via memcpy, which overflows that buffer whenever the override path is longer than the original path the game passed to CreateFileA.
Detail
src/windows/entrypoint.c (master 686e19b):
if (strcmpi(normalised_path, default_boot_config_path) == 0) {
char *narrowed_boot_config_override = narrow(config.boot_config_override);
memcpy(actual_file_name, narrowed_boot_config_override,
strlen(config.boot_config_override)); // actual_file_name == caller's lpFileName
free(narrowed_boot_config_override);
...
}
return CreateFileA(actual_file_name, ...);
actual_file_name is the caller's lpFileName. The caller sized that buffer for its own path (...\X_Data\boot.config); copying a longer boot_config_override into it is an out-of-bounds write into the game's memory. (strlen on the wide config.boot_config_override is also the wrong length unit.)
The wide variant create_file_hook does this correctly — it points actual_file_name at the override and never writes into the caller's buffer.
Fix
Mirror the wide hook: point actual_file_name at our own narrowed override buffer, pass it to CreateFileA, and free it after the call. No write into the caller's buffer. PR attached.
(Triggers on the ANSI CreateFileA path with boot_config_override set to a path longer than the game's boot.config path.)
What
create_file_hook_narrowwrites the boot.config override path into the caller'slpFileNamebuffer viamemcpy, which overflows that buffer whenever the override path is longer than the original path the game passed toCreateFileA.Detail
src/windows/entrypoint.c(master 686e19b):actual_file_nameis the caller'slpFileName. The caller sized that buffer for its own path (...\X_Data\boot.config); copying a longerboot_config_overrideinto it is an out-of-bounds write into the game's memory. (strlenon the wideconfig.boot_config_overrideis also the wrong length unit.)The wide variant
create_file_hookdoes this correctly — it pointsactual_file_nameat the override and never writes into the caller's buffer.Fix
Mirror the wide hook: point
actual_file_nameat our own narrowed override buffer, pass it toCreateFileA, and free it after the call. No write into the caller's buffer. PR attached.(Triggers on the ANSI
CreateFileApath withboot_config_overrideset to a path longer than the game's boot.config path.)