[File Locksmith] Fix IPC text-mode file I/O corrupting Unicode paths#47361
Conversation
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/b774efcd-c022-4218-adda-51334e1699bb Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses a File Locksmith IPC reliability issue where ipc::Writer writes raw UTF-16 bytes to last-run.log, but the file was opened in Windows text mode, allowing CRT \n translation to corrupt any UTF-16 code unit containing a 0x0A low byte. The fix switches the writer stream to binary mode so bytes are written exactly as produced.
Changes:
- Open
ipc::Writer’sstd::ofstreamwithstd::ios::binaryto prevent text-mode newline translation and preserve UTF-16 byte sequences.
| try | ||
| { | ||
| m_stream = std::ofstream(path); | ||
| m_stream = std::ofstream(path, std::ios::binary); |
There was a problem hiding this comment.
Added an explicit is_open() check in ipc::Writer::start() after opening the stream in binary mode, so open failures now return E_FAIL instead of incorrectly returning S_OK.
| try | ||
| { | ||
| m_stream = std::ofstream(path); | ||
| m_stream = std::ofstream(path, std::ios::binary); |
There was a problem hiding this comment.
Found the other last-run.log write path in src/modules/FileLocksmith/FileLocksmithLibInterop/NativeMethods.cpp (NativeMethods::StartAsElevated) and switched that std::ofstream open to std::ios::binary as well.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@copilot please re-review the latest changes. |
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/8a7cf963-d3b3-4b05-9f74-0553410f6d59 Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
Re-review complete. The two previous review comments (binary mode on The re-review also surfaced a symmetric reader-side bug: Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
| try | ||
| { | ||
| m_stream = std::ofstream(path); | ||
| m_stream = std::ofstream(path, std::ios::binary); |
There was a problem hiding this comment.
Added an explicit is_open() check in ipc::Writer::start() after opening the stream in binary mode, so open failures now return E_FAIL instead of incorrectly returning S_OK.
| try | ||
| { | ||
| m_stream = std::ofstream(path); | ||
| m_stream = std::ofstream(path, std::ios::binary); |
There was a problem hiding this comment.
Found the other last-run.log write path in src/modules/FileLocksmith/FileLocksmithLibInterop/NativeMethods.cpp (NativeMethods::StartAsElevated) and switched that std::ofstream open to std::ios::binary as well.
Summary of the Pull Request
The File Locksmith IPC layer reads and writes raw UTF-16 (WCHAR) bytes to
last-run.log, but all three stream opens were using the default text mode. On Windows, the CRT translates0x0Abytes to0x0D 0x0Aon write and collapses0x0D 0x0Aback to0x0Aon read. Because each WCHAR is 2 bytes, any code unit whose little-endian byte pair contains0x0Ain the low position (e.g.U+010A,U+0A0D) is silently corrupted. The fix opens all three streams in binary mode and adds an explicit open-failure guard.PR Checklist
Detailed Description of the Pull Request / Additional comments
Three targeted changes across two files:
FileLocksmithLib/IPC.cpp—Writer::start(): switchedstd::ofstreamfrom text to binary mode and added anis_open()check that returnsE_FAILimmediately when the file cannot be opened (previously the try/catch did not catch a silent open failure becausestd::ofstreamdoes not throw by default).FileLocksmithLibInterop/NativeMethods.cpp—StartAsElevated(): switchedstd::ofstreamfrom text to binary mode. This is the elevated-restart writer path; without this fix, Unicode corruption persisted when File Locksmith relaunched as administrator.FileLocksmithLibInterop/NativeMethods.cpp—ReadPathsFromFile(): switchedstd::ifstreamfrom text to binary mode. This is the symmetric reader-side bug — even with both writers corrected, the CRT text-mode reader could collapse a0x0D 0x0Abyte pair (a valid UTF-16 LE code unit, e.g. U+0A0D GURMUKHI EK ONKAR) into a single byte, desynchronising the 2-bytes-at-a-time read loop and corrupting all subsequent path data.No behaviour change for purely ASCII paths. Paths containing Unicode code points whose little-endian UTF-16 byte pair spans
0x0D 0x0Awere silently corrupted in all three code paths before this fix.Validation Steps Performed
ofstreamwriter inIPC.cpp,ofstreamwriter inNativeMethods.cpp,ifstreamreader inNativeMethods.cpp) now usestd::ios::binary, making write and read paths byte-exact and symmetric.std::ios::binarysuppresses Windows CRT\n↔\r\ntranslation; the delimiterL'\n'(LE bytes0x0A 0x00) is unambiguous in binary mode and is handled correctly by the existing read loop.