xref: /reactos/sdk/lib/ucrt/exec/cenvarg.cpp (revision 9ed5151d)
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