master
cpp 305 lines 9.4 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 LxssCreateProcess.cpp
8
9 Abstract:
10
11 This file contains process creation function definitions.
12
13 --*/
14
15 #include "precomp.h"
16 #include "LxssCreateProcess.h"
17
18 CreateLxProcessData LxssCreateProcess::ParseArguments(
19 _In_opt_ LPCSTR Filename,
20 _In_ ULONG CommandLineCount,
21 _In_reads_opt_(CommandLineCount) LPCSTR* CommandLine,
22 _In_opt_ LPCWSTR CurrentWorkingDirectory,
23 _In_opt_ LPCWSTR NtPath,
24 _In_reads_opt_(NtEnvironmentLength) PWCHAR NtEnvironment,
25 _In_ ULONG NtEnvironmentLength,
26 _In_opt_ LPCWSTR Username,
27 _In_ const std::vector<std::string>& DefaultEnvironment,
28 _In_ ULONG Flags)
29 {
30 THROW_HR_IF(E_INVALIDARG, (!ARGUMENT_PRESENT(Filename) && (CommandLineCount > 1)));
31 THROW_HR_IF(E_INVALIDARG, ((CommandLineCount != 0) && !CommandLine) || (CommandLineCount > USHORT_MAX));
32
33 // Convert the input strings to counted strings that reuse the existing
34 // buffer so the length of the strings is only computed once.
35
36 CreateLxProcessData Parsed{};
37 if (ARGUMENT_PRESENT(Filename))
38 {
39 Parsed.Filename = Filename;
40 THROW_HR_IF(E_INVALIDARG, Parsed.Filename.empty());
41 Parsed.CommandLine.reserve(CommandLineCount);
42 }
43 else if (CommandLineCount > 0)
44 {
45 Parsed.CommandLine.reserve(CommandLineCount + 1);
46 Parsed.CommandLine.emplace_back(std::string("-c"));
47 }
48
49 for (size_t Index = 0; Index < CommandLineCount; ++Index)
50 {
51 Parsed.CommandLine.emplace_back(std::string(CommandLine[Index]));
52 }
53
54 // Initialize the environment.
55
56 Parsed.Environment = DefaultEnvironment;
57
58 // Append the user's NT path if the configuration supports it.
59 //
60 // N.B. Failures to append user's NT path are non-fatal and errors are
61 // logged internally.
62
63 if ((ARGUMENT_PRESENT(NtPath)) && (LXSS_INTEROP_ENABLED(Flags)) && (WI_IsFlagSet(Flags, LXSS_DISTRO_FLAGS_APPEND_NT_PATH)))
64 {
65 Parsed.NtPath = wsl::shared::string::WideToMultiByte(NtPath);
66 }
67
68 // Validate that the environment is a NUL-NUL-terminated string.
69
70 if (ARGUMENT_PRESENT(NtEnvironment))
71 {
72 for (size_t Index = 0;;)
73 {
74 const PWCHAR Current = NtEnvironment + Index;
75 const size_t Length = wcsnlen(Current, NtEnvironmentLength - Index);
76 THROW_HR_IF(E_INVALIDARG, Length == NtEnvironmentLength - Index);
77 if (Length == 0)
78 {
79 break;
80 }
81
82 Parsed.NtEnvironment.push_back(wsl::shared::string::WideToMultiByte(Current));
83 Index += Length + 1;
84 }
85 }
86
87 // Translate the username to UTF-8.
88
89 if (ARGUMENT_PRESENT(Username))
90 {
91 Parsed.Username = wsl::shared::string::WideToMultiByte(Username);
92 }
93
94 // Initialize the current working directory.
95 //
96 // N.B. An empty current working directory means the user's home path will
97 // be used.
98
99 if (ARGUMENT_PRESENT(CurrentWorkingDirectory))
100 {
101 Parsed.CurrentWorkingDirectory = wsl::shared::string::WideToMultiByte(CurrentWorkingDirectory);
102 }
103
104 return Parsed;
105 }
106
107 // static function definitions
108 std::vector<gsl::byte> LxssCreateProcess::CreateMessage(_In_ LX_MESSAGE_TYPE MessageType, _In_ const CreateLxProcessData& CreateProcessData, _In_ ULONG DefaultUid)
109 {
110 //
111 // Compute the size of the total message starting with the base fields and
112 // adding in the strings.
113 //
114 // N.B. The filename and command line are optional.
115 //
116
117 size_t MessageSize;
118 switch (MessageType)
119 {
120 case LxInitMessageCreateProcess:
121 MessageSize = offsetof(LX_INIT_CREATE_PROCESS, Common.Buffer);
122 break;
123
124 case LxInitMessageCreateProcessUtilityVm:
125 MessageSize = offsetof(LX_INIT_CREATE_PROCESS_UTILITY_VM, Common.Buffer);
126 break;
127
128 default:
129 THROW_HR(E_INVALIDARG);
130 }
131
132 THROW_IF_FAILED(SizeTAdd(CreateProcessData.Filename.length() + 1, MessageSize, &MessageSize));
133
134 THROW_IF_FAILED(SizeTAdd(CreateProcessData.CurrentWorkingDirectory.length(), MessageSize, &MessageSize));
135
136 THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));
137
138 if (CreateProcessData.CommandLine.size() > 0)
139 {
140 for (size_t Index = 0; Index < CreateProcessData.CommandLine.size(); ++Index)
141 {
142 THROW_IF_FAILED(SizeTAdd(CreateProcessData.CommandLine[Index].length() + 1, MessageSize, &MessageSize));
143 }
144 }
145 else
146 {
147 THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));
148 }
149
150 WI_ASSERT(CreateProcessData.Environment.size() > 0);
151
152 for (size_t Index = 0; Index < CreateProcessData.Environment.size(); ++Index)
153 {
154 WI_ASSERT(CreateProcessData.Environment[Index].length() > 0);
155
156 THROW_IF_FAILED(SizeTAdd(CreateProcessData.Environment[Index].length(), MessageSize, &MessageSize));
157
158 THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));
159 }
160
161 if (CreateProcessData.NtEnvironment.size() > 0)
162 {
163 for (size_t Index = 0; Index < CreateProcessData.NtEnvironment.size(); ++Index)
164 {
165 WI_ASSERT(CreateProcessData.NtEnvironment[Index].length() > 0);
166
167 THROW_IF_FAILED(SizeTAdd(CreateProcessData.NtEnvironment[Index].length(), MessageSize, &MessageSize));
168
169 THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));
170 }
171 }
172 else
173 {
174 THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));
175 }
176
177 THROW_IF_FAILED(SizeTAdd(CreateProcessData.NtPath.length(), MessageSize, &MessageSize));
178
179 THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));
180
181 THROW_IF_FAILED(SizeTAdd(CreateProcessData.Username.length(), MessageSize, &MessageSize));
182
183 THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));
184
185 //
186 // Allocate the zero initialized buffer and populate the base fields.
187 //
188
189 THROW_HR_IF(E_INVALIDARG, MessageSize > ULONG_MAX);
190
191 std::vector<gsl::byte> Message(MessageSize);
192 const auto MessageSpan = gsl::make_span(Message);
193 auto* MessageHeader = gslhelpers::get_struct<MESSAGE_HEADER>(MessageSpan);
194 MessageHeader->MessageType = MessageType;
195 MessageHeader->MessageSize = gsl::narrow_cast<ULONG>(MessageSize);
196 gsl::span<gsl::byte> CommonSpan;
197 if (MessageType == LxInitMessageCreateProcess)
198 {
199 CommonSpan = MessageSpan.subspan(offsetof(LX_INIT_CREATE_PROCESS, Common));
200 }
201 else
202 {
203 CommonSpan = MessageSpan.subspan(offsetof(LX_INIT_CREATE_PROCESS_UTILITY_VM, Common));
204 }
205
206 //
207 // Populate the default UID.
208 //
209
210 auto* Common = gslhelpers::get_struct<LX_INIT_CREATE_PROCESS_COMMON>(CommonSpan);
211 Common->DefaultUid = DefaultUid;
212
213 //
214 // Populate the Filename string.
215 //
216
217 size_t Offset = offsetof(LX_INIT_CREATE_PROCESS_COMMON, Buffer);
218 Common->FilenameOffset = wsl::shared::string::CopyToSpan(CreateProcessData.Filename, CommonSpan, Offset);
219
220 //
221 // Populate the CurrentWorkingDirectory string.
222 //
223 // N.B. Checks for overflow were done earlier in this function.
224 //
225
226 Common->CurrentWorkingDirectoryOffset = wsl::shared::string::CopyToSpan(CreateProcessData.CurrentWorkingDirectory, CommonSpan, Offset);
227
228 //
229 // Populate the CommandLine strings.
230 //
231
232 WI_ASSERT(CreateProcessData.CommandLine.size() <= USHORT_MAX);
233
234 Common->CommandLineOffset = gsl::narrow_cast<ULONG>(Offset);
235 Common->CommandLineCount = gsl::narrow_cast<USHORT>(CreateProcessData.CommandLine.size());
236 if (Common->CommandLineCount > 0)
237 {
238 for (USHORT Index = 0; Index < Common->CommandLineCount; ++Index)
239 {
240 wsl::shared::string::CopyToSpan(CreateProcessData.CommandLine[Index], CommonSpan, Offset);
241 }
242 }
243 else
244 {
245 Offset += 1;
246 }
247
248 //
249 // Populate the Environment strings.
250 //
251
252 WI_ASSERT(CreateProcessData.Environment.size() <= USHORT_MAX);
253
254 Common->EnvironmentOffset = gsl::narrow_cast<ULONG>(Offset);
255 Common->EnvironmentCount = gsl::narrow_cast<USHORT>(CreateProcessData.Environment.size());
256 for (size_t Index = 0; Index < CreateProcessData.Environment.size(); ++Index)
257 {
258 wsl::shared::string::CopyToSpan(CreateProcessData.Environment[Index], CommonSpan, Offset);
259 }
260
261 //
262 // Populate the NtEnvironment strings.
263 //
264
265 WI_ASSERT(CreateProcessData.NtEnvironment.size() <= USHORT_MAX);
266
267 Common->NtEnvironmentOffset = gsl::narrow_cast<ULONG>(Offset);
268 Common->NtEnvironmentCount = gsl::narrow_cast<USHORT>(CreateProcessData.NtEnvironment.size());
269 if (Common->NtEnvironmentCount > 0)
270 {
271 for (USHORT Index = 0; Index < Common->NtEnvironmentCount; ++Index)
272 {
273 wsl::shared::string::CopyToSpan(CreateProcessData.NtEnvironment[Index], CommonSpan, Offset);
274 }
275 }
276 else
277 {
278 Offset += 1;
279 }
280
281 //
282 // Populate the shell options.
283 //
284
285 Common->ShellOptions = CreateProcessData.ShellOptions;
286
287 //
288 // Populate the NtPath string.
289 //
290
291 Common->NtPathOffset = wsl::shared::string::CopyToSpan(CreateProcessData.NtPath, CommonSpan, Offset);
292
293 //
294 // Populate the Username string.
295 //
296
297 Common->UsernameOffset = wsl::shared::string::CopyToSpan(CreateProcessData.Username, CommonSpan, Offset);
298
299 WI_ASSERT(
300 ((MessageType == LxInitMessageCreateProcess) && (MessageSize == (Offset + offsetof(LX_INIT_CREATE_PROCESS, Common)))) ||
301 ((MessageType == LxInitMessageCreateProcessUtilityVm) &&
302 (MessageSize == (Offset + offsetof(LX_INIT_CREATE_PROCESS_UTILITY_VM, Common)))));
303
304 return Message;
305 }