xref: /netbsd-src/external/gpl3/gcc/dist/gcc/jit/jit-w32.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* Functions used by the Windows port of libgccjit.
2    Copyright (C) 2020-2022 Free Software Foundation, Inc.
3    Contributed by Nicolas Bertolo <nicolasbertolo@gmail.com>.
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11 
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3.  If not see
19 <http://www.gnu.org/licenses/>.  */
20 
21 #include "config.h"
22 
23 /* Required for rand_s */
24 #define _CRT_RAND_S
25 
26 #include <cstdio>
27 #include <cstdint>
28 
29 #include "jit-w32.h"
30 
31 #include "libiberty.h"
32 
33 #include <accctrl.h>
34 #include <aclapi.h>
35 
36 namespace gcc {
37 namespace jit {
38 void
print_last_error(void)39 print_last_error (void)
40 {
41   LPSTR psz = NULL;
42   DWORD dwErrorCode;
43   dwErrorCode = GetLastError();
44   const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
45                                      | FORMAT_MESSAGE_IGNORE_INSERTS
46                                      | FORMAT_MESSAGE_ALLOCATE_BUFFER
47                                      | FORMAT_MESSAGE_MAX_WIDTH_MASK,
48                                      NULL,
49                                      dwErrorCode,
50                                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
51                                      reinterpret_cast<LPSTR>(&psz),
52                                      0,
53                                      NULL);
54   if (cchMsg > 0)
55     {
56       fprintf (stderr, "%s\n", psz);
57       LocalFree (psz);
58     }
59   else
60     {
61       fprintf (stderr, "Failed to retrieve error message string for error %lu\n",
62                dwErrorCode);
63     }
64 }
65 
66 /* Helper function used for getting the SID belonging to the current user. */
67 static TOKEN_USER*
get_TOKEN_USER_current_user()68 get_TOKEN_USER_current_user ()
69 {
70   TOKEN_USER *result = NULL;
71 
72   HANDLE process_token = INVALID_HANDLE_VALUE;
73 
74   DWORD token_user_info_len;
75   TOKEN_USER *token_user = NULL;
76 
77   /* Get current process access token. */
78   if (!OpenProcessToken (GetCurrentProcess (), TOKEN_READ,
79                          &process_token))
80     return NULL;
81 
82   /* Get necessary buffer size. */
83   if (!GetTokenInformation(process_token, TokenUser, NULL, 0, &token_user_info_len)
84       && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
85     goto cleanup;
86 
87   token_user = (TOKEN_USER*) new char[token_user_info_len];
88 
89   /* Get info about the user of the process */
90   if (!GetTokenInformation (process_token, TokenUser, token_user,
91                             token_user_info_len, &token_user_info_len))
92       goto cleanup;
93 
94   result = token_user;
95 
96  cleanup:
97   if (process_token != INVALID_HANDLE_VALUE)
98     CloseHandle(process_token);
99 
100   if (token_user != NULL && result == NULL)
101     delete[] (char*)token_user;
102 
103   return result;
104 }
105 
106 /* Helper function to create a directory with permissions such that only the
107   current user can access it. */
108 static bool
create_directory_for_current_user(const char * path)109 create_directory_for_current_user (const char * path)
110 {
111   PACL pACL = NULL;
112   EXPLICIT_ACCESS ea;
113   SECURITY_ATTRIBUTES sa;
114   SECURITY_DESCRIPTOR SD;
115   DWORD dwRes;
116   bool result = true;
117   TOKEN_USER *token_user = NULL;
118 
119   token_user = get_TOKEN_USER_current_user();
120   if (!token_user)
121     return false;
122 
123   memset (&ea, 0, sizeof (EXPLICIT_ACCESS));
124   ea.grfAccessPermissions = GENERIC_ALL; /* Access to all. */
125   ea.grfAccessMode = SET_ACCESS; /* Set access and revoke everything else. */
126   /* This is necessary for the Windows Explorer GUI to show the correct tick
127      boxes in the "Security" tab. */
128   ea.grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
129   ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
130   ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
131   ea.Trustee.ptstrName = (char*) token_user->User.Sid;
132 
133   /* Create a new ACL that contains the new ACEs. */
134   dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
135   if (dwRes != ERROR_SUCCESS)
136     return false;
137 
138   if (!InitializeSecurityDescriptor (&SD,
139                                      SECURITY_DESCRIPTOR_REVISION))
140     goto cleanup;
141 
142   /* Add the ACL to the security descriptor. */
143   if (!SetSecurityDescriptorDacl (&SD,
144                                   TRUE,     /* use pACL */
145                                   pACL,
146                                   FALSE))   /* not a default DACL */
147     goto cleanup;
148 
149   /* Initialize a security attributes structure. */
150   sa.nLength = sizeof (SECURITY_ATTRIBUTES);
151   sa.lpSecurityDescriptor = &SD;
152   sa.bInheritHandle = FALSE;
153 
154   /* Finally create the directory */
155   if (!CreateDirectoryA (path, &sa))
156     result = false;
157 
158  cleanup:
159   if (pACL)
160     LocalFree (pACL);
161 
162   if (token_user)
163     delete[] (char*)token_user;
164 
165   return result;
166 }
167 
168 
169 char *
win_mkdtemp(void)170 win_mkdtemp (void)
171 {
172   char lpTempPathBuffer[MAX_PATH];
173 
174   /* Gets the temp path env string (no guarantee it's a valid path). */
175   DWORD dwRetVal = GetTempPath (MAX_PATH, lpTempPathBuffer);
176   if (dwRetVal > MAX_PATH || (dwRetVal == 0))
177     {
178       print_last_error ();
179       return NULL;
180     }
181 
182   /* Check that the directory actually exists. */
183   DWORD dwAttrib = GetFileAttributes (lpTempPathBuffer);
184   bool temp_path_exists = (dwAttrib != INVALID_FILE_ATTRIBUTES
185                            && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
186   if (!temp_path_exists)
187     {
188       fprintf (stderr, "Path returned by GetTempPath does not exist: %s\n",
189                lpTempPathBuffer);
190     }
191 
192   /* Make sure there is enough space in the buffer for the prefix and random
193      number.*/
194   int temp_path_buffer_len = dwRetVal;
195   const int appended_len = strlen ("\\libgccjit-123456");
196   if (temp_path_buffer_len + appended_len + 1 >= MAX_PATH)
197     {
198       fprintf (stderr, "Temporary file path too long for generation of random"
199                " directories: %s", lpTempPathBuffer);
200     }
201 
202   /* This is all the space we have in the buffer to store the random number and
203      prefix. */
204   int extraspace = MAX_PATH - temp_path_buffer_len - 1;
205 
206   int tries;
207   const int max_tries = 1000;
208 
209   for (tries = 0; tries < max_tries; ++tries)
210     {
211       /* Get a random number in [0; UINT_MAX]. */
212       unsigned int rand_num;
213       if (rand_s (&rand_num) != 0)
214         {
215           fprintf (stderr,
216                    "Failed to create a random number using rand_s(): %s\n",
217                    _strerror (NULL));
218           return NULL;
219         }
220 
221       /* Create 6 digits random number. */
222       rand_num = ((double)rand_num / ((double) UINT_MAX + 1 ) * 1000000);
223 
224       /* Copy the prefix and random number to the buffer. */
225       snprintf (&lpTempPathBuffer[temp_path_buffer_len], extraspace,
226                 "\\libgccjit-%06u", rand_num);
227 
228       if (create_directory_for_current_user (lpTempPathBuffer))
229         break; // success!
230 
231       /* If we can't create the directory because we got unlucky and the
232          directory already exists retry, otherwise fail. */
233       if (GetLastError () != ERROR_ALREADY_EXISTS)
234         {
235           print_last_error ();
236           return NULL;
237         }
238     }
239 
240   if (tries == max_tries)
241     {
242       fprintf (stderr, "Failed to create a random directory in %s\n",
243                lpTempPathBuffer);
244       return NULL;
245     }
246 
247   {
248     int allocate_len = temp_path_buffer_len + appended_len + 1;
249     char * result = XNEWVEC (char, allocate_len);
250     strcpy (result, lpTempPathBuffer);
251     return result;
252   }
253 }
254 }
255 }
256