1 /*
2  * Copyright 2019 The Hafnium Authors.
3  *
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/BSD-3-Clause.
7  */
8 
9 #include "hf/std.h"
10 
11 #include "hf/check.h"
12 
13 /* Declare unsafe functions locally so they are not available globally. */
14 void *memset(void *s, int c, size_t n);
15 void *memcpy(void *dst, const void *src, size_t n);
16 void *memmove(void *dst, const void *src, size_t n);
17 
18 /*
19  * As per the C11 specification, mem*_s() operations fill the destination buffer
20  * if runtime constraint validation fails, assuming that `dest` and `destsz`
21  * are both valid.
22  */
23 #define CHECK_OR_FILL(cond, dest, destsz, ch)                               \
24 	do {                                                                \
25 		if (!(cond)) {                                              \
26 			if ((dest) != NULL && (destsz) <= RSIZE_MAX) {      \
27 				memset_s((dest), (destsz), (ch), (destsz)); \
28 			}                                                   \
29 			panic("%s failed: " #cond, __func__);               \
30 		}                                                           \
31 	} while (0)
32 
33 #define CHECK_OR_ZERO_FILL(cond, dest, destsz) \
34 	CHECK_OR_FILL(cond, dest, destsz, '\0')
35 
memset_s(void * dest,rsize_t destsz,int ch,rsize_t count)36 void memset_s(void *dest, rsize_t destsz, int ch, rsize_t count)
37 {
38 	if (dest == NULL || destsz > RSIZE_MAX) {
39 		panic("memset_s failed as either dest == NULL "
40 		      "or destsz > RSIZE_MAX.\n");
41 	}
42 
43 	/*
44 	 * Clang analyzer doesn't like us calling unsafe memory functions, so
45 	 * make it ignore this call.
46 	 */
47 	// NOLINTNEXTLINE
48 	memset(dest, ch, (count <= destsz ? count : destsz));
49 }
50 
memcpy_s(void * dest,rsize_t destsz,const void * src,rsize_t count)51 void memcpy_s(void *dest, rsize_t destsz, const void *src, rsize_t count)
52 {
53 	uintptr_t d = (uintptr_t)dest;
54 	uintptr_t s = (uintptr_t)src;
55 
56 	CHECK_OR_ZERO_FILL(dest != NULL, dest, destsz);
57 	CHECK_OR_ZERO_FILL(src != NULL, dest, destsz);
58 
59 	/* Check count <= destsz <= RSIZE_MAX. */
60 	CHECK_OR_ZERO_FILL(destsz <= RSIZE_MAX, dest, destsz);
61 	CHECK_OR_ZERO_FILL(count <= destsz, dest, destsz);
62 
63 	/*
64 	 * Buffer overlap test.
65 	 * case a) `d < s` implies `s >= d+count`
66 	 * case b) `d > s` implies `d >= s+count`
67 	 */
68 	CHECK_OR_ZERO_FILL(d != s, dest, destsz);
69 	CHECK_OR_ZERO_FILL(d < s || d >= (s + count), dest, destsz);
70 	CHECK_OR_ZERO_FILL(d > s || s >= (d + count), dest, destsz);
71 
72 	/*
73 	 * Clang analyzer doesn't like us calling unsafe memory functions, so
74 	 * make it ignore this call.
75 	 */
76 	// NOLINTNEXTLINE
77 	memcpy(dest, src, count);
78 }
79 
memmove_s(void * dest,rsize_t destsz,const void * src,rsize_t count)80 void memmove_s(void *dest, rsize_t destsz, const void *src, rsize_t count)
81 {
82 	CHECK_OR_ZERO_FILL(dest != NULL, dest, destsz);
83 	CHECK_OR_ZERO_FILL(src != NULL, dest, destsz);
84 
85 	/* Check count <= destsz <= RSIZE_MAX. */
86 	CHECK_OR_ZERO_FILL(destsz <= RSIZE_MAX, dest, destsz);
87 	CHECK_OR_ZERO_FILL(count <= destsz, dest, destsz);
88 
89 	/*
90 	 * Clang analyzer doesn't like us calling unsafe memory functions, so
91 	 * make it ignore this call.
92 	 */
93 	// NOLINTNEXTLINE
94 	memmove(dest, src, count);
95 }
96 
97 /**
98  * Finds the first occurrence of character `ch` in the first `count` bytes of
99  * memory pointed to by `ptr`.
100  *
101  * Returns NULL if `ch` is not found.
102  * Panics if `ptr` is NULL (undefined behaviour).
103  */
memchr(const void * ptr,int ch,size_t count)104 void *memchr(const void *ptr, int ch, size_t count)
105 {
106 	size_t i;
107 	const unsigned char *p = (const unsigned char *)ptr;
108 
109 	CHECK(ptr != NULL);
110 
111 	/* Iterate over at most `strsz` characters of `str`. */
112 	for (i = 0; i < count; ++i) {
113 		if (p[i] == (unsigned char)ch) {
114 			return (void *)(&p[i]);
115 		}
116 	}
117 
118 	return NULL;
119 }
120 
121 /**
122  * Returns the length of the null-terminated byte string `str`, examining at
123  * most `strsz` bytes.
124  *
125  * If `str` is a NULL pointer, it returns zero.
126  * If a NULL character is not found, it returns `strsz`.
127  */
strnlen_s(const char * str,size_t strsz)128 size_t strnlen_s(const char *str, size_t strsz)
129 {
130 	if (str == NULL) {
131 		return 0;
132 	}
133 
134 	for (size_t i = 0; i < strsz; ++i) {
135 		if (str[i] == '\0') {
136 			return i;
137 		}
138 	}
139 
140 	/* NULL character not found. */
141 	return strsz;
142 }
143