/* Functions used by the Windows port of libgccjit. Copyright (C) 2020-2024 Free Software Foundation, Inc. Contributed by Nicolas Bertolo . This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "config.h" /* Required for rand_s */ #define _CRT_RAND_S #include #include #include "jit-w32.h" #include "libiberty.h" #include #include namespace gcc { namespace jit { void print_last_error (void) { LPSTR psz = NULL; DWORD dwErrorCode; dwErrorCode = GetLastError(); const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&psz), 0, NULL); if (cchMsg > 0) { fprintf (stderr, "%s\n", psz); LocalFree (psz); } else { fprintf (stderr, "Failed to retrieve error message string for error %lu\n", dwErrorCode); } } /* Helper function used for getting the SID belonging to the current user. */ static TOKEN_USER* get_TOKEN_USER_current_user () { TOKEN_USER *result = NULL; HANDLE process_token = INVALID_HANDLE_VALUE; DWORD token_user_info_len; TOKEN_USER *token_user = NULL; /* Get current process access token. */ if (!OpenProcessToken (GetCurrentProcess (), TOKEN_READ, &process_token)) return NULL; /* Get necessary buffer size. */ if (!GetTokenInformation(process_token, TokenUser, NULL, 0, &token_user_info_len) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto cleanup; token_user = (TOKEN_USER*) new char[token_user_info_len]; /* Get info about the user of the process */ if (!GetTokenInformation (process_token, TokenUser, token_user, token_user_info_len, &token_user_info_len)) goto cleanup; result = token_user; cleanup: if (process_token != INVALID_HANDLE_VALUE) CloseHandle(process_token); if (token_user != NULL && result == NULL) delete[] (char*)token_user; return result; } /* Helper function to create a directory with permissions such that only the current user can access it. */ static bool create_directory_for_current_user (const char * path) { PACL pACL = NULL; EXPLICIT_ACCESS ea; SECURITY_ATTRIBUTES sa; SECURITY_DESCRIPTOR SD; DWORD dwRes; bool result = true; TOKEN_USER *token_user = NULL; token_user = get_TOKEN_USER_current_user(); if (!token_user) return false; memset (&ea, 0, sizeof (EXPLICIT_ACCESS)); ea.grfAccessPermissions = GENERIC_ALL; /* Access to all. */ ea.grfAccessMode = SET_ACCESS; /* Set access and revoke everything else. */ /* This is necessary for the Windows Explorer GUI to show the correct tick boxes in the "Security" tab. */ ea.grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; ea.Trustee.TrusteeType = TRUSTEE_IS_USER; ea.Trustee.ptstrName = (char*) token_user->User.Sid; /* Create a new ACL that contains the new ACEs. */ dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL); if (dwRes != ERROR_SUCCESS) return false; if (!InitializeSecurityDescriptor (&SD, SECURITY_DESCRIPTOR_REVISION)) goto cleanup; /* Add the ACL to the security descriptor. */ if (!SetSecurityDescriptorDacl (&SD, TRUE, /* use pACL */ pACL, FALSE)) /* not a default DACL */ goto cleanup; /* Initialize a security attributes structure. */ sa.nLength = sizeof (SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = &SD; sa.bInheritHandle = FALSE; /* Finally create the directory */ if (!CreateDirectoryA (path, &sa)) result = false; cleanup: if (pACL) LocalFree (pACL); if (token_user) delete[] (char*)token_user; return result; } char * win_mkdtemp (void) { char lpTempPathBuffer[MAX_PATH]; /* Gets the temp path env string (no guarantee it's a valid path). */ DWORD dwRetVal = GetTempPath (MAX_PATH, lpTempPathBuffer); if (dwRetVal > MAX_PATH || (dwRetVal == 0)) { print_last_error (); return NULL; } /* Check that the directory actually exists. */ DWORD dwAttrib = GetFileAttributes (lpTempPathBuffer); bool temp_path_exists = (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); if (!temp_path_exists) { fprintf (stderr, "Path returned by GetTempPath does not exist: %s\n", lpTempPathBuffer); } /* Make sure there is enough space in the buffer for the prefix and random number.*/ int temp_path_buffer_len = dwRetVal; const int appended_len = strlen ("\\libgccjit-123456"); if (temp_path_buffer_len + appended_len + 1 >= MAX_PATH) { fprintf (stderr, "Temporary file path too long for generation of random" " directories: %s", lpTempPathBuffer); } /* This is all the space we have in the buffer to store the random number and prefix. */ int extraspace = MAX_PATH - temp_path_buffer_len - 1; int tries; const int max_tries = 1000; for (tries = 0; tries < max_tries; ++tries) { /* Get a random number in [0; UINT_MAX]. */ unsigned int rand_num; if (rand_s (&rand_num) != 0) { fprintf (stderr, "Failed to create a random number using rand_s(): %s\n", _strerror (NULL)); return NULL; } /* Create 6 digits random number. */ rand_num = ((double)rand_num / ((double) UINT_MAX + 1 ) * 1000000); /* Copy the prefix and random number to the buffer. */ snprintf (&lpTempPathBuffer[temp_path_buffer_len], extraspace, "\\libgccjit-%06u", rand_num); if (create_directory_for_current_user (lpTempPathBuffer)) break; // success! /* If we can't create the directory because we got unlucky and the directory already exists retry, otherwise fail. */ if (GetLastError () != ERROR_ALREADY_EXISTS) { print_last_error (); return NULL; } } if (tries == max_tries) { fprintf (stderr, "Failed to create a random directory in %s\n", lpTempPathBuffer); return NULL; } { int allocate_len = temp_path_buffer_len + appended_len + 1; char * result = XNEWVEC (char, allocate_len); strcpy (result, lpTempPathBuffer); return result; } } } }