From 7cd481e4c339f28f69ad5b947722243d87f2b8fc Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Fri, 15 May 2026 13:49:27 +0300 Subject: [PATCH] os: fix read_file crash on Windows device files (TCC/GCC) When reading device files like \\.\nul on Windows, fseek succeeds but ftell returns -1. The old logic only checked for negative ftell when fseek also failed, so -1 was passed to fread as SIZE_MAX, crashing MinGW CRT. Return 0 to use the streaming fallback path instead. Also fix Windows MSVC CI failures: - cc_test.v: compare import lib filename instead of full path to avoid Windows path normalization issues in live reload linker args tests - coutput_test.v: skip `-cc clang` tests on msvc-windows (same as tcc-windows) since clang-built V binaries crash on these CI runners - repl_time_state_test.v: clear VFLAGS before starting REPL subprocess so fake cmd.exe in replfolder doesn't break MSVC detection --- vlib/os/os.c.v | 9 +++++++-- vlib/v/builder/cc_test.v | 6 ++++-- vlib/v/gen/c/coutput_test.v | 2 +- vlib/v/slow_tests/repl/repl_time_state_test.v | 3 +++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/vlib/os/os.c.v b/vlib/os/os.c.v index d496a604c..1ca3052a9 100644 --- a/vlib/os/os.c.v +++ b/vlib/os/os.c.v @@ -107,8 +107,13 @@ fn find_cfile_size(fp &C.FILE) !int { if raw_fsize != 0 && cseek != 0 { return error('fseek failed') } - if cseek != 0 && raw_fsize < 0 { - return error('ftell failed') + if raw_fsize < 0 { + if cseek != 0 { + return error('ftell failed') + } + // fseek succeeded but ftell returned -1 (e.g. device files like NUL on Windows). + // Treat as zero-size to use the streaming fallback path. + return 0 } len := int(raw_fsize) // For files > 2GB, C.ftell can return values that, when cast to `int`, can result in values below 0. diff --git a/vlib/v/builder/cc_test.v b/vlib/v/builder/cc_test.v index 801523d09..464b0ccfd 100644 --- a/vlib/v/builder/cc_test.v +++ b/vlib/v/builder/cc_test.v @@ -419,7 +419,8 @@ fn test_live_windows_main_linker_args_export_host_symbols() { ], .gcc) assert linker_args.contains('-Wl,--export-all-symbols') assert linker_args.contains('-Wl,--out-implib,') - assert normalized_test_path(linker_args).contains(normalized_test_path(live_windows_import_lib_path(hot_reload_graph_example()))) + expected_import_lib := os.file_name(live_windows_import_lib_path(hot_reload_graph_example())) + assert linker_args.contains(expected_import_lib), 'linker_args should contain ${expected_import_lib}' } fn test_live_windows_shared_linker_args_include_host_import_lib() { @@ -432,7 +433,8 @@ fn test_live_windows_shared_linker_args_include_host_import_lib() { '-shared', hot_reload_graph_example(), ], .gcc) - assert normalized_test_path(linker_args).contains(normalized_test_path(live_windows_import_lib_path(hot_reload_graph_example()))) + expected_import_lib := os.file_name(live_windows_import_lib_path(hot_reload_graph_example())) + assert linker_args.contains(expected_import_lib), 'linker_args should contain ${expected_import_lib}' } fn test_windows_cross_compile_args_match_shared_prod_args() { diff --git a/vlib/v/gen/c/coutput_test.v b/vlib/v/gen/c/coutput_test.v index 8ad9165ec..534401109 100644 --- a/vlib/v/gen/c/coutput_test.v +++ b/vlib/v/gen/c/coutput_test.v @@ -740,7 +740,7 @@ fn should_skip(relpath string) bool { return true } } - if github_job == 'tcc-windows' { + if github_job in ['tcc-windows', 'msvc-windows'] { test_path := os.join_path(vroot, relpath) file_options := get_file_options(test_path) if file_options.vflags.contains('-cc clang') { diff --git a/vlib/v/slow_tests/repl/repl_time_state_test.v b/vlib/v/slow_tests/repl/repl_time_state_test.v index 21c8ab7f9..ca1995a26 100644 --- a/vlib/v/slow_tests/repl/repl_time_state_test.v +++ b/vlib/v/slow_tests/repl/repl_time_state_test.v @@ -69,8 +69,11 @@ fn test_repl_bypasses_local_cmd_exe_on_windows() { assert build_fake_cmd.exit_code == 0, build_fake_cmd.output original_vexe := os.getenv('VEXE') os.setenv('VEXE', vexec, true) + original_vflags := os.getenv('VFLAGS') + os.setenv('VFLAGS', '', true) defer { os.setenv('VEXE', original_vexe, true) + os.setenv('VFLAGS', original_vflags, true) } mut repl := os.new_process(vexec) repl.set_args(['repl', '-replfolder', temp_dir, '-replprefix', 'cmd_bypass.']) -- 2.39.5