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
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along 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
36namespace gcc {
37namespace jit {
38void
39print_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. */
67static TOKEN_USER*
68get_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. */
108static bool
109create_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
169char *
170win_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