1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
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
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell 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
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 #include "private-lib-core.h"
26 #include "private-lib-misc-lwsac.h"
27 
28 void
lws_list_ptr_insert(lws_list_ptr * head,lws_list_ptr * add,lws_list_ptr_sort_func_t sort_func)29 lws_list_ptr_insert(lws_list_ptr *head, lws_list_ptr *add,
30 		    lws_list_ptr_sort_func_t sort_func)
31 {
32 	while (sort_func && *head) {
33 		if (sort_func(add, *head) <= 0)
34 			break;
35 
36 		head = *head;
37 	}
38 
39 	*add = *head;
40 	*head = add;
41 }
42 
43 size_t
lwsac_align(size_t length)44 lwsac_align(size_t length)
45 {
46 	size_t align = sizeof(int *);
47 
48 	if (length & (align - 1))
49 		length += align - (length & (align - 1));
50 
51 	return length;
52 }
53 
54 size_t
lwsac_sizeof(int first)55 lwsac_sizeof(int first)
56 {
57 	return sizeof(struct lwsac) + (first ? sizeof(struct lwsac_head) : 0);
58 }
59 
60 size_t
lwsac_get_tail_pos(struct lwsac * lac)61 lwsac_get_tail_pos(struct lwsac *lac)
62 {
63 	return lac->ofs;
64 }
65 
66 struct lwsac *
lwsac_get_next(struct lwsac * lac)67 lwsac_get_next(struct lwsac *lac)
68 {
69 	return lac->next;
70 }
71 
72 int
lwsac_extend(struct lwsac * head,int amount)73 lwsac_extend(struct lwsac *head, int amount)
74 {
75 	struct lwsac_head *lachead;
76 	struct lwsac *bf;
77 
78 	assert(head);
79 	lachead = (struct lwsac_head *)&head[1];
80 
81 	bf = lachead->curr;
82 	assert(bf);
83 
84 	if (bf->alloc_size - bf->ofs < lwsac_align(amount))
85 		return 1;
86 
87 	/* memset so constant folding never sees uninitialized data */
88 
89 	memset(((uint8_t *)bf) + bf->ofs, 0, lwsac_align(amount));
90 	bf->ofs += lwsac_align(amount);
91 
92 	return 0;
93 }
94 
95 static void *
_lwsac_use(struct lwsac ** head,size_t ensure,size_t chunk_size,char backfill)96 _lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size, char backfill)
97 {
98 	struct lwsac_head *lachead = NULL;
99 	size_t ofs, alloc, al, hp;
100 	struct lwsac *bf = *head;
101 
102 	if (bf)
103 		lachead = (struct lwsac_head *)&bf[1];
104 
105 	al = lwsac_align(ensure);
106 
107 	/* backfill into earlier chunks if that is allowed */
108 
109 	if (backfill)
110 		/*
111 		 * check if anything can take it, from the start
112 		 */
113 		while (bf) {
114 			if (bf->alloc_size - bf->ofs >= ensure)
115 				goto do_use;
116 
117 			bf = bf->next;
118 		}
119 	else {
120 		/*
121 		 * If there's a current chunk, just check if he can take it
122 		 */
123 		if (lachead && lachead->curr) {
124 			bf = lachead->curr;
125 			if (bf->alloc_size - bf->ofs >= ensure)
126 				goto do_use;
127 		}
128 	}
129 
130 	/* nothing can currently take it... so we must allocate */
131 
132 	hp = sizeof(*bf); /* always need the normal header part... */
133 	if (!*head)
134 		hp += sizeof(struct lwsac_head);
135 
136 	if (!chunk_size)
137 		alloc = LWSAC_CHUNK_SIZE + hp;
138 	else
139 		alloc = chunk_size + hp;
140 
141 	/*
142 	 * If we get asked for something outside our expectation,
143 	 * increase the allocation to meet it
144 	 */
145 
146 	if (al >= alloc - hp)
147 		alloc = al + hp;
148 
149 	lwsl_debug("%s: alloc %d for %d\n", __func__, (int)alloc, (int)ensure);
150 	bf = malloc(alloc);
151 	if (!bf) {
152 		lwsl_err("%s: OOM trying to alloc %llud\n", __func__,
153 				(unsigned long long)alloc);
154 		return NULL;
155 	}
156 
157 	/*
158 	 * belabouring the point... ofs is aligned to the platform's
159 	 * generic struct alignment at the start then
160 	 */
161 	bf->ofs = sizeof(*bf);
162 
163 	if (!*head) {
164 		/*
165 		 * We are the first, head, entry...
166 		 */
167 		*head = bf;
168 		/*
169 		 * ... allocate for the special head block
170 		 */
171 		bf->ofs += sizeof(*lachead);
172 		lachead = (struct lwsac_head *)&bf[1];
173 		memset(lachead, 0, sizeof(*lachead));
174 	} else
175 		if (lachead->curr)
176 			lachead->curr->next = bf;
177 
178 	lachead->curr = bf;
179 	bf->head = *head;
180 	bf->next = NULL;
181 	bf->alloc_size = alloc;
182 
183 	lachead->total_alloc_size += alloc;
184 	lachead->total_blocks++;
185 
186 do_use:
187 
188 	ofs = bf->ofs;
189 
190 	if (al > ensure)
191 		/* zero down the alignment padding part */
192 		memset((char *)bf + ofs + ensure, 0, al - ensure);
193 
194 	bf->ofs += al;
195 	if (bf->ofs >= bf->alloc_size)
196 		bf->ofs = bf->alloc_size;
197 
198 	return (char *)bf + ofs;
199 }
200 
201 void *
lwsac_use(struct lwsac ** head,size_t ensure,size_t chunk_size)202 lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size)
203 {
204 	return _lwsac_use(head, ensure, chunk_size, 0);
205 }
206 
207 void *
lwsac_use_backfill(struct lwsac ** head,size_t ensure,size_t chunk_size)208 lwsac_use_backfill(struct lwsac **head, size_t ensure, size_t chunk_size)
209 {
210 	return _lwsac_use(head, ensure, chunk_size, 1);
211 }
212 
213 uint8_t *
lwsac_scan_extant(struct lwsac * head,uint8_t * find,size_t len,int nul)214 lwsac_scan_extant(struct lwsac *head, uint8_t *find, size_t len, int nul)
215 {
216 	while (head) {
217 		uint8_t *pos = (uint8_t *)&head[1],
218 			*end = ((uint8_t *)head) + head->ofs - len;
219 
220 		if (head->ofs - sizeof(*head) >= len)
221 			while (pos < end) {
222 				if (*pos == *find && (!nul || !pos[len]) &&
223 				    pos[len - 1] == find[len - 1] &&
224 				    !memcmp(pos, find, len))
225 					/* found the blob */
226 					return pos;
227 				pos++;
228 			}
229 
230 		head = head->next;
231 	}
232 
233 	return NULL;
234 }
235 
236 uint64_t
lwsac_total_overhead(struct lwsac * head)237 lwsac_total_overhead(struct lwsac *head)
238 {
239 	uint64_t overhead = 0;
240 
241 	while (head) {
242 		overhead += (head->alloc_size - head->ofs) + sizeof(*head);
243 
244 		head = head->next;
245 	}
246 
247 	return overhead;
248 }
249 
250 void *
lwsac_use_zero(struct lwsac ** head,size_t ensure,size_t chunk_size)251 lwsac_use_zero(struct lwsac **head, size_t ensure, size_t chunk_size)
252 {
253 	void *p = lwsac_use(head, ensure, chunk_size);
254 
255 	if (p)
256 		memset(p, 0, ensure);
257 
258 	return p;
259 }
260 
261 void
lwsac_free(struct lwsac ** head)262 lwsac_free(struct lwsac **head)
263 {
264 	struct lwsac *it = *head;
265 
266 	*head = NULL;
267 	lwsl_debug("%s: head %p\n", __func__, *head);
268 
269 	while (it) {
270 		struct lwsac *tmp = it->next;
271 
272 		free(it);
273 		it = tmp;
274 	}
275 }
276 
277 void
lwsac_info(struct lwsac * head)278 lwsac_info(struct lwsac *head)
279 {
280 #if defined(_DEBUG)
281 	struct lwsac_head *lachead;
282 
283 	if (!head) {
284 		lwsl_debug("%s: empty\n", __func__);
285 		return;
286 	}
287 
288 	lachead = (struct lwsac_head *)&head[1];
289 
290 	lwsl_debug("%s: lac %p: %dKiB in %d blocks\n", __func__, head,
291 		   (int)(lachead->total_alloc_size >> 10), lachead->total_blocks);
292 #endif
293 }
294 
295 uint64_t
lwsac_total_alloc(struct lwsac * head)296 lwsac_total_alloc(struct lwsac *head)
297 {
298 	struct lwsac_head *lachead;
299 
300 	if (!head)
301 		return 0;
302 
303 	lachead = (struct lwsac_head *)&head[1];
304 	return lachead->total_alloc_size;
305 }
306 
307 void
lwsac_reference(struct lwsac * head)308 lwsac_reference(struct lwsac *head)
309 {
310 	struct lwsac_head *lachead = (struct lwsac_head *)&head[1];
311 
312 	lachead->refcount++;
313 	lwsl_debug("%s: head %p: (det %d) refcount -> %d\n",
314 		    __func__, head, lachead->detached, lachead->refcount);
315 }
316 
317 void
lwsac_unreference(struct lwsac ** head)318 lwsac_unreference(struct lwsac **head)
319 {
320 	struct lwsac_head *lachead;
321 
322 	if (!(*head))
323 		return;
324 
325 	lachead = (struct lwsac_head *)&(*head)[1];
326 
327 	if (!lachead->refcount)
328 		lwsl_warn("%s: refcount going below zero\n", __func__);
329 
330 	lachead->refcount--;
331 
332 	lwsl_debug("%s: head %p: (det %d) refcount -> %d\n",
333 		    __func__, *head, lachead->detached, lachead->refcount);
334 
335 	if (lachead->detached && !lachead->refcount) {
336 		lwsl_debug("%s: head %p: FREED\n", __func__, *head);
337 		lwsac_free(head);
338 	}
339 }
340 
341 void
lwsac_detach(struct lwsac ** head)342 lwsac_detach(struct lwsac **head)
343 {
344 	struct lwsac_head *lachead;
345 
346 	if (!(*head))
347 		return;
348 
349 	lachead = (struct lwsac_head *)&(*head)[1];
350 
351 	lachead->detached = 1;
352 	if (!lachead->refcount) {
353 		lwsl_debug("%s: head %p: FREED\n", __func__, *head);
354 		lwsac_free(head);
355 	} else
356 		lwsl_debug("%s: head %p: refcount %d: Marked as detached\n",
357 			    __func__, *head, lachead->refcount);
358 }
359