1 //
2 // cenvarg.cpp
3 //
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines the _cenvarg() and _capture_argv functions, which transform argument
7 // vectors and environments for use by the _exec() and _spawn() functions.
8 //
9 #include <corecrt_internal.h>
10 #include <errno.h>
11 #include <corecrt_internal_traits.h>
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #pragma warning(disable:__WARNING_POSTCONDITION_NULLTERMINATION_VIOLATION) // 26036
18
19
20 // Converts a main()-style argv arguments vector into a command line. On success,
21 // returns a pointer to the newly constructed arguments block; the caller is
22 // responsible for freeing the string. On failure, returns null and sets errno.
23 template <typename Character>
construct_command_line(Character const * const * const argv,Character ** const command_line_result)24 static errno_t __cdecl construct_command_line(
25 Character const* const* const argv,
26 Character** const command_line_result
27 ) throw()
28 {
29 typedef __crt_char_traits<Character> traits;
30
31 *command_line_result = nullptr;
32
33 // Compute the number of bytes required to store the arguments in argv in a
34 // command line string (including spaces between arguments and a terminator):
35 size_t const command_line_count = [&]
36 {
37 size_t n = 0;
38 for (Character const* const* it = argv; *it; n += traits::tcslen(*it++) + 1) { }
39
40 // If there were no arguments, return 1 so that we can return an empty
41 // string:
42 return __max(n, 1);
43 }();
44
45 __crt_unique_heap_ptr<Character> command_line(_calloc_crt_t(Character, command_line_count));
46 if (!command_line)
47 {
48 __acrt_errno_map_os_error(ERROR_NOT_ENOUGH_MEMORY);
49 return errno = ENOMEM;
50 }
51
52 Character const* const* source_it = argv;
53 Character* result_it = command_line.get();
54
55 // If there are no arguments, just return the empty string:
56 if (*source_it == nullptr)
57 {
58 *command_line_result = command_line.detach();
59 return 0;
60 }
61
62 // Copy the arguments, separated by spaces:
63 while (*source_it != nullptr)
64 {
65 _ERRCHECK(traits::tcscpy_s(result_it, command_line_count - (result_it - command_line.get()), *source_it));
66 result_it += traits::tcslen(*source_it);
67 *result_it++ = ' ';
68 ++source_it;
69 }
70
71 // Replace the last space with a terminator:
72 result_it[-1] = '\0';
73
74 *command_line_result = command_line.detach();
75 return 0;
76 }
77
78
79
80 // Converts a main()-style envp environment vector into an environment block in
81 // the form required by the CreateProcess API. On success, returns a pointer to
82 // the newly constructed environment block; the caller is responsible for freeing
83 // the block. On failure, returns null and sets errno.
84 template <typename Character>
construct_environment_block(_In_opt_z_ Character const * const * const envp,_Outptr_result_maybenull_ Character ** const environment_block_result)85 static errno_t __cdecl construct_environment_block(
86 _In_opt_z_ Character const* const* const envp,
87 _Outptr_result_maybenull_ Character** const environment_block_result
88 ) throw()
89 {
90 typedef __crt_char_traits<Character> traits;
91
92 *environment_block_result = nullptr;
93
94 // If envp is null, we will use the current environment of this process as
95 // the environment for the new process. No action is required in this case
96 // because simply passing a null environment pointer to CreateProcess will
97 // do the right thing.
98 if (envp == nullptr)
99 return 0;
100
101 // Get the value of the SystemRoot environment variable, if it is defined,
102 // and compute the number of characters required to store it in the
103 // envrionment block:
104 Character const system_root_name[] = { 'S', 'y', 's', 't', 'e', 'm', 'R', 'o', 'o', 't', '\0' };
105
106 __crt_unique_heap_ptr<Character> system_root_value;
107 if (_ERRCHECK_EINVAL(traits::tdupenv_s_crt(system_root_value.get_address_of(), nullptr, system_root_name)) != 0)
108 return errno;
109
110 size_t const system_root_value_count = system_root_value
111 ? traits::tcslen(system_root_value.get()) + 1
112 : 0;
113
114 size_t const system_root_count = _countof(system_root_name) + system_root_value_count;
115
116 // Compute the number of characters required to hold the environment
117 // strings provided by the user:
118 size_t const envp_count = [&]
119 {
120 size_t n = 2; // Account for double null terminator
121 for (auto it = envp; *it != nullptr; n += traits::tcslen(*it++) + 1) { }
122 return n;
123 }();
124
125 // Get the current environment from the OS so that we can get the current
126 // directory strings (those starting with '=') and append them to the user-
127 // provided environment.
128 __crt_unique_heap_ptr<Character> const os_environment(traits::get_environment_from_os());
129 if (!os_environment)
130 return EINVAL;
131
132 // Find the first shell environment variable:
133 Character* const first_cwd = [&]
134 {
135 Character* it = os_environment.get();
136 while (*it != '=')
137 it += traits::tcslen(it) + 1;
138 return it;
139 }();
140
141 // Find the end of the shell environment variables (assume they are contiguous):
142 Character* const last_cwd = [&]
143 {
144 Character* it = first_cwd;
145 while (it[0] == '=' && it[1] != '\0' && it[2] == ':' && it[3] == '=')
146 it += 4 + traits::tcslen(it + 4) + 1;
147 return it;
148 }();
149
150 size_t const cwd_count = last_cwd - first_cwd;
151
152
153 // Check to see if the SystemRoot is already defined in the environment:
154 bool const system_root_defined_in_environment = [&]
155 {
156 for (auto it = envp; *it != nullptr; ++it)
157 {
158 if (traits::tcsnicmp(*it, system_root_name, traits::tcslen(system_root_name)) == 0)
159 return true;
160 }
161
162 return false;
163 }();
164
165 // Allocate storage for the new environment:
166 size_t const environment_block_count = system_root_defined_in_environment
167 ? envp_count + cwd_count
168 : envp_count + cwd_count + system_root_count;
169
170 __crt_unique_heap_ptr<Character> environment_block(_calloc_crt_t(Character, environment_block_count));
171 if (!environment_block)
172 {
173 __acrt_errno_map_os_error(ERROR_OUTOFMEMORY);
174 return errno = ENOMEM;
175 }
176
177 // Build the environment block by concatenating the environment strings with
178 // null characters between them, and with a double null terminator.
179 Character* result_it = environment_block.get();
180 size_t remaining_characters = environment_block_count;
181
182 // Copy the cwd strings into the new environment:
183 if (cwd_count != 0)
184 {
185 memcpy(result_it, first_cwd, cwd_count * sizeof(Character));
186 result_it += cwd_count;
187 remaining_characters -= cwd_count;
188 }
189
190 // Copy the environment strings from envp into the new environment:
191 for (auto it = envp; *it != nullptr; ++it)
192 {
193 _ERRCHECK(traits::tcscpy_s(result_it, remaining_characters, *it));
194
195 size_t const count_copied = traits::tcslen(*it) + 1;
196 result_it += count_copied;
197 remaining_characters -= count_copied;
198 }
199
200 // Copy the SystemRoot into the new environment:
201 if (!system_root_defined_in_environment)
202 {
203 static Character const equal_sign[] = { '=', '\0' };
204
205 _ERRCHECK(traits::tcscpy_s(result_it, system_root_count, system_root_name));
206 _ERRCHECK(traits::tcscat_s(result_it, system_root_count, equal_sign));
207 if (system_root_value)
208 {
209 _ERRCHECK(traits::tcscat_s(result_it, system_root_count, system_root_value.get()));
210 }
211 result_it += system_root_count;
212 }
213
214 // Null-terminate the environment block and return it. If the environment
215 // block is empty, it requires two null terminators:
216 if (result_it == environment_block.get())
217 *result_it++ = '\0';
218
219 *result_it = '\0';
220
221 *environment_block_result = environment_block.detach();
222 return 0;
223 }
224
225
226
227 // Converts a main()-style argv arguments vector and envp environment vector into
228 // a command line and an environment block, for use in the _exec and _spawn
229 // functions. On success, returns 0 and sets the two result argumetns to point
230 // to the newly created command line and environment block. The caller is
231 // responsible for freeing these blocks. On failure, returns -1 and sets errno.
232 template <typename Character>
233 _Success_(return == 0)
234 _Ret_range_(-1, 0)
common_pack_argv_and_envp(_In_z_ Character const * const * const argv,_In_opt_z_ Character const * const * const envp,_Outptr_result_maybenull_ Character ** const command_line_result,_Outptr_result_maybenull_ Character ** const environment_block_result)235 static int __cdecl common_pack_argv_and_envp(
236 _In_z_ Character const* const* const argv,
237 _In_opt_z_ Character const* const* const envp,
238 _Outptr_result_maybenull_ Character** const command_line_result,
239 _Outptr_result_maybenull_ Character** const environment_block_result
240 ) throw()
241 {
242 typedef __crt_char_traits<Character> traits;
243
244 __crt_unique_heap_ptr<Character> command_line;
245 if (construct_command_line(argv, command_line.get_address_of()) != 0)
246 return -1;
247
248 __crt_unique_heap_ptr<Character> environment_block;
249 if (construct_environment_block(envp, environment_block.get_address_of()) != 0)
250 return -1;
251
252 *command_line_result = command_line.detach();
253 *environment_block_result = environment_block.detach();
254 return 0;
255 }
256
__acrt_pack_narrow_command_line_and_environment(char const * const * const argv,char const * const * const envp,char ** const command_line_result,char ** const environment_block_result)257 extern "C" int __cdecl __acrt_pack_narrow_command_line_and_environment(
258 char const* const* const argv,
259 char const* const* const envp,
260 char** const command_line_result,
261 char** const environment_block_result
262 )
263 {
264 return common_pack_argv_and_envp(argv, envp, command_line_result, environment_block_result);
265 }
266
__acrt_pack_wide_command_line_and_environment(wchar_t const * const * const argv,wchar_t const * const * const envp,wchar_t ** const command_line_result,wchar_t ** const environment_block_result)267 extern "C" int __cdecl __acrt_pack_wide_command_line_and_environment(
268 wchar_t const* const* const argv,
269 wchar_t const* const* const envp,
270 wchar_t** const command_line_result,
271 wchar_t** const environment_block_result
272 )
273 {
274 return common_pack_argv_and_envp(argv, envp, command_line_result, environment_block_result);
275 }
276
277
278
279 // Creates an argv array for the _exec and _spawn functions. This function walks
280 // the provided varargs list, copying the char* or wchar_t* pointers into an
281 // array. The caller_array is used first; if it is too small to fit all of the
282 // arguments, an array is dynamically allocated. A pointer to the argv array is
283 // returned to the caller. If the returned pointer is not 'caller_array', the
284 // caller must free the array. On failure, nullptr is returned and errno is set.
285 template <typename Character>
286 _Success_(return != 0)
287 static Character** __cdecl common_capture_argv(
288 _In_ va_list* const arglist,
289 _In_z_ Character const* const first_argument,
290 _When_(return == caller_array, _Post_z_)
291 _Out_writes_(caller_array_count) Character** const caller_array,
292 _In_ size_t const caller_array_count
293 ) throw()
294 {
295 Character** argv = caller_array;
296 size_t argv_count = caller_array_count;
297
298 __crt_unique_heap_ptr<Character*> local_array;
299
300 size_t i = 0;
301 Character* next_argument = const_cast<Character*>(first_argument);
302 for (;;)
303 {
304 if (i >= argv_count)
305 {
306 _VALIDATE_RETURN_NOEXC(SIZE_MAX / 2 > argv_count, ENOMEM, nullptr);
307
308 // If we have run out of room in the caller-provided array, allocate
309 // an array on the heap and copy the contents of the caller-provided
310 // array:
311 if (argv == caller_array)
312 {
313 local_array = _calloc_crt_t(Character*, argv_count * 2);
314 _VALIDATE_RETURN_NOEXC(local_array.get() != nullptr, ENOMEM, nullptr);
315
316 _ERRCHECK(memcpy_s(local_array.get(), argv_count * 2, caller_array, caller_array_count));
317
318 argv = local_array.get();
319 }
320 // Otherwise, we have run out of room in a dynamically allocated
321 // array. We need to reallocate:
322 else
323 {
324 __crt_unique_heap_ptr<Character*> new_array(_recalloc_crt_t(Character*, local_array.get(), argv_count * 2));
325 _VALIDATE_RETURN_NOEXC(new_array.get() != nullptr, ENOMEM, nullptr);
326
327 local_array.detach();
328 local_array.attach(new_array.detach());
329
330 argv = local_array.get();
331 }
332
333 argv_count *= 2;
334 }
335
336 argv[i++] = next_argument;
337 if (!next_argument)
338 break;
339
340 #pragma warning(suppress:__WARNING_INCORRECT_ANNOTATION) // 26007 Possibly incorrect single element annotation on arglist
341 next_argument = va_arg(*arglist, Character*);
342 }
343
344 // At this point, we have succeeded; either local_array is null, or argv is
345 // local_array. In either case, we detach so that we can transfer ownership
346 // to the caller:
347 local_array.detach();
348 return argv;
349 }
350
__acrt_capture_narrow_argv(va_list * const arglist,char const * const first_argument,char ** const caller_array,size_t const caller_array_count)351 extern "C" char** __acrt_capture_narrow_argv(
352 va_list* const arglist,
353 char const* const first_argument,
354 char** const caller_array,
355 size_t const caller_array_count
356 )
357 {
358 return common_capture_argv(arglist, first_argument, caller_array, caller_array_count);
359 }
360
__acrt_capture_wide_argv(va_list * const arglist,wchar_t const * const first_argument,wchar_t ** const caller_array,size_t const caller_array_count)361 extern "C" wchar_t** __acrt_capture_wide_argv(
362 va_list* const arglist,
363 wchar_t const* const first_argument,
364 wchar_t** const caller_array,
365 size_t const caller_array_count
366 )
367 {
368 return common_capture_argv(arglist, first_argument, caller_array, caller_array_count);
369 }
370