Invalid memory access in SystemTools::ConvertToUnixSlashes
Found while running a fuzz test for cmake.
Paths of the form `~foo` cause an invalid memory access in the `ConvertToUnixSlashes` function which is used in many places. https://gitlab.kitware.com/cmake/cmake/-/blob/v3.31.5/Source/kwsys/SystemTools.cxx#L2239
Can be reproduced with the simple test case
```
diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx
index 9275043141..8e518a654e 100644
--- a/Source/kwsys/testSystemTools.cxx
+++ b/Source/kwsys/testSystemTools.cxx
@@ -1223,6 +1223,9 @@ int testSystemTools(int, char*[])
if (kwsys::SystemTools::GetEnv("HOME", output)) {
output += "/foo bar/lala";
res &= CheckConvertToUnixSlashes("~/foo bar/lala", output);
+
+ std::string path = "~" + std::string(100, 'a'); // avoid SSO
+ res &= CheckConvertToUnixSlashes(path, path);
}
```
When compiled with address sanitizer the test gives the following output:
```
Test project /home/simon/git/cmake/build/Source/kwsys
Start 5: kwsys.testSystemTools
1/1 Test #5: kwsys.testSystemTools ............***Failed 0.28 sec
=================================================================
==3215139==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50b0000000ef at pc 0x55bac710564a bp 0x7ffec0950210 sp 0x7ffec0950208
READ of size 1 at 0x50b0000000ef thread T0
#0 0x55bac7105649 in cmsys::SystemTools::ConvertToUnixSlashes(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /home/simon/git/cmake/Source/kwsys/SystemTools.cxx:2134:18
#1 0x55bac70d4132 in CheckConvertToUnixSlashes(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /home/simon/git/cmake/Source/kwsys/testSystemTools.cxx:63:3
#2 0x55bac70d37f9 in testSystemTools(int, char**) /home/simon/git/cmake/Source/kwsys/testSystemTools.cxx:1228:12
#3 0x55bac70d2090 in main /home/simon/git/cmake/build/Source/kwsys/cmsysTestsCxx.cxx:241:14
#4 0x7f1cabe2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#5 0x7f1cabe2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
#6 0x55bac6ff6064 in _start (/home/simon/git/cmake/build/Source/kwsys/cmsysTestsCxx+0x44064) (BuildId: 8d7a7613d828082eb93e65174e0c7a6592dc25dc)
0x50b0000000ef is located 1 bytes before 102-byte region [0x50b0000000f0,0x50b000000156)
allocated by thread T0 here:
#0 0x55bac70cf4d1 in operator new(unsigned long) (/home/simon/git/cmake/build/Source/kwsys/cmsysTestsCxx+0x11d4d1) (BuildId: 8d7a7613d828082eb93e65174e0c7a6592dc25dc)
#1 0x7f1cac36b85a in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct<char*>(char*, char*, std::forward_iterator_tag) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/bits/basic_string.tcc:229:13
#2 0x55bac70d4129 in CheckConvertToUnixSlashes(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /home/simon/git/cmake/Source/kwsys/testSystemTools.cxx:62:24
#3 0x55bac70d37f9 in testSystemTools(int, char**) /home/simon/git/cmake/Source/kwsys/testSystemTools.cxx:1228:12
#4 0x55bac70d2090 in main /home/simon/git/cmake/build/Source/kwsys/cmsysTestsCxx.cxx:241:14
#5 0x7f1cabe2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#6 0x7f1cabe2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
#7 0x55bac6ff6064 in _start (/home/simon/git/cmake/build/Source/kwsys/cmsysTestsCxx+0x44064) (BuildId: 8d7a7613d828082eb93e65174e0c7a6592dc25dc)
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/simon/git/cmake/Source/kwsys/SystemTools.cxx:2134:18 in cmsys::SystemTools::ConvertToUnixSlashes(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&)
Shadow bytes around the buggy address:
0x50affffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x50affffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x50afffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x50afffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x50b000000000: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
=>0x50b000000080: fd fd fd fd fd fa fa fa fa fa fa fa fa[fa]00 00
0x50b000000100: 00 00 00 00 00 00 00 00 00 00 06 fa fa fa fa fa
0x50b000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x50b000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x50b000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x50b000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==3215139==ABORTING
```
issue