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