1/*
2 * Copyright (c) 2015-2017, Intel Corporation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * * Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Intel Corporation nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** \file
30 * \brief Runtime code for hs_database manipulation.
31 */
32
33#include <stdio.h>
34#include <string.h>
35
36#include "allocator.h"
37#include "hs_common.h"
38#include "hs_internal.h"
39#include "hs_version.h"
40#include "ue2common.h"
41#include "database.h"
42#include "crc32.h"
43#include "rose/rose_internal.h"
44#include "util/unaligned.h"
45
46static really_inline
47int db_correctly_aligned(const void *db) {
48 return ISALIGNED_N(db, alignof(unsigned long long));
49}
50
51HS_PUBLIC_API
52hs_error_t HS_CDECL hs_free_database(hs_database_t *db) {
53 if (db && db->magic != HS_DB_MAGIC) {
54 return HS_INVALID;
55 }
56 hs_database_free(db);
57
58 return HS_SUCCESS;
59}
60
61HS_PUBLIC_API
62hs_error_t HS_CDECL hs_serialize_database(const hs_database_t *db, char **bytes,
63 size_t *serialized_length) {
64 if (!db || !bytes || !serialized_length) {
65 return HS_INVALID;
66 }
67
68 if (!db_correctly_aligned(db)) {
69 return HS_BAD_ALIGN;
70 }
71
72 hs_error_t ret = validDatabase(db);
73 if (ret != HS_SUCCESS) {
74 return ret;
75 }
76
77 size_t length = sizeof(struct hs_database) + db->length;
78
79 char *out = hs_misc_alloc(length);
80 ret = hs_check_alloc(out);
81 if (ret != HS_SUCCESS) {
82 hs_misc_free(out);
83 return ret;
84 }
85
86 memset(out, 0, length);
87
88 u32 *buf = (u32 *)out;
89 *buf = db->magic;
90 buf++;
91 *buf = db->version;
92 buf++;
93 *buf = db->length;
94 buf++;
95 memcpy(buf, &db->platform, sizeof(u64a));
96 buf += 2;
97 *buf = db->crc32;
98 buf++;
99 *buf = db->reserved0;
100 buf++;
101 *buf = db->reserved1;
102 buf++;
103
104 const char *bytecode = hs_get_bytecode(db);
105 memcpy(buf, bytecode, db->length);
106
107 *bytes = out;
108 *serialized_length = length;
109 return HS_SUCCESS;
110}
111
112// check that the database header's platform is compatible with the current
113// runtime platform.
114static
115hs_error_t db_check_platform(const u64a p) {
116 if (p != hs_current_platform
117 && p != hs_current_platform_no_avx2
118 && p != hs_current_platform_no_avx512) {
119 return HS_DB_PLATFORM_ERROR;
120 }
121 // passed all checks
122 return HS_SUCCESS;
123}
124
125// Decode and check the database header, returning appropriate errors or
126// HS_SUCCESS if it's OK. The header should be allocated on the stack
127// and later copied into the deserialized database.
128static
129hs_error_t db_decode_header(const char **bytes, const size_t length,
130 struct hs_database *header) {
131 if (!*bytes) {
132 return HS_INVALID;
133 }
134
135 if (length < sizeof(struct hs_database)) {
136 return HS_INVALID;
137 }
138
139 // There's no requirement, really, that the serialized stream of bytes
140 // we've been given is 4-byte aligned, so we use unaligned loads here.
141
142 const u32 *buf = (const u32 *)*bytes;
143
144 // Zero header so that none of it (e.g. its padding) is uninitialized.
145 memset(header, 0, sizeof(struct hs_database));
146
147 header->magic = unaligned_load_u32(buf++);
148 if (header->magic != HS_DB_MAGIC) {
149 return HS_INVALID;
150 }
151
152 header->version = unaligned_load_u32(buf++);
153 if (header->version != HS_DB_VERSION) {
154 return HS_DB_VERSION_ERROR;
155 }
156
157 header->length = unaligned_load_u32(buf++);
158 if (length != sizeof(struct hs_database) + header->length) {
159 DEBUG_PRINTF("bad length %zu, expecting %zu\n", length,
160 sizeof(struct hs_database) + header->length);
161 return HS_INVALID;
162 }
163
164 header->platform = unaligned_load_u64a(buf);
165 buf += 2;
166 header->crc32 = unaligned_load_u32(buf++);
167 header->reserved0 = unaligned_load_u32(buf++);
168 header->reserved1 = unaligned_load_u32(buf++);
169
170 *bytes = (const char *)buf;
171
172 return HS_SUCCESS; // Header checks out
173}
174
175// Check the CRC on a database
176static
177hs_error_t db_check_crc(const hs_database_t *db) {
178 const char *bytecode = hs_get_bytecode(db);
179 u32 crc = Crc32c_ComputeBuf(0, bytecode, db->length);
180 if (crc != db->crc32) {
181 DEBUG_PRINTF("crc mismatch! 0x%x != 0x%x\n", crc, db->crc32);
182 return HS_INVALID;
183 }
184 return HS_SUCCESS;
185}
186
187static
188void db_copy_bytecode(const char *serialized, hs_database_t *db) {
189 // we need to align things manually
190 uintptr_t shift = (uintptr_t)db->bytes & 0x3f;
191 db->bytecode = offsetof(struct hs_database, bytes) - shift;
192 char *bytecode = (char *)db + db->bytecode;
193
194 // Copy the bytecode into place
195 memcpy(bytecode, serialized, db->length);
196}
197
198HS_PUBLIC_API
199hs_error_t HS_CDECL hs_deserialize_database_at(const char *bytes,
200 const size_t length,
201 hs_database_t *db) {
202 if (!bytes || !db) {
203 return HS_INVALID;
204 }
205
206 // We require the user to deserialize into an 8-byte aligned region.
207 if (!ISALIGNED_N(db, 8)) {
208 return HS_BAD_ALIGN;
209 }
210
211 // Decode the header
212 hs_database_t header;
213 hs_error_t ret = db_decode_header(&bytes, length, &header);
214 if (ret != HS_SUCCESS) {
215 return ret;
216 }
217
218 // Make sure the serialized database is for our platform
219 ret = db_check_platform(header.platform);
220 if (ret != HS_SUCCESS) {
221 return ret;
222 }
223
224 // Zero new space for safety
225 size_t dblength = sizeof(struct hs_database) + header.length;
226 memset(db, 0, dblength);
227
228 // Copy the decoded header into place
229 memcpy(db, &header, sizeof(header));
230
231 // Copy the bytecode into the correctly-aligned location, set offsets
232 db_copy_bytecode(bytes, db);
233
234 if (db_check_crc(db) != HS_SUCCESS) {
235 return HS_INVALID;
236 }
237
238 return HS_SUCCESS;
239}
240
241HS_PUBLIC_API
242hs_error_t HS_CDECL hs_deserialize_database(const char *bytes,
243 const size_t length,
244 hs_database_t **db) {
245 if (!bytes || !db) {
246 return HS_INVALID;
247 }
248
249 *db = NULL;
250
251 // Decode and check the header
252 hs_database_t header;
253 hs_error_t ret = db_decode_header(&bytes, length, &header);
254 if (ret != HS_SUCCESS) {
255 return ret;
256 }
257
258 // Make sure the serialized database is for our platform
259 ret = db_check_platform(header.platform);
260 if (ret != HS_SUCCESS) {
261 return ret;
262 }
263
264 // Allocate space for new database
265 size_t dblength = sizeof(struct hs_database) + header.length;
266 struct hs_database *tempdb = hs_database_alloc(dblength);
267 ret = hs_check_alloc(tempdb);
268 if (ret != HS_SUCCESS) {
269 hs_database_free(tempdb);
270 return ret;
271 }
272
273 // Zero new space for safety
274 memset(tempdb, 0, dblength);
275
276 // Copy the decoded header into place
277 memcpy(tempdb, &header, sizeof(header));
278
279 // Copy the bytecode into the correctly-aligned location, set offsets
280 db_copy_bytecode(bytes, tempdb);
281
282 if (db_check_crc(tempdb) != HS_SUCCESS) {
283 hs_database_free(tempdb);
284 return HS_INVALID;
285 }
286
287 *db = tempdb;
288 return HS_SUCCESS;
289}
290
291HS_PUBLIC_API
292hs_error_t HS_CDECL hs_database_size(const hs_database_t *db, size_t *size) {
293 if (!size) {
294 return HS_INVALID;
295 }
296
297 hs_error_t ret = validDatabase(db);
298 if (unlikely(ret != HS_SUCCESS)) {
299 return ret;
300 }
301
302 *size = sizeof(struct hs_database) + db->length;
303 return HS_SUCCESS;
304}
305
306HS_PUBLIC_API
307hs_error_t HS_CDECL hs_serialized_database_size(const char *bytes,
308 const size_t length,
309 size_t *size) {
310 // Decode and check the header
311 hs_database_t header;
312 hs_error_t ret = db_decode_header(&bytes, length, &header);
313 if (ret != HS_SUCCESS) {
314 return ret;
315 }
316
317 if (!size) {
318 return HS_INVALID;
319 }
320
321 *size = sizeof(struct hs_database) + header.length;
322 return HS_SUCCESS;
323}
324
325hs_error_t dbIsValid(const hs_database_t *db) {
326 if (db->magic != HS_DB_MAGIC) {
327 DEBUG_PRINTF("bad magic\n");
328 return HS_INVALID;
329 }
330
331 if (db->version != HS_DB_VERSION) {
332 DEBUG_PRINTF("bad version\n");
333 return HS_DB_VERSION_ERROR;
334 }
335
336 if (db_check_platform(db->platform) != HS_SUCCESS) {
337 DEBUG_PRINTF("bad platform\n");
338 return HS_DB_PLATFORM_ERROR;
339 }
340
341 if (!ISALIGNED_16(hs_get_bytecode(db))) {
342 DEBUG_PRINTF("bad alignment\n");
343 return HS_INVALID;
344 }
345
346 hs_error_t rv = db_check_crc(db);
347 if (rv != HS_SUCCESS) {
348 DEBUG_PRINTF("bad crc\n");
349 return rv;
350 }
351
352 return HS_SUCCESS;
353}
354
355#if defined(_WIN32)
356#define SNPRINTF_COMPAT _snprintf
357#else
358#define SNPRINTF_COMPAT snprintf
359#endif
360
361/** Allocate a buffer and prints the database info into it. Returns an
362 * appropriate error code on failure, or HS_SUCCESS on success. */
363static
364hs_error_t print_database_string(char **s, u32 version, const platform_t plat,
365 u32 raw_mode) {
366 assert(s);
367 *s = NULL;
368
369 u8 release = (version >> 8) & 0xff;
370 u8 minor = (version >> 16) & 0xff;
371 u8 major = (version >> 24) & 0xff;
372
373 const char *features = (plat & HS_PLATFORM_NOAVX512)
374 ? (plat & HS_PLATFORM_NOAVX2) ? "" : "AVX2"
375 : "AVX512";
376
377 const char *mode = NULL;
378
379 if (raw_mode == HS_MODE_STREAM) {
380 mode = "STREAM";
381 } else if (raw_mode == HS_MODE_VECTORED) {
382 mode = "VECTORED";
383 } else {
384 assert(raw_mode == HS_MODE_BLOCK);
385 mode = "BLOCK";
386 }
387
388 // Initial allocation size, which should be large enough to print our info.
389 // If it isn't, snprintf will tell us and we can resize appropriately.
390 size_t len = 256;
391
392 while (1) {
393 char *buf = hs_misc_alloc(len);
394 hs_error_t ret = hs_check_alloc(buf);
395 if (ret != HS_SUCCESS) {
396 hs_misc_free(buf);
397 return ret;
398 }
399
400 // Note: SNPRINTF_COMPAT is a macro defined above, to cope with systems
401 // that don't have snprintf but have a workalike.
402 int p_len = SNPRINTF_COMPAT(
403 buf, len, "Version: %u.%u.%u Features: %s Mode: %s",
404 major, minor, release, features, mode);
405 if (p_len < 0) {
406 DEBUG_PRINTF("snprintf output error, returned %d\n", p_len);
407 hs_misc_free(buf);
408 break;
409 } else if ((size_t)p_len < len) { // output fit within buffer.
410 assert(buf[p_len] == '\0');
411 *s = buf;
412 return HS_SUCCESS;
413 } else { // output didn't fit: resize and reallocate.
414 len = (size_t)p_len + 1; // must add one for null terminator.
415 hs_misc_free(buf);
416 }
417 }
418
419 return HS_NOMEM;
420}
421
422HS_PUBLIC_API
423hs_error_t HS_CDECL hs_serialized_database_info(const char *bytes,
424 size_t length, char **info) {
425 if (!info) {
426 return HS_INVALID;
427 }
428 *info = NULL;
429
430 // Decode and check the header
431 hs_database_t header;
432 hs_error_t ret = db_decode_header(&bytes, length, &header);
433 if (ret != HS_SUCCESS) {
434 return ret;
435 }
436
437 u32 mode = unaligned_load_u32(bytes + offsetof(struct RoseEngine, mode));
438
439 return print_database_string(info, header.version, header.platform, mode);
440}
441
442HS_PUBLIC_API
443hs_error_t HS_CDECL hs_database_info(const hs_database_t *db, char **info) {
444 if (!info) {
445 return HS_INVALID;
446 }
447 *info = NULL;
448
449 if (!db || !db_correctly_aligned(db) || db->magic != HS_DB_MAGIC) {
450 return HS_INVALID;
451 }
452
453 platform_t plat;
454 plat = db->platform;
455
456 const struct RoseEngine *rose = hs_get_bytecode(db);
457
458 return print_database_string(info, db->version, plat, rose->mode);
459}
460