/src/vulkan-loader/loader/loader_json.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Copyright (c) 2015-2021 The Khronos Group Inc. |
3 | | Copyright (c) 2015-2021 Valve Corporation |
4 | | Copyright (c) 2015-2021 LunarG, Inc. |
5 | | |
6 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | | of this software and associated documentation files (the "Software"), to deal |
8 | | in the Software without restriction, including without limitation the rights |
9 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | | copies of the Software, and to permit persons to whom the Software is |
11 | | furnished to do so, subject to the following conditions: |
12 | | |
13 | | The above copyright notice and this permission notice shall be included in |
14 | | all copies or substantial portions of the Software. |
15 | | |
16 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | | THE SOFTWARE. |
23 | | */ |
24 | | |
25 | | #include "loader_json.h" |
26 | | |
27 | | #include <assert.h> |
28 | | #include <float.h> |
29 | | #include <limits.h> |
30 | | #include <math.h> |
31 | | #include <stdio.h> |
32 | | #include <stdlib.h> |
33 | | #include <string.h> |
34 | | |
35 | | #include "cJSON.h" |
36 | | |
37 | | #include "allocation.h" |
38 | | #include "loader.h" |
39 | | #include "log.h" |
40 | | |
41 | | extern void create_callback_file(const char *filename); |
42 | | |
43 | | #if COMMON_UNIX_PLATFORMS |
44 | | #include <fcntl.h> |
45 | | #include <sys/stat.h> |
46 | | #endif |
47 | | |
48 | | #ifdef _WIN32 |
49 | | static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, |
50 | | char **out_buff) { |
51 | | HANDLE file_handle = INVALID_HANDLE_VALUE; |
52 | | DWORD len = 0, read_len = 0; |
53 | | VkResult res = VK_SUCCESS; |
54 | | BOOL read_ok = false; |
55 | | |
56 | | int filename_utf16_size = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); |
57 | | if (filename_utf16_size > 0) { |
58 | | wchar_t *filename_utf16 = (wchar_t *)loader_stack_alloc(filename_utf16_size * sizeof(wchar_t)); |
59 | | if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_utf16, filename_utf16_size) == filename_utf16_size) { |
60 | | file_handle = |
61 | | CreateFileW(filename_utf16, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
62 | | } |
63 | | } |
64 | | if (INVALID_HANDLE_VALUE == file_handle) { |
65 | | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); |
66 | | res = VK_ERROR_INITIALIZATION_FAILED; |
67 | | goto out; |
68 | | } |
69 | | len = GetFileSize(file_handle, NULL); |
70 | | if (INVALID_FILE_SIZE == len) { |
71 | | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename); |
72 | | res = VK_ERROR_INITIALIZATION_FAILED; |
73 | | goto out; |
74 | | } |
75 | | *out_buff = (char *)loader_instance_heap_calloc(inst, len + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); |
76 | | if (NULL == *out_buff) { |
77 | | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename); |
78 | | res = VK_ERROR_OUT_OF_HOST_MEMORY; |
79 | | goto out; |
80 | | } |
81 | | read_ok = ReadFile(file_handle, *out_buff, len, &read_len, NULL); |
82 | | if (len != read_len || false == read_ok) { |
83 | | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename); |
84 | | res = VK_ERROR_INITIALIZATION_FAILED; |
85 | | goto out; |
86 | | } |
87 | | *out_len = len + 1; |
88 | | (*out_buff)[len] = '\0'; |
89 | | |
90 | | out: |
91 | | if (INVALID_HANDLE_VALUE != file_handle) { |
92 | | CloseHandle(file_handle); |
93 | | } |
94 | | return res; |
95 | | } |
96 | | #elif COMMON_UNIX_PLATFORMS |
97 | | static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, |
98 | 2.80k | char **out_buff) { |
99 | 2.80k | FILE *file = NULL; |
100 | 2.80k | struct stat stats = {0}; |
101 | 2.80k | VkResult res = VK_SUCCESS; |
102 | | |
103 | | // fprintf(stderr, "loader_get_json: Reading JSON file %s\n", filename); |
104 | 2.80k | file = fopen(filename, "rb"); |
105 | 2.80k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
106 | 2.80k | if (file == NULL) { |
107 | 0 | create_callback_file(filename); |
108 | 0 | file = fopen(filename, "rb"); |
109 | 0 | } |
110 | 2.80k | #endif |
111 | 2.80k | if (NULL == file) { |
112 | 0 | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); |
113 | 0 | res = VK_ERROR_INITIALIZATION_FAILED; |
114 | 0 | goto out; |
115 | 0 | } |
116 | 2.80k | if (-1 == fstat(fileno(file), &stats)) { |
117 | 0 | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename); |
118 | 0 | res = VK_ERROR_INITIALIZATION_FAILED; |
119 | 0 | goto out; |
120 | 0 | } |
121 | 2.80k | *out_buff = (char *)loader_instance_heap_calloc(inst, stats.st_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); |
122 | 2.80k | if (NULL == *out_buff) { |
123 | 0 | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename); |
124 | 0 | res = VK_ERROR_OUT_OF_HOST_MEMORY; |
125 | 0 | goto out; |
126 | 0 | } |
127 | 2.80k | if (stats.st_size != (long int)fread(*out_buff, sizeof(char), stats.st_size, file)) { |
128 | 0 | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename); |
129 | 0 | res = VK_ERROR_INITIALIZATION_FAILED; |
130 | 0 | goto out; |
131 | 0 | } |
132 | 2.80k | *out_len = stats.st_size + 1; |
133 | 2.80k | (*out_buff)[stats.st_size] = '\0'; |
134 | | |
135 | 2.80k | out: |
136 | 2.80k | if (NULL != file) { |
137 | 2.80k | fclose(file); |
138 | 2.80k | } |
139 | 2.80k | return res; |
140 | 2.80k | } |
141 | | #else |
142 | | #warning fopen not available on this platform |
143 | | VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, char **out_buff) { |
144 | | return VK_ERROR_INITIALIZATION_FAILED; |
145 | | } |
146 | | #endif |
147 | | |
148 | 2.80k | TEST_FUNCTION_EXPORT VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json) { |
149 | 2.80k | char *json_buf = NULL; |
150 | 2.80k | VkResult res = VK_SUCCESS; |
151 | | |
152 | 2.80k | assert(json != NULL); |
153 | | |
154 | 2.80k | size_t json_len = 0; |
155 | 2.80k | *json = NULL; |
156 | 2.80k | res = loader_read_entire_file(inst, filename, &json_len, &json_buf); |
157 | 2.80k | if (VK_SUCCESS != res) { |
158 | 0 | goto out; |
159 | 0 | } |
160 | 2.80k | bool out_of_memory = false; |
161 | | // Parse text from file |
162 | 2.80k | *json = loader_cJSON_ParseWithLength(inst ? &inst->alloc_callbacks : NULL, json_buf, json_len, &out_of_memory); |
163 | 2.80k | if (out_of_memory) { |
164 | 0 | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Out of Memory error occurred while parsing JSON file %s.", |
165 | 0 | filename); |
166 | 0 | res = VK_ERROR_OUT_OF_HOST_MEMORY; |
167 | 0 | goto out; |
168 | 2.80k | } else if (*json == NULL) { |
169 | 1.02k | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Invalid JSON file %s.", filename); |
170 | 1.02k | goto out; |
171 | 1.02k | } |
172 | | |
173 | 2.80k | out: |
174 | 2.80k | loader_instance_heap_free(inst, json_buf); |
175 | 2.80k | if (res != VK_SUCCESS && *json != NULL) { |
176 | 0 | loader_cJSON_Delete(*json); |
177 | 0 | *json = NULL; |
178 | 0 | } |
179 | | |
180 | 2.80k | return res; |
181 | 2.80k | } |
182 | | |
183 | 0 | VkResult loader_parse_json_string_to_existing_str(cJSON *object, const char *key, size_t out_str_len, char *out_string) { |
184 | 0 | if (NULL == key) { |
185 | 0 | return VK_ERROR_INITIALIZATION_FAILED; |
186 | 0 | } |
187 | 0 | cJSON *item = loader_cJSON_GetObjectItem(object, key); |
188 | 0 | if (NULL == item) { |
189 | 0 | return VK_ERROR_INITIALIZATION_FAILED; |
190 | 0 | } |
191 | | |
192 | 0 | if (item->type != cJSON_String || item->valuestring == NULL) { |
193 | 0 | return VK_ERROR_INITIALIZATION_FAILED; |
194 | 0 | } |
195 | 0 | bool out_of_memory = false; |
196 | 0 | bool success = loader_cJSON_PrintPreallocated(item, out_string, (int)out_str_len, cJSON_False); |
197 | 0 | if (out_of_memory) { |
198 | 0 | return VK_ERROR_OUT_OF_HOST_MEMORY; |
199 | 0 | } |
200 | 0 | if (!success) { |
201 | 0 | return VK_ERROR_INITIALIZATION_FAILED; |
202 | 0 | } |
203 | 0 | return VK_SUCCESS; |
204 | 0 | } |
205 | | |
206 | 0 | VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string) { |
207 | 0 | if (NULL == key) { |
208 | 0 | return VK_ERROR_INITIALIZATION_FAILED; |
209 | 0 | } |
210 | | |
211 | 0 | cJSON *item = loader_cJSON_GetObjectItem(object, key); |
212 | 0 | if (NULL == item || NULL == item->valuestring) { |
213 | 0 | return VK_ERROR_INITIALIZATION_FAILED; |
214 | 0 | } |
215 | | |
216 | 0 | bool out_of_memory = false; |
217 | 0 | char *str = loader_cJSON_Print(item, &out_of_memory); |
218 | 0 | if (out_of_memory || NULL == str) { |
219 | 0 | return VK_ERROR_OUT_OF_HOST_MEMORY; |
220 | 0 | } |
221 | 0 | if (NULL != out_string) { |
222 | 0 | *out_string = str; |
223 | 0 | } |
224 | 0 | return VK_SUCCESS; |
225 | 0 | } |
226 | | VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, |
227 | 0 | struct loader_string_list *string_list) { |
228 | 0 | if (NULL == key) { |
229 | 0 | return VK_ERROR_INITIALIZATION_FAILED; |
230 | 0 | } |
231 | 0 | cJSON *item = loader_cJSON_GetObjectItem(object, key); |
232 | 0 | if (NULL == item) { |
233 | 0 | return VK_ERROR_INITIALIZATION_FAILED; |
234 | 0 | } |
235 | | |
236 | 0 | uint32_t count = loader_cJSON_GetArraySize(item); |
237 | 0 | if (count == 0) { |
238 | 0 | return VK_SUCCESS; |
239 | 0 | } |
240 | | |
241 | 0 | VkResult res = create_string_list(inst, count, string_list); |
242 | 0 | if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { |
243 | 0 | goto out; |
244 | 0 | } |
245 | 0 | cJSON *element = NULL; |
246 | 0 | cJSON_ArrayForEach(element, item) { |
247 | 0 | if (element->type != cJSON_String) { |
248 | 0 | return VK_ERROR_INITIALIZATION_FAILED; |
249 | 0 | } |
250 | 0 | bool out_of_memory = false; |
251 | 0 | char *out_data = loader_cJSON_Print(element, &out_of_memory); |
252 | 0 | if (out_of_memory) { |
253 | 0 | res = VK_ERROR_OUT_OF_HOST_MEMORY; |
254 | 0 | goto out; |
255 | 0 | } |
256 | 0 | res = append_str_to_string_list(inst, string_list, out_data); |
257 | 0 | if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { |
258 | 0 | goto out; |
259 | 0 | } |
260 | 0 | } |
261 | 0 | out: |
262 | 0 | if (res == VK_ERROR_OUT_OF_HOST_MEMORY && NULL != string_list->list) { |
263 | 0 | free_string_list(inst, string_list); |
264 | 0 | } |
265 | |
|
266 | 0 | return res; |
267 | 0 | } |