Handle unexpected BOM when reading from stdin in wixnative.exe
In certain cases, the encoding of stdin/stdout can be changed in Windows to cause a BOM to be added on the first read of stdin. To handle the unexpected BOM, the first line read from stdin is now a "preamble" where the last character is expected to be a ":". We use the last character because a multibyte BOM value may appear before it. Also updated SAL annotations to resolve IDE warnings.
Rob Mensching committed
Dec 2, 2021 at 19:57 UTC
fd5138896d596a6f3226a5c9438e456d4c8c2068
7 files changed
+57
-29
src/wix/WixToolset.Core.Native/Cabinet.cs
-13
@@ -57,19 +57,6 @@ namespace WixToolset.Core.Native
57
}
58
59
wixnative.Run();
60
-
61
-#if TOOD_ERROR_HANDLING
62
- catch (COMException ce)
63
- {
64
- // If we get a "the file exists" error, we must have a full temp directory - so report the issue
65
- if (0x80070050 == unchecked((uint)ce.ErrorCode))
66
- {
67
- throw new WixException(WixErrors.FullTempDirectory("WSC", Path.GetTempPath()));
68
- }
69
-
70
- throw;
71
- }
72
-#endif
60
}
61
62
/// <summary>
src/wix/WixToolset.Core.Native/WixNativeExe.cs
+3
-1
@@ -4,7 +4,6 @@ namespace WixToolset.Core.Native
4
{
5
using System;
6
using System.Collections.Generic;
7
- using System.ComponentModel;
7
using System.Diagnostics;
8
using System.IO;
9
@@ -54,6 +53,9 @@ namespace WixToolset.Core.Native
53
process.BeginOutputReadLine();
54
process.BeginErrorReadLine();
55
56
+ // Send the stdin preamble.
57
+ process.StandardInput.WriteLine(":");
58
+
59
if (this.stdinLines.Count > 0)
60
{
61
foreach (var line in this.stdinLines)
src/wix/wixnative/extractcab.cpp
+4
-4
@@ -2,12 +2,12 @@
2
3
#include "precomp.h"
4
5
-static HRESULT ProgressCallback(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext);
5
+static HRESULT ProgressCallback(__in BOOL fBeginFile, __in_z LPCWSTR wzFileId, __in_opt LPVOID pvContext);
6
7
8
HRESULT ExtractCabCommand(
9
__in int argc,
10
- __in LPWSTR argv[]
10
+ __in_ecount(argc) LPWSTR argv[]
11
)
12
{
13
HRESULT hr = E_INVALIDARG;
@@ -37,8 +37,8 @@ LExit:
37
38
static HRESULT ProgressCallback(
39
__in BOOL fBeginFile,
40
- __in LPCWSTR wzFileId,
41
- __in LPVOID /*pvContext*/
40
+ __in_z LPCWSTR wzFileId,
41
+ __in_opt LPVOID /*pvContext*/
42
)
43
{
44
if (fBeginFile)
src/wix/wixnative/precomp.h
+6
-4
@@ -4,6 +4,7 @@
4
#include <windows.h>
5
#include <aclapi.h>
6
#include <mergemod.h>
7
+#include <strsafe.h>
8
9
#include "dutil.h"
10
#include "conutil.h"
@@ -13,7 +14,8 @@
14
#include "cabcutil.h"
15
#include "cabutil.h"
16
16
-HRESULT SmartCabCommand(int argc, LPWSTR argv[]);
17
-HRESULT ResetAclsCommand(int argc, LPWSTR argv[]);
18
-HRESULT EnumCabCommand(int argc, LPWSTR argv[]);
19
-HRESULT ExtractCabCommand(int argc, LPWSTR argv[]);
17
+HRESULT WixNativeReadStdinPreamble();
18
+HRESULT SmartCabCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]);
19
+HRESULT ResetAclsCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]);
20
+HRESULT EnumCabCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]);
21
+HRESULT ExtractCabCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]);
src/wix/wixnative/resetacls.cpp
+6
-1
@@ -2,7 +2,9 @@
2
3
#include "precomp.h"
4
5
-HRESULT ResetAclsCommand(int argc, LPWSTR argv[])
5
+HRESULT ResetAclsCommand(
6
+ __in int argc,
7
+ __in_ecount(argc) LPWSTR argv[])
8
{
9
Unused(argc);
10
Unused(argv);
@@ -24,6 +26,9 @@ HRESULT ResetAclsCommand(int argc, LPWSTR argv[])
26
ConsoleExitOnLastError(hr, CONSOLE_COLOR_RED, "failed to initialize ACL");
27
}
28
29
+ hr = WixNativeReadStdinPreamble();
30
+ ExitOnFailure(hr, "failed to read stdin preamble before resetting ACLs");
31
+
32
// Reset the existing security permissions on each provided file.
33
for (;;)
34
{
src/wix/wixnative/smartcab.cpp
+10
-6
@@ -1,14 +1,15 @@
1
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3
#include "precomp.h"
4
+#include "fileutil.h"
5
5
-static HRESULT CompressFiles(HANDLE hCab);
6
-static void __stdcall CabNamesCallback(LPWSTR wzFirstCabName, LPWSTR wzNewCabName, LPWSTR wzFileToken);
6
+static HRESULT CompressFiles(__in HANDLE hCab);
7
+static void __stdcall CabNamesCallback(__in_z LPWSTR wzFirstCabName, __in_z LPWSTR wzNewCabName, __in_z LPWSTR wzFileToken);
8
9
10
HRESULT SmartCabCommand(
11
__in int argc,
11
- __in LPWSTR argv[]
12
+ __in_ecount(argc) LPWSTR argv[]
13
)
14
{
15
HRESULT hr = E_INVALIDARG;
@@ -66,6 +67,9 @@ HRESULT SmartCabCommand(
67
68
if (uiFileCount > 0)
69
{
70
+ hr = WixNativeReadStdinPreamble();
71
+ ExitOnFailure(hr, "failed to read stdin preamble before smartcabbing");
72
+
73
hr = CompressFiles(hCab);
74
ExitOnFailure(hr, "failed to compress files into cabinet: %ls", wzCabPath);
75
}
@@ -151,9 +155,9 @@ LExit:
155
// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab"
156
// Third argument is the file token of the first file present in the splitting cabinet
157
static void __stdcall CabNamesCallback(
154
- __in LPWSTR wzFirstCabName,
155
- __in LPWSTR wzNewCabName,
156
- __in LPWSTR wzFileToken
158
+ __in_z LPWSTR wzFirstCabName,
159
+ __in_z LPWSTR wzNewCabName,
160
+ __in_z LPWSTR wzFileToken
161
)
162
{
163
ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls\t%ls\t%ls", wzFirstCabName, wzNewCabName, wzFileToken);
src/wix/wixnative/wixnative.cpp
+28
@@ -36,3 +36,31 @@ int __cdecl wmain(int argc, LPWSTR argv[])
36
ConsoleUninitialize();
37
return HRESULT_CODE(hr);
38
}
39
+
40
+HRESULT WixNativeReadStdinPreamble()
41
+{
42
+ HRESULT hr = S_OK;
43
+ LPWSTR sczLine = NULL;
44
+ size_t cchPreamble = 0;
45
+
46
+ // Read the first line to determine if a byte-order-mark was prepended to stdin.
47
+ // A byte-order-mark is not normally expected but has been seen in some CI/CD systems.
48
+ // The preable is a single line with ":".
49
+ hr = ConsoleReadW(&sczLine);
50
+ ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to read preamble from stdin");
51
+
52
+ hr = ::StringCchLengthW(sczLine, STRSAFE_MAX_CCH, &cchPreamble);
53
+ ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to get length of stdin preamble");
54
+
55
+ // Ensure the preamble ends with ":" and ignore anything before that (since it may be a BOM).
56
+ if (!cchPreamble || sczLine[cchPreamble - 1] != L':')
57
+ {
58
+ hr = E_INVALIDDATA;
59
+ ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "expected ':' as preamble on first line of stdin");
60
+ }
61
+
62
+LExit:
63
+ ReleaseStr(sczLine);
64
+
65
+ return hr;
66
+}