1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 #include <gtest/gtest.h>
19 #include <unistd.h>
20 #include <signal.h>
21 #include <stdio.h>
22 #include <malloc.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <sys/mman.h>
26 #include <vector>
27 #include <vulkan/vulkan.h>
28 #include <optional>
29 #include "testutils.h"
30 
31 char *buffer;
32 volatile int counter = 0;
33 
handler(int,siginfo_t * si,void *)34 static void handler (int, siginfo_t *si, void *) {
35     ++counter;
36     ASSERT_EQ(mprotect((void *)((size_t)si->si_addr & ~0xFFFull), 1, PROT_READ | PROT_WRITE), 0) <<
37         "mprotect() error. Can't reset privileges in signal handler.";
38 }
39 
TEST(memory,mprotect)40 TEST(memory, mprotect) {
41     ASSUME_GAMECORE_CERTIFIED();
42 
43     VkInstance instance;
44     VkApplicationInfo appInfo = {};
45     appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
46     appInfo.pApplicationName = "mprotect test";
47     appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
48     appInfo.pEngineName = "No Engine";
49     appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
50     appInfo.apiVersion = VK_API_VERSION_1_0;
51 
52     VkInstanceCreateInfo createInstanceInfo = {};
53     createInstanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
54     createInstanceInfo.pApplicationInfo = &appInfo;
55 
56     ASSERT_EQ(vkCreateInstance(&createInstanceInfo, nullptr, &instance), VK_SUCCESS)  << "vkCreateInstance() failed!";
57 
58 
59 
60     VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
61     uint32_t deviceCount = 0;
62     vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
63     ASSERT_GT(deviceCount, 0) << "vkEnumeratePhysicalDevices() could not find a physical device";
64     std::vector<VkPhysicalDevice> devices(deviceCount);
65     vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
66     physicalDevice = devices[0];
67 
68 
69 
70     uint32_t queueFamilyCount = 0;
71     vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
72     std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
73     vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
74     uint32_t graphicsFamily = queueFamilyCount+1;
75 
76     int i = 0;
77     for (const auto& queueFamily : queueFamilies) {
78         if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
79             graphicsFamily = i;
80         }
81 
82         if (graphicsFamily < queueFamilyCount+1) {
83             break;
84         }
85 
86         i++;
87     }
88 
89     ASSERT_LT(graphicsFamily, queueFamilyCount + 1) << "No Graphics Queue. Can't init Vulkan";
90 
91 
92 
93     VkDevice device;
94 
95     VkDeviceQueueCreateInfo queueCreateInfo = {};
96     queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
97     queueCreateInfo.queueFamilyIndex = graphicsFamily;
98     queueCreateInfo.queueCount = 1;
99     float queuePriority = 1.0f;
100     queueCreateInfo.pQueuePriorities = &queuePriority;
101 
102     VkPhysicalDeviceFeatures deviceFeatures = {};
103     VkDeviceCreateInfo createDeviceInfo = {};
104     createDeviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
105     createDeviceInfo.pQueueCreateInfos = &queueCreateInfo;
106     createDeviceInfo.queueCreateInfoCount = 1;
107     createDeviceInfo.pEnabledFeatures = &deviceFeatures;
108     createDeviceInfo.enabledExtensionCount = 0;
109     createDeviceInfo.enabledLayerCount = 0;
110 
111     ASSERT_EQ(vkCreateDevice(physicalDevice, &createDeviceInfo, nullptr, &device), VK_SUCCESS) << "vkCreateDevice() failed!";
112 
113 
114 
115     int pagesize;
116     struct sigaction sa;
117 
118     sa.sa_flags = SA_SIGINFO;
119     sigemptyset(&sa.sa_mask);
120     sa.sa_sigaction = handler;
121     ASSERT_EQ(sigaction(SIGSEGV, &sa, nullptr), 0) << "sigaction() failed!";
122 
123     pagesize = sysconf(_SC_PAGE_SIZE);
124     ASSERT_GT(pagesize, -1) << "sysconf() failed!";
125 
126 
127     const int bufferOffset = 2;
128     const int pagesToWrite = 5;
129     const int pagesInBuffer = 4;
130 
131 
132     VkPhysicalDeviceMemoryProperties memProperties;
133     vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
134 
135     for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
136         if ((memProperties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
137             VkMemoryAllocateInfo allocInfo = {};
138             allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
139             allocInfo.allocationSize = pagesToWrite * pagesize;
140             allocInfo.memoryTypeIndex = i;
141 
142             VkDeviceMemory testBufferMemory;
143 
144             ASSERT_EQ(vkAllocateMemory(device, &allocInfo, nullptr, &testBufferMemory), VK_SUCCESS) << "vkAllocateMemory() failed!";
145 
146             // Map a "misaligned" 4-page subset of the 5 pages allocated.
147             vkMapMemory(device, testBufferMemory, bufferOffset, pagesInBuffer * pagesize, 0, (void**)&buffer);
148 
149             ASSERT_NE(buffer, nullptr) << "vkMapMemory() returned a null buffer.";
150 
151             // mprotect requires the beginning of a page as its first parameter. We also map a little more than 4 pages
152             // to make sure we can attempt to write to the final page.
153             ASSERT_EQ(mprotect(buffer - bufferOffset, pagesInBuffer * pagesize + 1, PROT_READ), 0) << "mprotect() failed!";
154 
155             for (char *p = buffer; p != (buffer + pagesInBuffer * pagesize); p++) {
156                 *p = 'a';
157             }
158 
159             vkUnmapMemory(device, testBufferMemory);
160             EXPECT_EQ(pagesToWrite, counter) << "Memory type " << i << " wrote " << counter << " pages instead of " << pagesToWrite;
161         }
162         counter = 0;
163     }
164 }
165