1 //
2 // spawnv.cpp
3 //
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines the -v and -ve flavors of the _exec() and _spawn() functions.
7 //
8 // There are many varieties of the _exec() and _spawn() functions. A high level
9 // summary of the behavioral differences is as follows:
10 //
11 // All of these functions execute a new process. The _spawn() functions all take
12 // a 'mode' parameter, which specifies the way in which the process is started.
13 // The 'mode' must be one of the _P_-prefixed modes from <process.h>. The _exec()
14 // family of functions execute a new process, then call _exit() to terminate the
15 // calling process. Each _exit() function is equivalent to the corresponding
16 // _spawn() function with the _P_OVERLAY mode.
17 //
18 // There are eight variants each of _exec() and _spawn(), suffixed with -l, -le,
19 // -lp, -lpe, -v, -ve, -vp, and -vpe.
20 //
21 // If a function has 'e' in its suffix, it accepts an environment with which to
22 // create the new process. If a function does not have 'e' in its suffix, it
23 // does not accept an environment and instead uses the environment of the calling
24 // process. A call to an 'e' function with a null environment argument has the
25 // same effect as calling the non-'e'-suffixed equivalent.
26 //
27 // Each _exec() or _spawn() function has either an 'l' or 'v' suffix. These have
28 // equivalent functionality; they differ only in how they accept their arguments.
29 // The 'l'-suffixed functions accept the command-line arguments and the environment
30 // as varargs. There must be at least one command line argument (conventionally
31 // the name of the program to be executed). If the function accepts an environment
32 // (if it is 'e'-suffixed), then there must be a null pointer between the last
33 // argument and the first environment variable. The arguments are terminated by a
34 // null pointer.
35 //
36 // The 'v'-suffixed functions accept a pair of pointers: one to the argv vector
37 // and one to the envp vector. Each is an array of pointers terminated by a null
38 // pointer, similar to how arguments and the environment are passed to main().
39 //
40 // Finally, if a function has a 'p' in its suffix, and if the provided executable
41 // file name is just a file name (and does not contain any path component), the
42 // %PATH% is searched for an executable with the given name. If a function does
43 // not have a 'p' suffix, then the environment is not searched; the executable
44 // must be found simply by passing its name to CreateProcess.
45 //
46 // All functions return -1 and set errno on failure. On success, the _exec()
47 // functions do not return. On success, the _spawn() functions return different
48 // things, depending on the provided mode. See the CreateProcess invocation
49 // logic in this file for details.
50 //
51 // Note that the only supported modes are wait and overlay.
52 //
53 // These functions may set errno to one of the following values:
54 // * E2BIG: Failed in argument or environment processing because the argument
55 // list or environment is too large.
56 // * EACCESS: Locking or sharing violation on a file
57 // * EMFILE: Too many files open
58 // * ENOENT: Failed to find the program (no such file or directory)
59 // * ENOEXEC: Failed in a call to exec() due to a bad executable format
60 // * ENOMEM: Failed to allocate memory required for the spawn operation
61 // * EINVAL: Invalid mode argumnt or process state for spawn (note that most
62 // invalid arguments cause the invalid parameter handler to be
63 // invoked).
64 //
65 #include <corecrt_internal.h>
66 #include <corecrt_internal_lowio.h>
67 #include <corecrt_internal_traits.h>
68 #include <errno.h>
69 #include <io.h>
70 #include <mbstring.h>
71 #include <process.h>
72 #include <stdlib.h>
73 #include <string.h>
74
75
76
77 // Accumulates the inheritable file handles into *data, in the structure expected
78 // by the spawnee (see the lowio initialization code for the logic that decodes
79 // this data structure). On success, *data and *size have the handle data array
80 // and the size of the handle data, and true is returned. The caller must free
81 // *data. On failure, false is returned and errno is set.
accumulate_inheritable_handles(BYTE ** const data,size_t * const size,bool const include_std_handles)82 static bool accumulate_inheritable_handles(
83 BYTE** const data,
84 size_t* const size,
85 bool const include_std_handles
86 ) throw()
87 {
88 return __acrt_lock_and_call(__acrt_lowio_index_lock, [&]() -> bool
89 {
90 *data = nullptr;
91 *size = 0;
92
93 // Count the number of handles to be inherited:
94 size_t handle_count = 0;
95 for (handle_count = _nhandle; handle_count != 0 && _osfile(handle_count - 1) != 0; --handle_count)
96 {
97 }
98
99 size_t const max_handle_count = (USHRT_MAX - sizeof(int)) / (sizeof(char) + sizeof(intptr_t));
100 _VALIDATE_RETURN_NOEXC(handle_count < max_handle_count, ENOMEM, false);
101
102 size_t const handle_data_header_size = sizeof(int);
103 size_t const handle_data_element_size = sizeof(char) + sizeof(intptr_t);
104
105 unsigned short const handle_data_size = static_cast<unsigned short>(
106 handle_data_header_size +
107 handle_count * handle_data_element_size);
108
109 __crt_unique_heap_ptr<BYTE> handle_data(_calloc_crt_t(BYTE, handle_data_size));
110 _VALIDATE_RETURN_NOEXC(handle_data.get() != nullptr, ENOMEM, false);
111
112 // Set the handle count in the data:
113 *reinterpret_cast<int*>(handle_data.get()) = static_cast<int>(handle_count);
114
115 auto const first_flags = reinterpret_cast<char*>(handle_data.get() + sizeof(int));
116 auto const first_handle = reinterpret_cast<intptr_t UNALIGNED*>(first_flags + (handle_count * sizeof(char)));
117
118 // Copy the handle data:
119 auto flags_it = first_flags;
120 auto handle_it = first_handle;
121 for (size_t i = 0; i != handle_count; ++i, ++flags_it, ++handle_it)
122 {
123 __crt_lowio_handle_data* const pio = _pioinfo(i);
124 if ((pio->osfile & FNOINHERIT) == 0)
125 {
126 *flags_it = pio->osfile;
127 *handle_it = pio->osfhnd;
128 }
129 else
130 {
131 *flags_it = 0;
132 *handle_it = reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE);
133 }
134 }
135
136 // Exclude the first three handles (stdin, stdout, stderr) if asked:
137 if (!include_std_handles)
138 {
139 flags_it = first_flags;
140 handle_it = first_handle;
141 for (size_t i = 0; i != __min(handle_count, 3); ++i, ++flags_it, ++handle_it)
142 {
143 *flags_it = 0;
144 *handle_it = reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE);
145 }
146 }
147
148 *data = handle_data.detach();
149 *size = handle_data_size;
150 return true;
151 });
152 }
153
154
155
should_create_unicode_environment(char)156 static bool __cdecl should_create_unicode_environment(char) throw() { return false; }
should_create_unicode_environment(wchar_t)157 static bool __cdecl should_create_unicode_environment(wchar_t) throw() { return true; }
158
159
160
161 // Spawns a child process. The mode must be one of the _P-modes from <process.h>.
162 // The return value depends on the mode:
163 // * _P_OVERLAY: On success, calls _exit() and does not return. Returns -1 on failure.
164 // * _P_WAIT: Returns (termination_code << 8 + result_code)
165 // * _P_DETACH: Returns 0 on success; -1 on failure
166 // * Others: Returns a handle to the process. The caller must close the handle.
167 template <typename Character>
execute_command(int const mode,Character const * const file_name,Character const * const * const arguments,Character const * const * const environment)168 static intptr_t __cdecl execute_command(
169 int const mode,
170 Character const* const file_name,
171 Character const* const* const arguments,
172 Character const* const* const environment
173 ) throw()
174 {
175 typedef __crt_char_traits<Character> traits;
176
177 _VALIDATE_RETURN(file_name != nullptr, EINVAL, -1);
178 _VALIDATE_RETURN(arguments != nullptr, EINVAL, -1);
179
180 _VALIDATE_CLEAR_OSSERR_RETURN(mode >= 0 && mode <= _P_DETACH, EINVAL, -1);
181
182 __crt_unique_heap_ptr<Character> command_line;
183 __crt_unique_heap_ptr<Character> environment_block;
184 if (traits::pack_command_line_and_environment(
185 arguments,
186 environment,
187 command_line.get_address_of(),
188 environment_block.get_address_of()
189 ) == -1)
190 {
191 return -1;
192 }
193
194 __crt_unique_heap_ptr<BYTE> handle_data;
195 size_t handle_data_size;
196 if (!accumulate_inheritable_handles(handle_data.get_address_of(), &handle_data_size, mode != _P_DETACH))
197 return -1;
198
199 DWORD creation_flags = 0;
200 if (mode == _P_DETACH)
201 creation_flags |= DETACHED_PROCESS;
202
203 if (should_create_unicode_environment(Character()))
204 creation_flags |= CREATE_UNICODE_ENVIRONMENT;
205
206 _doserrno = 0;
207
208 STARTUPINFOW startup_info = { };
209 startup_info.cb = sizeof(startup_info);
210 startup_info.cbReserved2 = static_cast<WORD>(handle_data_size);
211 startup_info.lpReserved2 = handle_data.get();
212
213 PROCESS_INFORMATION process_info;
214 BOOL const create_process_status = traits::create_process(
215 const_cast<Character*>(file_name),
216 command_line.get(),
217 nullptr,
218 nullptr,
219 TRUE,
220 creation_flags,
221 environment_block.get(),
222 nullptr,
223 &startup_info,
224 &process_info);
225
226 __crt_unique_handle process_handle(process_info.hProcess);
227 __crt_unique_handle thread_handle(process_info.hThread);
228
229 if (!create_process_status)
230 {
231 __acrt_errno_map_os_error(GetLastError());
232 return -1;
233 }
234
235 if (mode == _P_OVERLAY)
236 {
237 // Destroy ourselves:
238 _exit(0);
239 }
240 else if (mode == _P_WAIT)
241 {
242 WaitForSingleObject(process_info.hProcess, static_cast<DWORD>(-1));
243
244 // Return the termination code and exit code. Note that we return
245 // the full exit code.
246 DWORD exit_code;
247 if (0 != GetExitCodeProcess(process_info.hProcess, &exit_code))
248 {
249 return static_cast<int>(exit_code);
250 }
251 else
252 {
253 __acrt_errno_map_os_error(GetLastError());
254 return -1;
255 }
256 }
257 else if (mode == _P_DETACH)
258 {
259 /* like totally detached asynchronous spawn, dude,
260 close process handle, return 0 for success */
261 return 0;
262 }
263 else
264 {
265 // Asynchronous spawn: return process handle:
266 return reinterpret_cast<intptr_t>(process_handle.detach());
267 }
268 }
269
270
271
272 template <typename Character>
common_spawnv(int const mode,Character const * const file_name,Character const * const * const arguments,Character const * const * const environment)273 static intptr_t __cdecl common_spawnv(
274 int const mode,
275 Character const* const file_name,
276 Character const* const* const arguments,
277 Character const* const* const environment
278 ) throw()
279 {
280 typedef __crt_char_traits<Character> traits;
281
282 _VALIDATE_RETURN(file_name != nullptr, EINVAL, -1);
283 _VALIDATE_RETURN(file_name[0] != '\0', EINVAL, -1);
284 _VALIDATE_RETURN(arguments != nullptr, EINVAL, -1);
285 _VALIDATE_RETURN(arguments[0] != nullptr, EINVAL, -1);
286 _VALIDATE_RETURN(arguments[0][0] != '\0', EINVAL, -1);
287
288 Character const* const final_backslash = traits::tcsrchr(file_name, '\\');
289 Character const* const final_slash = traits::tcsrchr(file_name, '/');
290
291 Character const* mutated_file_name = file_name;
292 Character const* end_of_directory = final_backslash;
293 if (!final_slash)
294 {
295 if (!final_backslash)
296 {
297 Character const* const final_colon = traits::tcsrchr(file_name, ':');
298 if (final_colon)
299 {
300 end_of_directory = final_colon;
301 }
302 else
303 {
304 // The path is a file name only. We force it to be a relative
305 // path name.
306 size_t const file_name_size = traits::tcslen(file_name) + 3;
307 __crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, file_name_size));
308 if (!buffer)
309 return -1;
310
311 static Character const dot_slash[] = { '.', '\\', '\0' };
312 _ERRCHECK(traits::tcscpy_s(buffer.get(), file_name_size, dot_slash));
313 _ERRCHECK(traits::tcscat_s(buffer.get(), file_name_size, file_name));
314
315 mutated_file_name = buffer.detach();
316 end_of_directory = mutated_file_name + 2; // Adjust for ".\"
317 }
318 }
319 }
320 else if (!final_backslash || final_slash > final_backslash)
321 {
322 end_of_directory = final_slash;
323 }
324
325 // If we allocated a file name above, make sure we clean it up:
326 __crt_unique_heap_ptr<Character const> const mutated_file_name_cleanup(file_name == mutated_file_name
327 ? nullptr
328 : mutated_file_name);
329
330 if (traits::tcsrchr(end_of_directory, '.'))
331 {
332 // If an extension was provided, just invoke the path:
333 if (traits::taccess_s(mutated_file_name, 0) == 0)
334 {
335 return execute_command(mode, mutated_file_name, arguments, environment);
336 }
337 }
338 else
339 {
340 // If no extension was provided, try known executable extensions:
341 size_t const buffer_size = traits::tcslen(mutated_file_name) + 5;
342 __crt_unique_heap_ptr<Character> const buffer(_calloc_crt_t(Character, buffer_size));
343 if (!buffer)
344 return -1;
345
346 _ERRCHECK(traits::tcscpy_s(buffer.get(), buffer_size, mutated_file_name));
347 Character* extension_buffer = buffer.get() + buffer_size - 5;
348
349 typedef Character const extension_type[5];
350 static extension_type const extensions[4] =
351 {
352 { '.', 'c', 'o', 'm', '\0' },
353 { '.', 'e', 'x', 'e', '\0' },
354 { '.', 'b', 'a', 't', '\0' },
355 { '.', 'c', 'm', 'd', '\0' }
356 };
357
358 errno_t const saved_errno = errno;
359
360 extension_type const* const first_extension = extensions;
361 extension_type const* const last_extension = first_extension + _countof(extensions);
362 for (auto it = first_extension; it != last_extension; ++it)
363 {
364 _ERRCHECK(traits::tcscpy_s(extension_buffer, 5, *it));
365
366 if (traits::taccess_s(buffer.get(), 0) == 0)
367 {
368 errno = saved_errno;
369 return execute_command(mode, buffer.get(), arguments, environment);
370 }
371 }
372 }
373
374 return -1;
375 }
376
377
378
_execv(char const * const file_name,char const * const * const arguments)379 extern "C" intptr_t __cdecl _execv(
380 char const* const file_name,
381 char const* const* const arguments
382 )
383 {
384 return common_spawnv(_P_OVERLAY, file_name, arguments, static_cast<char const* const*>(nullptr));
385 }
386
_execve(char const * const file_name,char const * const * const arguments,char const * const * const environment)387 extern "C" intptr_t __cdecl _execve(
388 char const* const file_name,
389 char const* const* const arguments,
390 char const* const* const environment
391 )
392 {
393 return common_spawnv(_P_OVERLAY, file_name, arguments, environment);
394 }
395
_spawnv(int const mode,char const * const file_name,char const * const * const arguments)396 extern "C" intptr_t __cdecl _spawnv(
397 int const mode,
398 char const* const file_name,
399 char const* const* const arguments
400 )
401 {
402 return common_spawnv(mode, file_name, arguments, static_cast<char const* const*>(nullptr));
403 }
404
_spawnve(int const mode,char const * const file_name,char const * const * const arguments,char const * const * const environment)405 extern "C" intptr_t __cdecl _spawnve(
406 int const mode,
407 char const* const file_name,
408 char const* const* const arguments,
409 char const* const* const environment
410 )
411 {
412 return common_spawnv(mode, file_name, arguments, environment);
413 }
414
415
416
_wexecv(wchar_t const * const file_name,wchar_t const * const * const arguments)417 extern "C" intptr_t __cdecl _wexecv(
418 wchar_t const* const file_name,
419 wchar_t const* const* const arguments
420 )
421 {
422 return common_spawnv(_P_OVERLAY, file_name, arguments, static_cast<wchar_t const* const*>(nullptr));
423 }
424
_wexecve(wchar_t const * const file_name,wchar_t const * const * const arguments,wchar_t const * const * const environment)425 extern "C" intptr_t __cdecl _wexecve(
426 wchar_t const* const file_name,
427 wchar_t const* const* const arguments,
428 wchar_t const* const* const environment
429 )
430 {
431 return common_spawnv(_P_OVERLAY, file_name, arguments, environment);
432 }
433
_wspawnv(int const mode,wchar_t const * const file_name,wchar_t const * const * const arguments)434 extern "C" intptr_t __cdecl _wspawnv(
435 int const mode,
436 wchar_t const* const file_name,
437 wchar_t const* const* const arguments
438 )
439 {
440 return common_spawnv(mode, file_name, arguments, static_cast<wchar_t const* const*>(nullptr));
441 }
442
_wspawnve(int const mode,wchar_t const * const file_name,wchar_t const * const * const arguments,wchar_t const * const * const environment)443 extern "C" intptr_t __cdecl _wspawnve(
444 int const mode,
445 wchar_t const* const file_name,
446 wchar_t const* const* const arguments,
447 wchar_t const* const* const environment
448 )
449 {
450 return common_spawnv(mode, file_name, arguments, environment);
451 }
452