1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2020 Robert Mustacchi
14  */
15 
16 /*
17  * Test memory based streams: opem_memstream(3C), open_wmemstream(3C), and
18  * fmemopen(3C).
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/sysmacros.h>
25 #include <strings.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <wchar.h>
29 #include <umem.h>
30 #include <locale.h>
31 
32 typedef boolean_t (*memstream_test_f)(void);
33 static char *fmemopen_str1 = "The Road goes ever on and on\n"
34 	"Down from the door where it began.\n";
35 const wchar_t *wstream_str = L"いつか終わる夢";
36 /*
37  * smatch doesn't support wide-character constants (wchar_t foo = L'xxx'), so
38  * instead use a string which it'll happily accept.
39  */
40 const wchar_t *wstr_const = L"光";
41 
42 const char *
_umem_debug_init(void)43 _umem_debug_init(void)
44 {
45 	return ("default,verbose");
46 }
47 
48 const char *
_umem_logging_init(void)49 _umem_logging_init(void)
50 {
51 	return ("fail,contents");
52 }
53 
54 static boolean_t
fmemopen_badopen(void * buf,size_t size,const char * mode,int err)55 fmemopen_badopen(void *buf, size_t size, const char *mode, int err)
56 {
57 	FILE *f = fmemopen(buf, size, mode);
58 
59 	if (f != NULL) {
60 		warnx("fmemopen() succeeded erroneously");
61 		(void) fclose(f);
62 		return (B_FALSE);
63 	}
64 
65 	if (errno != err) {
66 		warnx("fmemopen() open failed with wrong errno, "
67 		    "found %d (%s), expected %d (%s)", errno, strerror(errno),
68 		    err, strerror(err));
69 		return (B_FALSE);
70 	}
71 
72 	return (B_TRUE);
73 }
74 
75 static boolean_t
fmemopen_badmode(void)76 fmemopen_badmode(void)
77 {
78 	return (fmemopen_badopen(fmemopen_str1, strlen(fmemopen_str1), "foobar",
79 	    EINVAL));
80 }
81 
82 static boolean_t
fmemopen_zerobuf1(void)83 fmemopen_zerobuf1(void)
84 {
85 	return (fmemopen_badopen(fmemopen_str1, 0, "w", EINVAL));
86 }
87 
88 static boolean_t
fmemopen_zerobuf2(void)89 fmemopen_zerobuf2(void)
90 {
91 	return (fmemopen_badopen(NULL, 0, "w+", EINVAL));
92 }
93 
94 static boolean_t
fmemopen_nullbuf1(void)95 fmemopen_nullbuf1(void)
96 {
97 	return (fmemopen_badopen(NULL, 10, "r", EINVAL));
98 }
99 
100 static boolean_t
fmemopen_nullbuf2(void)101 fmemopen_nullbuf2(void)
102 {
103 	return (fmemopen_badopen(NULL, 10, "w", EINVAL));
104 }
105 
106 static boolean_t
fmemopen_nullbuf3(void)107 fmemopen_nullbuf3(void)
108 {
109 	return (fmemopen_badopen(NULL, 10, "a", EINVAL));
110 }
111 
112 static boolean_t
fmemopen_nullbuf4(void)113 fmemopen_nullbuf4(void)
114 {
115 	return (fmemopen_badopen(NULL, 10, "ax", EINVAL));
116 }
117 
118 static boolean_t
fmemopen_sizemax(void)119 fmemopen_sizemax(void)
120 {
121 	return (fmemopen_badopen(NULL, SIZE_MAX, "w+", ENOMEM));
122 }
123 
124 static boolean_t
fmemopen_cantalloc(void)125 fmemopen_cantalloc(void)
126 {
127 	boolean_t ret;
128 
129 	umem_setmtbf(1);
130 	ret = fmemopen_badopen(NULL, 10, "w+", ENOMEM);
131 	umem_setmtbf(0);
132 	return (ret);
133 }
134 
135 static boolean_t
open_memstream_badopen(char ** bufp,size_t * sizep,int err)136 open_memstream_badopen(char **bufp, size_t *sizep, int err)
137 {
138 	FILE *f = open_memstream(bufp, sizep);
139 
140 	if (f != NULL) {
141 		warnx("open_memstream() succeeded erroneously");
142 		(void) fclose(f);
143 		return (B_FALSE);
144 	}
145 
146 	if (errno != err) {
147 		warnx("open_memstream() open failed with wrong errno, "
148 		    "found %d (%s), expected %d (%s)", errno, strerror(errno),
149 		    err, strerror(err));
150 		return (B_FALSE);
151 	}
152 
153 	return (B_TRUE);
154 }
155 
156 static boolean_t
open_memstream_badbuf(void)157 open_memstream_badbuf(void)
158 {
159 	size_t s, check;
160 	boolean_t ret;
161 
162 	arc4random_buf(&s, sizeof (s));
163 	check = s;
164 	ret = open_memstream_badopen(NULL, &s, EINVAL);
165 	if (check != s) {
166 		warnx("open_memstream() open erroneously wrote to size "
167 		    "pointer");
168 		return (B_FALSE);
169 	}
170 	return (ret);
171 }
172 
173 static boolean_t
open_memstream_badsize(void)174 open_memstream_badsize(void)
175 {
176 	char *c;
177 	return (open_memstream_badopen(&c, NULL, EINVAL));
178 }
179 
180 static boolean_t
open_memstream_allnull(void)181 open_memstream_allnull(void)
182 {
183 	return (open_memstream_badopen(NULL, NULL, EINVAL));
184 }
185 
186 static boolean_t
open_memstream_cantalloc(void)187 open_memstream_cantalloc(void)
188 {
189 	boolean_t ret;
190 	char *c;
191 	size_t len;
192 
193 	umem_setmtbf(1);
194 	ret = open_memstream_badopen(&c, &len, EAGAIN);
195 	umem_setmtbf(0);
196 	return (ret);
197 }
198 
199 static boolean_t
open_wmemstream_badopen(wchar_t ** bufp,size_t * sizep,int err)200 open_wmemstream_badopen(wchar_t **bufp, size_t *sizep, int err)
201 {
202 	FILE *f = open_wmemstream(bufp, sizep);
203 
204 	if (f != NULL) {
205 		warnx("open_wmemstream() succeeded erroneously");
206 		(void) fclose(f);
207 		return (B_FALSE);
208 	}
209 
210 	if (errno != err) {
211 		warnx("open_wmemstream() open failed with wrong errno, "
212 		    "found %d (%s), expected %d (%s)", errno, strerror(errno),
213 		    err, strerror(err));
214 		return (B_FALSE);
215 	}
216 
217 	return (B_TRUE);
218 }
219 
220 static boolean_t
open_wmemstream_badbuf(void)221 open_wmemstream_badbuf(void)
222 {
223 	size_t s, check;
224 	boolean_t ret;
225 
226 	arc4random_buf(&s, sizeof (s));
227 	check = s;
228 	ret = open_wmemstream_badopen(NULL, &s, EINVAL);
229 	if (check != s) {
230 		warnx("open_wmemstream() open erroneously wrote to size "
231 		    "pointer");
232 		return (B_FALSE);
233 	}
234 	return (ret);
235 }
236 
237 static boolean_t
open_wmemstream_badsize(void)238 open_wmemstream_badsize(void)
239 {
240 	wchar_t *c;
241 	return (open_wmemstream_badopen(&c, NULL, EINVAL));
242 }
243 
244 static boolean_t
open_wmemstream_allnull(void)245 open_wmemstream_allnull(void)
246 {
247 	return (open_wmemstream_badopen(NULL, NULL, EINVAL));
248 }
249 
250 static boolean_t
open_wmemstream_cantalloc(void)251 open_wmemstream_cantalloc(void)
252 {
253 	boolean_t ret;
254 	wchar_t *c;
255 	size_t len;
256 
257 	umem_setmtbf(1);
258 	ret = open_wmemstream_badopen(&c, &len, EAGAIN);
259 	umem_setmtbf(0);
260 	return (ret);
261 }
262 
263 static boolean_t
fmemopen_fill_putc(FILE * f,size_t len,boolean_t buffer)264 fmemopen_fill_putc(FILE *f, size_t len, boolean_t buffer)
265 {
266 	boolean_t ret = B_TRUE;
267 	size_t i;
268 
269 	for (i = 0; i < BUFSIZ * 2; i++) {
270 		if (fputc('a', f) != 'a') {
271 			break;
272 		}
273 	}
274 
275 	if (buffer) {
276 		if (i < len) {
277 			warnx("write mismatch, had %zu bytes, wrote %zu",
278 			    len, i);
279 			ret = B_FALSE;
280 		}
281 
282 		if (fflush(f) == 0) {
283 			warnx("somehow flushed overly full stream, expected "
284 			    "failure");
285 			ret = B_FALSE;
286 		}
287 	} else if (i != len) {
288 		warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
289 		ret = B_FALSE;
290 	}
291 
292 	if (feof(f) != 0) {
293 		warn("EOF mistakenly set on write");
294 		ret = B_FALSE;
295 	}
296 
297 	if (ferror(f) == 0) {
298 		warn("feof not set on write past the end");
299 		ret = B_FALSE;
300 	}
301 
302 	if (fclose(f) != 0) {
303 		warn("failed to close memory stream");
304 		return (B_FALSE);
305 	}
306 
307 	return (ret);
308 }
309 
310 static boolean_t
fmemopen_fill_fwrite(FILE * f,size_t len,boolean_t buffer)311 fmemopen_fill_fwrite(FILE *f, size_t len, boolean_t buffer)
312 {
313 	boolean_t ret = B_TRUE;
314 	size_t i;
315 	char buf[BUFSIZ];
316 
317 	(void) memset(buf, 'a', sizeof (buf));
318 	i = fwrite(buf, sizeof (buf), 1, f);
319 
320 	if (buffer) {
321 		if (i != 1) {
322 			warnx("write mismatch, expected 1 entry, found %zu", i);
323 			ret = B_FALSE;
324 		}
325 
326 		if (fflush(f) == 0) {
327 			warnx("somehow flushed overly full stream, expected "
328 			    "failure");
329 			ret = B_FALSE;
330 		}
331 	} else if (i != 0 && i != len) {
332 		warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
333 		ret = B_FALSE;
334 	}
335 
336 	if (feof(f) != 0) {
337 		warn("EOF mistakenly set on write");
338 		ret = B_FALSE;
339 	}
340 
341 	if (ferror(f) == 0) {
342 		warn("feof not set on write past the end");
343 		ret = B_FALSE;
344 	}
345 
346 	if (fclose(f) != 0) {
347 		warn("failed to close memory stream");
348 		return (B_FALSE);
349 	}
350 
351 	return (ret);
352 }
353 
354 static boolean_t
fmemopen_fill_alt_fwrite(FILE * f,size_t len,boolean_t buffer)355 fmemopen_fill_alt_fwrite(FILE *f, size_t len, boolean_t buffer)
356 {
357 	boolean_t ret = B_TRUE;
358 	size_t i;
359 	char buf[BUFSIZ];
360 
361 	(void) memset(buf, 'a', sizeof (buf));
362 	i = fwrite(buf, 1, sizeof (buf), f);
363 
364 	if (buffer) {
365 		if (i < len) {
366 			warnx("write mismatch, had %zu bytes, wrote %zu",
367 			    len, i);
368 			ret = B_FALSE;
369 		}
370 
371 		if (fflush(f) == 0) {
372 			warnx("somehow flushed overly full stream, expected "
373 			    "failure");
374 			ret = B_FALSE;
375 		}
376 	} else if (i != len) {
377 		warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
378 		ret = B_FALSE;
379 	}
380 
381 	if (feof(f) != 0) {
382 		warn("EOF mistakenly set on write");
383 		ret = B_FALSE;
384 	}
385 
386 	if (ferror(f) == 0) {
387 		warn("feof not set on write past the end");
388 		ret = B_FALSE;
389 	}
390 
391 	if (fclose(f) != 0) {
392 		warn("failed to close memory stream");
393 		return (B_FALSE);
394 	}
395 
396 	return (ret);
397 }
398 
399 static boolean_t
fmemopen_fill_fputs(FILE * f,size_t len,boolean_t buffer)400 fmemopen_fill_fputs(FILE *f, size_t len, boolean_t buffer)
401 {
402 	boolean_t ret = B_TRUE;
403 	size_t i;
404 	char buf[17];
405 
406 	(void) memset(buf, 'a', sizeof (buf));
407 	buf[16] = '\0';
408 	for (i = 0; i < BUFSIZ * 2; i += 16) {
409 		if (fputs(buf, f) != 16) {
410 			break;
411 		}
412 	}
413 
414 	/*
415 	 * We don't check flushing in the puts case because fputs seems to clear
416 	 * the buffer as a side effect.
417 	 */
418 	if (buffer) {
419 		if (i < len) {
420 			warnx("write mismatch, had %zu bytes, wrote %zu",
421 			    len, i);
422 			ret = B_FALSE;
423 		}
424 	} else if (i != len) {
425 		warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
426 		ret = B_FALSE;
427 	}
428 
429 	if (feof(f) != 0) {
430 		warn("EOF mistakenly set on write");
431 		ret = B_FALSE;
432 	}
433 
434 	if (ferror(f) == 0) {
435 		warn("feof not set on write past the end");
436 		ret = B_FALSE;
437 	}
438 
439 	if (fclose(f) != 0) {
440 		warn("failed to close memory stream");
441 		return (B_FALSE);
442 	}
443 
444 	return (ret);
445 }
446 
447 
448 static boolean_t
fmemopen_fill_default(void)449 fmemopen_fill_default(void)
450 {
451 	FILE *f;
452 
453 	f = fmemopen(NULL, 128, "w+");
454 	if (f == NULL) {
455 		warn("failed to open fmemopen stream");
456 		return (B_FALSE);
457 	}
458 
459 	return (fmemopen_fill_putc(f, 128, B_TRUE));
460 }
461 
462 static boolean_t
fmemopen_fill_lbuf(void)463 fmemopen_fill_lbuf(void)
464 {
465 	FILE *f;
466 
467 	f = fmemopen(NULL, 128, "w+");
468 	if (f == NULL) {
469 		warn("failed to open fmemopen stream");
470 		return (B_FALSE);
471 	}
472 
473 	if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
474 		warn("failed to set buffer to line-buffered mode");
475 	}
476 
477 	return (fmemopen_fill_putc(f, 128, B_TRUE));
478 }
479 
480 static boolean_t
fmemopen_fill_nobuf(void)481 fmemopen_fill_nobuf(void)
482 {
483 	FILE *f;
484 
485 	f = fmemopen(NULL, 128, "w+");
486 	if (f == NULL) {
487 		warn("failed to open fmemopen stream");
488 		return (B_FALSE);
489 	}
490 
491 	if (setvbuf(f, NULL, _IONBF, 0) != 0) {
492 		warn("failed to set buffer to non-buffered mode");
493 	}
494 
495 	return (fmemopen_fill_putc(f, 128, B_FALSE));
496 }
497 
498 static boolean_t
fmemopen_fwrite_default(void)499 fmemopen_fwrite_default(void)
500 {
501 	FILE *f;
502 
503 	f = fmemopen(NULL, 128, "w+");
504 	if (f == NULL) {
505 		warn("failed to open fmemopen stream");
506 		return (B_FALSE);
507 	}
508 
509 	return (fmemopen_fill_fwrite(f, 128, B_TRUE));
510 }
511 
512 static boolean_t
fmemopen_fwrite_lbuf(void)513 fmemopen_fwrite_lbuf(void)
514 {
515 	FILE *f;
516 
517 	f = fmemopen(NULL, 128, "w+");
518 	if (f == NULL) {
519 		warn("failed to open fmemopen stream");
520 		return (B_FALSE);
521 	}
522 
523 	if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
524 		warn("failed to set buffer to line-buffered mode");
525 	}
526 
527 	return (fmemopen_fill_fwrite(f, 128, B_TRUE));
528 }
529 
530 static boolean_t
fmemopen_fwrite_nobuf(void)531 fmemopen_fwrite_nobuf(void)
532 {
533 	FILE *f;
534 
535 	f = fmemopen(NULL, 128, "w+");
536 	if (f == NULL) {
537 		warn("failed to open fmemopen stream");
538 		return (B_FALSE);
539 	}
540 
541 	if (setvbuf(f, NULL, _IONBF, 0) != 0) {
542 		warn("failed to set buffer to non-buffered mode");
543 	}
544 
545 	return (fmemopen_fill_fwrite(f, 128, B_FALSE));
546 }
547 
548 static boolean_t
fmemopen_alt_fwrite_default(void)549 fmemopen_alt_fwrite_default(void)
550 {
551 	FILE *f;
552 
553 	f = fmemopen(NULL, 128, "w+");
554 	if (f == NULL) {
555 		warn("failed to open fmemopen stream");
556 		return (B_FALSE);
557 	}
558 
559 	return (fmemopen_fill_alt_fwrite(f, 128, B_TRUE));
560 }
561 
562 static boolean_t
fmemopen_alt_fwrite_lbuf(void)563 fmemopen_alt_fwrite_lbuf(void)
564 {
565 	FILE *f;
566 
567 	f = fmemopen(NULL, 128, "w+");
568 	if (f == NULL) {
569 		warn("failed to open fmemopen stream");
570 		return (B_FALSE);
571 	}
572 
573 	if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
574 		warn("failed to set buffer to line-buffered mode");
575 	}
576 
577 	return (fmemopen_fill_alt_fwrite(f, 128, B_TRUE));
578 }
579 
580 static boolean_t
fmemopen_alt_fwrite_nobuf(void)581 fmemopen_alt_fwrite_nobuf(void)
582 {
583 	FILE *f;
584 
585 	f = fmemopen(NULL, 128, "w+");
586 	if (f == NULL) {
587 		warn("failed to open fmemopen stream");
588 		return (B_FALSE);
589 	}
590 
591 	if (setvbuf(f, NULL, _IONBF, 0) != 0) {
592 		warn("failed to set buffer to non-buffered mode");
593 	}
594 
595 	return (fmemopen_fill_alt_fwrite(f, 128, B_FALSE));
596 }
597 
598 static boolean_t
fmemopen_fputs_default(void)599 fmemopen_fputs_default(void)
600 {
601 	FILE *f;
602 
603 	f = fmemopen(NULL, 128, "w+");
604 	if (f == NULL) {
605 		warn("failed to open fmemopen stream");
606 		return (B_FALSE);
607 	}
608 
609 	return (fmemopen_fill_fputs(f, 128, B_TRUE));
610 }
611 
612 static boolean_t
fmemopen_fputs_lbuf(void)613 fmemopen_fputs_lbuf(void)
614 {
615 	FILE *f;
616 
617 	f = fmemopen(NULL, 128, "w+");
618 	if (f == NULL) {
619 		warn("failed to open fmemopen stream");
620 		return (B_FALSE);
621 	}
622 
623 	if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
624 		warn("failed to set buffer to line-buffered mode");
625 	}
626 
627 	return (fmemopen_fill_fputs(f, 128, B_TRUE));
628 }
629 
630 static boolean_t
fmemopen_fputs_nobuf(void)631 fmemopen_fputs_nobuf(void)
632 {
633 	FILE *f;
634 
635 	f = fmemopen(NULL, 128, "w+");
636 	if (f == NULL) {
637 		warn("failed to open fmemopen stream");
638 		return (B_FALSE);
639 	}
640 
641 	if (setvbuf(f, NULL, _IONBF, 0) != 0) {
642 		warn("failed to set buffer to non-buffered mode");
643 	}
644 
645 	return (fmemopen_fill_fputs(f, 128, B_FALSE));
646 }
647 
648 static boolean_t
memstream_check_seek(FILE * f,size_t len,int whence)649 memstream_check_seek(FILE *f, size_t len, int whence)
650 {
651 	off_t o;
652 	long l;
653 	boolean_t ret = B_TRUE;
654 
655 	if (fseeko(f, 0, whence) != 0) {
656 		warn("failed to seek, whence: %d", whence);
657 		return (B_FALSE);
658 	}
659 
660 	if ((o = ftello(f)) == -1) {
661 		warn("failed to get offset from ftello");
662 		ret = B_FALSE;
663 	} else if (o < 0 || (size_t)o != len) {
664 		warnx("found bad stream position: expected %zu, found: %zu",
665 		    len, (size_t)o);
666 		ret = B_FALSE;
667 	}
668 
669 	if ((l = ftell(f)) == -1) {
670 		warn("failed to get offset from ftell");
671 		ret = B_FALSE;
672 	} else if (l < 0 || (size_t)l != len) {
673 		warnx("found bad stream position: expected %zu, found: %zu",
674 		    len, (size_t)l);
675 		ret = B_FALSE;
676 	}
677 
678 	return (ret);
679 }
680 
681 static boolean_t
fmemopen_defseek_r(void)682 fmemopen_defseek_r(void)
683 {
684 	FILE *f;
685 	size_t len = strlen(fmemopen_str1);
686 	boolean_t ret, ret2;
687 
688 	f = fmemopen(fmemopen_str1, len, "r");
689 	if (f == NULL) {
690 		warn("failed to open fmemopen stream");
691 		return (B_FALSE);
692 	}
693 
694 	ret = memstream_check_seek(f, 0, SEEK_CUR);
695 	ret2 = memstream_check_seek(f, len, SEEK_END);
696 	(void) fclose(f);
697 	return (ret && ret2);
698 }
699 
700 static boolean_t
fmemopen_defseek_rp(void)701 fmemopen_defseek_rp(void)
702 {
703 	FILE *f;
704 	size_t len = strlen(fmemopen_str1);
705 	boolean_t ret, ret2;
706 
707 	f = fmemopen(fmemopen_str1, len, "r+");
708 	if (f == NULL) {
709 		warn("failed to open fmemopen stream");
710 		return (B_FALSE);
711 	}
712 
713 	ret = memstream_check_seek(f, 0, SEEK_CUR);
714 	ret2 = memstream_check_seek(f, len, SEEK_END);
715 	(void) fclose(f);
716 	return (ret && ret2);
717 }
718 
719 static boolean_t
fmemopen_defseek_w(void)720 fmemopen_defseek_w(void)
721 {
722 	FILE *f;
723 	size_t len = strlen(fmemopen_str1);
724 	boolean_t ret, ret2;
725 	char *str;
726 
727 	if ((str = strdup(fmemopen_str1)) == NULL) {
728 		warn("failed to duplicate string");
729 		return (B_FALSE);
730 	}
731 
732 	f = fmemopen(str, len, "w");
733 	if (f == NULL) {
734 		warn("failed to open fmemopen stream");
735 		free(str);
736 		return (B_FALSE);
737 	}
738 
739 	ret = memstream_check_seek(f, 0, SEEK_CUR);
740 	ret2 = memstream_check_seek(f, 0, SEEK_END);
741 	(void) fclose(f);
742 	free(str);
743 	return (ret && ret2);
744 }
745 
746 static boolean_t
fmemopen_defseek_wp(void)747 fmemopen_defseek_wp(void)
748 {
749 	FILE *f;
750 	size_t len = strlen(fmemopen_str1);
751 	boolean_t ret, ret2;
752 	char *str;
753 
754 	if ((str = strdup(fmemopen_str1)) == NULL) {
755 		warn("failed to duplicate string");
756 		return (B_FALSE);
757 	}
758 
759 	f = fmemopen(str, len, "w+");
760 	if (f == NULL) {
761 		warn("failed to open fmemopen stream");
762 		free(str);
763 		return (B_FALSE);
764 	}
765 
766 	ret = memstream_check_seek(f, 0, SEEK_CUR);
767 	ret2 = memstream_check_seek(f, 0, SEEK_END);
768 	(void) fclose(f);
769 	free(str);
770 	return (ret && ret2);
771 }
772 
773 static boolean_t
fmemopen_defseek_a(void)774 fmemopen_defseek_a(void)
775 {
776 	FILE *f;
777 	size_t len = strlen(fmemopen_str1);
778 	boolean_t ret, ret2;
779 	char *str;
780 
781 	if ((str = strdup(fmemopen_str1)) == NULL) {
782 		warn("failed to duplicate string");
783 		return (B_FALSE);
784 	}
785 
786 	f = fmemopen(str, len, "a");
787 	if (f == NULL) {
788 		warn("failed to open fmemopen stream");
789 		free(str);
790 		return (B_FALSE);
791 	}
792 
793 	ret = memstream_check_seek(f, len, SEEK_CUR);
794 	ret2 = memstream_check_seek(f, len, SEEK_END);
795 	(void) fclose(f);
796 	free(str);
797 	return (ret && ret2);
798 }
799 
800 static boolean_t
fmemopen_defseek_ap(void)801 fmemopen_defseek_ap(void)
802 {
803 	FILE *f;
804 	size_t len = strlen(fmemopen_str1);
805 	boolean_t ret, ret2;
806 	char *str;
807 
808 	if ((str = strdup(fmemopen_str1)) == NULL) {
809 		warn("failed to duplicate string");
810 		return (B_FALSE);
811 	}
812 
813 	f = fmemopen(str, len, "a+");
814 	if (f == NULL) {
815 		warn("failed to open fmemopen stream");
816 		free(str);
817 		return (B_FALSE);
818 	}
819 
820 	ret = memstream_check_seek(f, len, SEEK_CUR);
821 	ret2 = memstream_check_seek(f, len, SEEK_END);
822 	(void) fclose(f);
823 	free(str);
824 	return (ret && ret2);
825 }
826 
827 static boolean_t
fmemopen_defseek_a_nbyte(void)828 fmemopen_defseek_a_nbyte(void)
829 {
830 	FILE *f;
831 	size_t len = strlen(fmemopen_str1);
832 	boolean_t ret, ret2;
833 	char *str;
834 
835 	if ((str = strdup(fmemopen_str1)) == NULL) {
836 		warn("failed to duplicate string");
837 		return (B_FALSE);
838 	}
839 	str[8] = '\0';
840 
841 	f = fmemopen(str, len, "a");
842 	if (f == NULL) {
843 		warn("failed to open fmemopen stream");
844 		free(str);
845 		return (B_FALSE);
846 	}
847 
848 	ret = memstream_check_seek(f, 8, SEEK_CUR);
849 	ret2 = memstream_check_seek(f, 8, SEEK_END);
850 	(void) fclose(f);
851 	free(str);
852 	return (ret && ret2);
853 }
854 
855 static boolean_t
fmemopen_defseek_ap_nbyte(void)856 fmemopen_defseek_ap_nbyte(void)
857 {
858 	FILE *f;
859 	size_t len = strlen(fmemopen_str1);
860 	boolean_t ret, ret2;
861 	char *str;
862 
863 	if ((str = strdup(fmemopen_str1)) == NULL) {
864 		warn("failed to duplicate string");
865 		return (B_FALSE);
866 	}
867 	str[12] = '\0';
868 
869 	f = fmemopen(str, len, "a+");
870 	if (f == NULL) {
871 		warn("failed to open fmemopen stream");
872 		free(str);
873 		return (B_FALSE);
874 	}
875 
876 	ret = memstream_check_seek(f, 12, SEEK_CUR);
877 	ret2 = memstream_check_seek(f, 12, SEEK_END);
878 	(void) fclose(f);
879 	free(str);
880 	return (ret && ret2);
881 }
882 
883 static boolean_t
fmemopen_defseek_ap_null(void)884 fmemopen_defseek_ap_null(void)
885 {
886 	FILE *f;
887 	size_t len = strlen(fmemopen_str1);
888 	boolean_t ret, ret2;
889 
890 	f = fmemopen(NULL, len, "a+");
891 	if (f == NULL) {
892 		warn("failed to open fmemopen stream");
893 		return (B_FALSE);
894 	}
895 
896 	ret = memstream_check_seek(f, 0, SEEK_CUR);
897 	ret2 = memstream_check_seek(f, 0, SEEK_END);
898 	(void) fclose(f);
899 	return (ret && ret2);
900 }
901 
902 static boolean_t
fmemopen_read_eof_fgetc(void)903 fmemopen_read_eof_fgetc(void)
904 {
905 	FILE *f;
906 	size_t len = strlen(fmemopen_str1);
907 	boolean_t ret = B_TRUE, ret2, ret3;
908 
909 	f = fmemopen(fmemopen_str1, len, "r");
910 	if (f == NULL) {
911 		warn("failed to open fmemopen stream");
912 		return (B_FALSE);
913 	}
914 
915 	while (fgetc(f) != EOF) {
916 		continue;
917 	}
918 
919 	if (feof(f) == 0) {
920 		warnx("stream not at end of EOF");
921 		ret = B_FALSE;
922 	}
923 
924 	ret2 = memstream_check_seek(f, len, SEEK_CUR);
925 	ret3 = memstream_check_seek(f, len, SEEK_END);
926 	(void) fclose(f);
927 	return (ret && ret2 && ret3);
928 }
929 
930 static boolean_t
fmemopen_read_eof_fgets(void)931 fmemopen_read_eof_fgets(void)
932 {
933 	FILE *f;
934 	size_t len = strlen(fmemopen_str1);
935 	boolean_t ret = B_TRUE, ret2, ret3;
936 	char buf[BUFSIZ];
937 
938 	f = fmemopen(fmemopen_str1, len, "r");
939 	if (f == NULL) {
940 		warn("failed to open fmemopen stream");
941 		return (B_FALSE);
942 	}
943 
944 	while (fgets(buf, sizeof (buf), f) != NULL) {
945 		continue;
946 	}
947 
948 	if (feof(f) == 0) {
949 		warnx("stream not at end of EOF");
950 		ret = B_FALSE;
951 	}
952 
953 	ret2 = memstream_check_seek(f, len, SEEK_CUR);
954 	ret3 = memstream_check_seek(f, len, SEEK_END);
955 	(void) fclose(f);
956 	return (ret && ret2 && ret3);
957 }
958 
959 static boolean_t
fmemopen_read_eof_fread(void)960 fmemopen_read_eof_fread(void)
961 {
962 	FILE *f;
963 	size_t len = strlen(fmemopen_str1);
964 	boolean_t ret = B_TRUE, ret2, ret3;
965 	char buf[BUFSIZ];
966 
967 	f = fmemopen(fmemopen_str1, len, "r");
968 	if (f == NULL) {
969 		warn("failed to open fmemopen stream");
970 		return (B_FALSE);
971 	}
972 
973 	while (fread(buf, sizeof (buf), 1, f) != 0) {
974 		continue;
975 	}
976 
977 	if (feof(f) == 0) {
978 		warnx("stream not at end of EOF");
979 		ret = B_FALSE;
980 	}
981 
982 	ret2 = memstream_check_seek(f, len, SEEK_CUR);
983 	ret3 = memstream_check_seek(f, len, SEEK_END);
984 	(void) fclose(f);
985 	return (ret && ret2 && ret3);
986 }
987 
988 static boolean_t
fmemopen_read_eof_fread2(void)989 fmemopen_read_eof_fread2(void)
990 {
991 	FILE *f;
992 	size_t len = strlen(fmemopen_str1);
993 	boolean_t ret = B_TRUE, ret2, ret3;
994 	char buf[BUFSIZ];
995 
996 	f = fmemopen(fmemopen_str1, len, "r");
997 	if (f == NULL) {
998 		warn("failed to open fmemopen stream");
999 		return (B_FALSE);
1000 	}
1001 
1002 	while (fread(buf, 1, sizeof (buf), f) != 0) {
1003 		continue;
1004 	}
1005 
1006 	if (feof(f) == 0) {
1007 		warnx("stream not at end of EOF");
1008 		ret = B_FALSE;
1009 	}
1010 
1011 	ret2 = memstream_check_seek(f, len, SEEK_CUR);
1012 	ret3 = memstream_check_seek(f, len, SEEK_END);
1013 	(void) fclose(f);
1014 	return (ret && ret2 && ret3);
1015 }
1016 
1017 static boolean_t
fmemopen_bad_seeks(void)1018 fmemopen_bad_seeks(void)
1019 {
1020 	FILE *f;
1021 	boolean_t ret = B_TRUE;
1022 	size_t len = strlen(fmemopen_str1);
1023 	uint_t i;
1024 	struct {
1025 		int ret;
1026 		int whence;
1027 		long off;
1028 		long newpos;
1029 	} seeks[] = {
1030 		{ 0, SEEK_CUR, 0, 0 },
1031 		{ -1, SEEK_CUR, -1, 0 },
1032 		{ -1, SEEK_SET, -5, 0 },
1033 		{ -1, SEEK_END, -128, 0 },
1034 		{ -1, SEEK_END, 1, 0 },
1035 		{ -1, SEEK_SET, 128, 0 },
1036 		{ 0, SEEK_SET, 16, 16 },
1037 		{ -1, SEEK_CUR, -20, 16 },
1038 		{ 0, SEEK_CUR, -16, 0 },
1039 	};
1040 
1041 	f = fmemopen(fmemopen_str1, len, "r");
1042 	if (f == NULL) {
1043 		warn("failed to open fmemopen stream");
1044 		return (B_FALSE);
1045 	}
1046 
1047 	for (i = 0; i < ARRAY_SIZE(seeks); i++) {
1048 		int r;
1049 
1050 		r = fseek(f, seeks[i].off, seeks[i].whence);
1051 		if (r != seeks[i].ret) {
1052 			warnx("found bad return value for seek %d/%ld, "
1053 			    "expected %d, found %d", seeks[i].whence,
1054 			    seeks[i].off, seeks[i].ret, r);
1055 			ret = B_FALSE;
1056 		}
1057 
1058 		ret &= memstream_check_seek(f, seeks[i].newpos, SEEK_CUR);
1059 	}
1060 
1061 	(void) fclose(f);
1062 	return (ret);
1063 }
1064 
1065 static boolean_t
fmemopen_open_trunc(void)1066 fmemopen_open_trunc(void)
1067 {
1068 	char buf[16];
1069 	FILE *f;
1070 	boolean_t ret = B_TRUE;
1071 
1072 	(void) memset(buf, 'a', sizeof (buf));
1073 	f = fmemopen(buf, sizeof (buf), "w+");
1074 	if (f == NULL) {
1075 		warn("failed to create fmemopen stream");
1076 		return (B_FALSE);
1077 	}
1078 
1079 	if (buf[0] != '\0') {
1080 		warnx("w+ mode didn't truncate the buffer");
1081 		ret = B_FALSE;
1082 	}
1083 
1084 	(void) fclose(f);
1085 	return (ret);
1086 }
1087 
1088 static boolean_t
fmemopen_write_nul(void)1089 fmemopen_write_nul(void)
1090 {
1091 	char buf[BUFSIZ];
1092 	FILE *f;
1093 	boolean_t ret = B_TRUE;
1094 	size_t npos = sizeof (buf) - 32;
1095 
1096 	(void) memset(buf, 'a', sizeof (buf));
1097 
1098 	f = fmemopen(buf, sizeof (buf), "w");
1099 	if (f == NULL) {
1100 		warn("failed to create fmemopen stream");
1101 		return (B_FALSE);
1102 	}
1103 
1104 	if (fputc('b', f) != 'b') {
1105 		warn("failed to write 'b' character to stream");
1106 		ret = B_FALSE;
1107 	}
1108 
1109 	if (fflush(f) != 0) {
1110 		warn("failed to flush stream");
1111 		ret = B_FALSE;
1112 	}
1113 
1114 	if (buf[0] != 'b' || buf[1] != '\0') {
1115 		warn("stream didn't properly write character and nul");
1116 		ret = B_FALSE;
1117 	}
1118 
1119 	if (fseek(f, sizeof (buf) - 32, SEEK_SET)) {
1120 		warn("failed to seek stream");
1121 		ret = B_FALSE;
1122 	}
1123 
1124 	if (fflush(f) != 0) {
1125 		warn("failed to flush stream");
1126 		ret = B_FALSE;
1127 	}
1128 
1129 	if (buf[npos] != 'a' || buf[npos - 1] != 'a' ||
1130 	    buf[npos + 1] != 'a') {
1131 		warnx("seeking incorrectly inserted a nul");
1132 		ret = B_FALSE;
1133 	}
1134 
1135 	(void) fclose(f);
1136 
1137 	if (buf[npos] != 'a' || buf[npos - 1] != 'a' ||
1138 	    buf[npos + 1] != 'a') {
1139 		warnx("seeking incorrectly inserted a nul");
1140 		ret = B_FALSE;
1141 	}
1142 
1143 	return (ret);
1144 }
1145 
1146 static boolean_t
fmemopen_append_nul(void)1147 fmemopen_append_nul(void)
1148 {
1149 	char buf[32], buf2[32];
1150 	FILE *f;
1151 	boolean_t ret = B_TRUE;
1152 
1153 	(void) memset(buf, 'a', sizeof (buf));
1154 	buf[8] = '\0';
1155 
1156 	f = fmemopen(buf, sizeof (buf), "a");
1157 	if (f == NULL) {
1158 		warn("failed to create fmemopen stream");
1159 		return (B_FALSE);
1160 	}
1161 
1162 	if (fputc('b', f) != 'b') {
1163 		warn("failed to write 'b' character to stream");
1164 		ret = B_FALSE;
1165 	}
1166 
1167 	if (fflush(f) != 0) {
1168 		warn("failed to flush stream");
1169 		ret = B_FALSE;
1170 	}
1171 
1172 	if (buf[8] != 'b' || buf[9] != '\0') {
1173 		warn("stream didn't properly write character and nul");
1174 		ret = B_FALSE;
1175 	}
1176 
1177 	/*
1178 	 * Append mode shouldn't insert a NUL if we write the entire buffer.
1179 	 */
1180 	(void) memset(buf2, 'b', sizeof (buf2));
1181 	if (fwrite(buf2, sizeof (buf2) - ftell(f), 1, f) != 1) {
1182 		warn("failed to write buf2");
1183 		ret = B_FALSE;
1184 	}
1185 
1186 	if (fflush(f) != 0) {
1187 		warn("failed to flush stream");
1188 		ret = B_FALSE;
1189 	}
1190 
1191 	if (buf[sizeof (buf) - 1] != 'b') {
1192 		warnx("found invalid character: %x", buf[sizeof (buf) - 1]);
1193 		ret = B_FALSE;
1194 	}
1195 
1196 	(void) fclose(f);
1197 
1198 	if (buf[sizeof (buf) - 1] != 'b') {
1199 		warnx("found invalid character: %x", buf[sizeof (buf) - 1]);
1200 		ret = B_FALSE;
1201 	}
1202 
1203 	return (ret);
1204 }
1205 
1206 static boolean_t
fmemopen_read_nul(void)1207 fmemopen_read_nul(void)
1208 {
1209 	char buf[32];
1210 	FILE *f;
1211 
1212 	(void) memset(buf, '\0', sizeof (buf));
1213 
1214 	f = fmemopen(buf, sizeof (buf), "r+");
1215 	if (f == NULL) {
1216 		warn("failed to create fmemopen stream");
1217 		return (B_FALSE);
1218 	}
1219 
1220 	if (fgetc(f) != '\0') {
1221 		warnx("failed to read nul character");
1222 		return (B_FALSE);
1223 	}
1224 
1225 	(void) fclose(f);
1226 	return (B_TRUE);
1227 }
1228 
1229 static boolean_t
open_memstream_def_seek(void)1230 open_memstream_def_seek(void)
1231 {
1232 	char *c;
1233 	size_t s;
1234 	FILE *f;
1235 	boolean_t ret, ret2;
1236 
1237 	if ((f = open_memstream(&c, &s)) == NULL) {
1238 		warn("failed to call open_memstream()");
1239 		return (B_FALSE);
1240 	}
1241 
1242 	ret = memstream_check_seek(f, 0, SEEK_CUR);
1243 	ret2 = memstream_check_seek(f, 0, SEEK_END);
1244 	(void) fclose(f);
1245 	free(c);
1246 	return (ret && ret2);
1247 }
1248 
1249 static boolean_t
open_wmemstream_def_seek(void)1250 open_wmemstream_def_seek(void)
1251 {
1252 	wchar_t *c;
1253 	size_t s;
1254 	FILE *f;
1255 	boolean_t ret, ret2;
1256 
1257 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1258 		warn("failed to call open_wmemstream()");
1259 		return (B_FALSE);
1260 	}
1261 
1262 	ret = memstream_check_seek(f, 0, SEEK_CUR);
1263 	ret2 = memstream_check_seek(f, 0, SEEK_END);
1264 	(void) fclose(f);
1265 	free(c);
1266 	return (ret && ret2);
1267 }
1268 
1269 static boolean_t
open_memstream_no_read(void)1270 open_memstream_no_read(void)
1271 {
1272 	char *c;
1273 	size_t s;
1274 	FILE *f;
1275 	boolean_t ret = B_TRUE;
1276 
1277 	if ((f = open_memstream(&c, &s)) == NULL) {
1278 		warn("failed to call open_memstream()");
1279 		return (B_FALSE);
1280 	}
1281 
1282 	if (fgetc(f) != EOF) {
1283 		warnx("read succeeded when it should have failed");
1284 		ret = B_FALSE;
1285 	}
1286 
1287 	if (errno != EBADF) {
1288 		warnx("found wrong errno, expected %d, found %d", EBADF, errno);
1289 		ret = B_FALSE;
1290 	}
1291 
1292 	(void) fclose(f);
1293 	free(c);
1294 	return (ret);
1295 }
1296 
1297 static boolean_t
open_wmemstream_no_read(void)1298 open_wmemstream_no_read(void)
1299 {
1300 	wchar_t *c;
1301 	size_t s;
1302 	FILE *f;
1303 	boolean_t ret = B_TRUE;
1304 
1305 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1306 		warn("failed to call open_wmemstream()");
1307 		return (B_FALSE);
1308 	}
1309 
1310 	if (fgetc(f) != EOF) {
1311 		warnx("read succeeded when it should have failed");
1312 		ret = B_FALSE;
1313 	}
1314 
1315 	if (errno != EBADF) {
1316 		warnx("found wrong errno, expected %d, found %d", EBADF, errno);
1317 		ret = B_FALSE;
1318 	}
1319 
1320 	(void) fclose(f);
1321 	free(c);
1322 	return (ret);
1323 }
1324 
1325 static boolean_t
open_memstream_bad_flush(void)1326 open_memstream_bad_flush(void)
1327 {
1328 	char *c;
1329 	size_t s;
1330 	FILE *f;
1331 	boolean_t ret = B_TRUE;
1332 
1333 	if ((f = open_memstream(&c, &s)) == NULL) {
1334 		warn("failed to call open_memstream()");
1335 		return (B_FALSE);
1336 	}
1337 
1338 	/* Force the buffer to exist */
1339 	if (fputc('a', f) != 'a') {
1340 		warn("failed to write character to buffer");
1341 		ret = B_FALSE;
1342 	}
1343 
1344 	if (fseek(f, BUFSIZ * 2 + 1, SEEK_END) != 0) {
1345 		warn("Failed to seek beyond buffer size");
1346 		ret = B_FALSE;
1347 	}
1348 
1349 	umem_setmtbf(1);
1350 	if (fputc('a', f) != 'a') {
1351 		warn("failed to write character to buffer");
1352 		ret = B_FALSE;
1353 	}
1354 
1355 	if (fflush(f) != EOF) {
1356 		warnx("fflush succeeded when it should have failed");
1357 	}
1358 
1359 	if (errno != EAGAIN) {
1360 		warnx("bad errno, found %d, expected %d", errno, EAGAIN);
1361 	}
1362 	umem_setmtbf(0);
1363 
1364 	(void) fclose(f);
1365 	free(c);
1366 	return (ret);
1367 }
1368 
1369 static boolean_t
open_wmemstream_bad_flush(void)1370 open_wmemstream_bad_flush(void)
1371 {
1372 	wchar_t *c;
1373 	size_t s;
1374 	FILE *f;
1375 	boolean_t ret = B_TRUE;
1376 
1377 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1378 		warn("failed to call open_wmemstream()");
1379 		return (B_FALSE);
1380 	}
1381 
1382 	/* Force the buffer to exist */
1383 	if (fputwc('a', f) != 'a') {
1384 		warn("failed to write character to buffer");
1385 		ret = B_FALSE;
1386 	}
1387 
1388 	if (fseek(f, BUFSIZ * 2 + 1, SEEK_END) != 0) {
1389 		warn("Failed to seek beyond buffer size");
1390 		ret = B_FALSE;
1391 	}
1392 
1393 	umem_setmtbf(1);
1394 	if (fputc('a', f) != 'a') {
1395 		warn("failed to write character to buffer");
1396 		ret = B_FALSE;
1397 	}
1398 
1399 	if (fflush(f) != EOF) {
1400 		warnx("fflush succeeded when it should have failed");
1401 	}
1402 
1403 	if (errno != EAGAIN) {
1404 		warnx("bad errno, found %d, expected %d", errno, EAGAIN);
1405 	}
1406 	umem_setmtbf(0);
1407 
1408 	(void) fclose(f);
1409 	free(c);
1410 	return (ret);
1411 }
1412 
1413 static boolean_t
memstream_bad_seek(void)1414 memstream_bad_seek(void)
1415 {
1416 	FILE *f, *fw;
1417 	boolean_t ret = B_TRUE;
1418 	uint_t i;
1419 	char *c;
1420 	wchar_t *w;
1421 	size_t s1, s2;
1422 	struct {
1423 		int ret;
1424 		int whence;
1425 		long off;
1426 		long newpos;
1427 	} seeks[] = {
1428 		{ 0, SEEK_CUR, 0, 0 },
1429 		{ -1, SEEK_CUR, -1, 0 },
1430 		{ -1, SEEK_SET, -5, 0 },
1431 		{ -1, SEEK_END, -5, 0 },
1432 		{ 0, SEEK_SET, 16, 16 },
1433 		{ -1, SEEK_CUR, -20, 16 },
1434 		{ 0, SEEK_CUR, -16, 0 },
1435 	};
1436 
1437 	f = open_memstream(&c, &s1);
1438 	fw = open_wmemstream(&w, &s2);
1439 	if (f == NULL || fw == NULL) {
1440 		warnx("failed to create memory streams");
1441 		return (B_FALSE);
1442 	}
1443 
1444 	for (i = 0; i < ARRAY_SIZE(seeks); i++) {
1445 		int r;
1446 
1447 		r = fseek(f, seeks[i].off, seeks[i].whence);
1448 		if (r != seeks[i].ret) {
1449 			warnx("found bad return value for seek %d/%ld, "
1450 			    "expected %d, found %d", seeks[i].whence,
1451 			    seeks[i].off, seeks[i].ret, r);
1452 			ret = B_FALSE;
1453 		}
1454 
1455 		ret &= memstream_check_seek(f, seeks[i].newpos, SEEK_CUR);
1456 
1457 		r = fseek(fw, seeks[i].off, seeks[i].whence);
1458 		if (r != seeks[i].ret) {
1459 			warnx("found bad return value for seek %d/%ld, "
1460 			    "expected %d, found %d", seeks[i].whence,
1461 			    seeks[i].off, seeks[i].ret, r);
1462 			ret = B_FALSE;
1463 		}
1464 
1465 		ret &= memstream_check_seek(fw, seeks[i].newpos, SEEK_CUR);
1466 	}
1467 
1468 	(void) fclose(f);
1469 	(void) fclose(fw);
1470 	free(c);
1471 	free(w);
1472 	return (ret);
1473 }
1474 
1475 static boolean_t
open_memstream_append_nul(void)1476 open_memstream_append_nul(void)
1477 {
1478 	char *c;
1479 	size_t s;
1480 	FILE *f;
1481 	boolean_t ret = B_TRUE;
1482 
1483 	if ((f = open_memstream(&c, &s)) == NULL) {
1484 		warn("failed to call open_memstream()");
1485 		return (B_FALSE);
1486 	}
1487 
1488 	if (fputc('a', f) != 'a') {
1489 		warn("failed to write 'a' to stream");
1490 		ret = B_FALSE;
1491 	}
1492 
1493 	if (fflush(f) != 0) {
1494 		warn("failed to flush stream");
1495 		ret = B_FALSE;
1496 	}
1497 
1498 	if (c[s] != '\0') {
1499 		warnx("missing nul character, found %x", c[s]);
1500 		ret = B_FALSE;
1501 	}
1502 
1503 	if (fseek(f, arc4random_uniform(2 * BUFSIZ), SEEK_SET) != 0) {
1504 		warn("failed to seek");
1505 		ret = B_FALSE;
1506 	}
1507 
1508 	if (fflush(f) != 0) {
1509 		warn("failed to flush stream");
1510 		ret = B_FALSE;
1511 	}
1512 
1513 	if (c[s] != '\0') {
1514 		warnx("missing nul character, found %x", c[s]);
1515 		ret = B_FALSE;
1516 	}
1517 
1518 	(void) fclose(f);
1519 	free(c);
1520 	return (ret);
1521 }
1522 
1523 static boolean_t
open_wmemstream_append_nul(void)1524 open_wmemstream_append_nul(void)
1525 {
1526 	wchar_t *c;
1527 	size_t s;
1528 	FILE *f;
1529 	boolean_t ret = B_TRUE;
1530 
1531 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1532 		warn("failed to call open_wmemstream()");
1533 		return (B_FALSE);
1534 	}
1535 
1536 	if (fputwc('a', f) != 'a') {
1537 		warn("failed to write 'a' to stream");
1538 		ret = B_FALSE;
1539 	}
1540 
1541 	if (fflush(f) != 0) {
1542 		warn("failed to flush stream");
1543 		ret = B_FALSE;
1544 	}
1545 
1546 	if (c[s] != L'\0') {
1547 		warnx("missing nul character, found %x", c[s]);
1548 		ret = B_FALSE;
1549 	}
1550 
1551 	if (fseek(f, arc4random_uniform(2 * BUFSIZ), SEEK_SET) != 0) {
1552 		warn("failed to seek");
1553 		ret = B_FALSE;
1554 	}
1555 
1556 	if (fflush(f) != 0) {
1557 		warn("failed to flush stream");
1558 		ret = B_FALSE;
1559 	}
1560 
1561 	if (c[s] != L'\0') {
1562 		warnx("missing nul character, found %x", c[s]);
1563 		ret = B_FALSE;
1564 	}
1565 
1566 	(void) fclose(f);
1567 	free(c);
1568 	return (ret);
1569 }
1570 
1571 static boolean_t
open_wmemstream_embed_nuls(void)1572 open_wmemstream_embed_nuls(void)
1573 {
1574 	const char str[] = { 'H', 'e', 'l', 'l', 'o', '\0', 'w',
1575 	    'o', 'r', 'd' };
1576 	const wchar_t wstr[] = { L'H', L'e', L'l', L'l', L'o', L'\0', L'w',
1577 	    L'o', L'r', L'd' };
1578 	wchar_t *c;
1579 	size_t s;
1580 	FILE *f;
1581 	boolean_t ret = B_TRUE;
1582 
1583 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1584 		warn("failed to call open_wmemstream()");
1585 		return (B_FALSE);
1586 	}
1587 
1588 	if (fwrite(str, sizeof (char), ARRAY_SIZE(str), f) == 0) {
1589 		warn("failed to write data buffer");
1590 		ret = B_FALSE;
1591 	}
1592 
1593 	if (fflush(f) != 0) {
1594 		warn("failed to flush data buffer");
1595 		ret = B_FALSE;
1596 	}
1597 
1598 	if (ARRAY_SIZE(wstr) != s) {
1599 		warnx("size mismatch, wrote %zu chars, found %zu chars",
1600 		    ARRAY_SIZE(wstr), s);
1601 		ret = B_FALSE;
1602 	}
1603 
1604 	if (bcmp(wstr, c, sizeof (wstr)) != 0) {
1605 		warnx("data not written in expected format");
1606 		ret = B_FALSE;
1607 	}
1608 
1609 	(void) fclose(f);
1610 	free(c);
1611 	return (ret);
1612 }
1613 
1614 static boolean_t
open_wmemstream_wide_write(void)1615 open_wmemstream_wide_write(void)
1616 {
1617 	size_t slen = wcslen(wstream_str);
1618 	wchar_t *c;
1619 	size_t s;
1620 	FILE *f;
1621 	boolean_t ret = B_TRUE;
1622 
1623 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1624 		warn("failed to call open_wmemstream()");
1625 		return (B_FALSE);
1626 	}
1627 
1628 	if (fputws(wstream_str, f) == -1) {
1629 		warn("failed to write string");
1630 		ret = B_FALSE;
1631 	}
1632 
1633 	if (fflush(f) != 0) {
1634 		warn("failed to flush stream");
1635 		ret = B_FALSE;
1636 	}
1637 
1638 	if (s != slen) {
1639 		warnx("size mismatch, expected %zu chars, but found %zu",
1640 		    slen, s);
1641 		ret = B_FALSE;
1642 	}
1643 
1644 	if (wcscmp(wstream_str, c) != 0) {
1645 		warnx("basic write doesn't match!");
1646 		ret = B_FALSE;
1647 	}
1648 
1649 	ret &= memstream_check_seek(f, slen, SEEK_CUR);
1650 	ret &= memstream_check_seek(f, slen, SEEK_END);
1651 
1652 	(void) fclose(f);
1653 	free(c);
1654 	return (ret);
1655 }
1656 
1657 /*
1658  * Make sure that if we seek somewhere and flush that it doesn't cause us to
1659  * grow.
1660  */
1661 static boolean_t
open_wmemstream_seek_grow(void)1662 open_wmemstream_seek_grow(void)
1663 {
1664 	size_t slen = wcslen(wstream_str);
1665 	wchar_t *c;
1666 	size_t s;
1667 	FILE *f;
1668 	boolean_t ret = B_TRUE;
1669 
1670 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1671 		warn("failed to call open_wmemstream()");
1672 		return (B_FALSE);
1673 	}
1674 
1675 	if (fflush(f) != 0) {
1676 		warn("failed to flush stream");
1677 		ret = B_FALSE;
1678 	}
1679 
1680 	if (s != 0) {
1681 		warn("bad initial size");
1682 		ret = B_FALSE;
1683 	}
1684 
1685 	ret &= memstream_check_seek(f, 0, SEEK_CUR);
1686 	ret &= memstream_check_seek(f, 0, SEEK_END);
1687 	if (fseek(f, 2048, SEEK_SET) != 0) {
1688 		warn("failed to seek");
1689 	}
1690 
1691 	ret &= memstream_check_seek(f, 2048, SEEK_CUR);
1692 
1693 	if (fflush(f) != 0) {
1694 		warn("failed to flush stream");
1695 		ret = B_FALSE;
1696 	}
1697 
1698 	if (s != 0) {
1699 		warnx("bad size after seek");
1700 		ret = B_FALSE;
1701 	}
1702 
1703 	if (fputws(wstream_str, f) == -1) {
1704 		warn("failed to write string");
1705 		ret = B_FALSE;
1706 	}
1707 
1708 	if (fflush(f) != 0) {
1709 		warn("failed to flush stream");
1710 		ret = B_FALSE;
1711 	}
1712 
1713 	if (s != slen + 2048) {
1714 		warnx("size is off after seek and write, found %zu", s);
1715 		ret = B_FALSE;
1716 	}
1717 
1718 	ret &= memstream_check_seek(f, s, SEEK_CUR);
1719 	ret &= memstream_check_seek(f, s, SEEK_END);
1720 
1721 	(void) fclose(f);
1722 	free(c);
1723 	return (ret);
1724 }
1725 
1726 static boolean_t
open_wmemstream_byte_writes(void)1727 open_wmemstream_byte_writes(void)
1728 {
1729 	wchar_t *c;
1730 	size_t s, len, i;
1731 	FILE *f;
1732 	boolean_t ret = B_TRUE;
1733 
1734 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1735 		warn("failed to call open_wmemstream()");
1736 		return (B_FALSE);
1737 	}
1738 
1739 	/*
1740 	 * Use non-buffered mode so that way we can make sure to detect mbs
1741 	 * state errors right away.
1742 	 */
1743 	if (setvbuf(f, NULL, _IONBF, 0) != 0) {
1744 		warnx("failed to set to non-buffered mode");
1745 		ret = B_FALSE;
1746 	}
1747 
1748 	len = wcslen(wstream_str);
1749 	for (i = 0; i < len; i++) {
1750 		char buf[MB_CUR_MAX + 1];
1751 		int mblen, curmb;
1752 
1753 		mblen = wctomb(buf, wstream_str[i]);
1754 
1755 		if (mblen == -1) {
1756 			warn("failed to convert wc %zu", i);
1757 			ret = B_FALSE;
1758 			continue;
1759 		}
1760 		for (curmb = 0; curmb < mblen; curmb++) {
1761 			if (fputc(buf[curmb], f) == EOF) {
1762 				warn("failed to write byte %d of wc %zu",
1763 				    curmb, i);
1764 				ret = B_FALSE;
1765 			}
1766 		}
1767 	}
1768 
1769 	if (fflush(f) != 0) {
1770 		warn("failed to flush stream");
1771 		ret = B_FALSE;
1772 	}
1773 
1774 	if (s != len) {
1775 		warnx("found wrong number of wide characters, expected %zu, "
1776 		    "found %zu", len + 1, s);
1777 		ret = B_FALSE;
1778 	}
1779 
1780 	if (wcscmp(c, wstream_str) != 0) {
1781 		warnx("the wide character strings don't compare equally");
1782 		ret = B_FALSE;
1783 	}
1784 
1785 	(void) fclose(f);
1786 	free(c);
1787 	return (ret);
1788 }
1789 
1790 static boolean_t
open_wmemstream_bad_seq(void)1791 open_wmemstream_bad_seq(void)
1792 {
1793 	wchar_t *c, test = wstr_const[0];
1794 	size_t s;
1795 	FILE *f;
1796 	char buf[MB_CUR_MAX + 1];
1797 	boolean_t ret = B_TRUE;
1798 
1799 	if (wctomb(buf, test) == -1) {
1800 		warn("failed to convert 光 to multi-byte sequence");
1801 		return (B_FALSE);
1802 	}
1803 
1804 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1805 		warn("failed to call open_wmemstream()");
1806 		return (B_FALSE);
1807 	}
1808 
1809 	/*
1810 	 * Make sure to use a non-buffered mode so that way writes immediately
1811 	 * get sent to the underlying stream.
1812 	 */
1813 	if (setvbuf(f, NULL, _IONBF, 0) != 0) {
1814 		warnx("failed to set to non-buffered mode");
1815 		ret = B_FALSE;
1816 	}
1817 
1818 	if (fputc(buf[0], f) == EOF) {
1819 		warn("failed to write 0x%x to buffer", buf[0]);
1820 		ret = B_FALSE;
1821 	}
1822 
1823 	if (fputc(buf[0], f) != EOF) {
1824 		warnx("successfully wrote 0x%x to buffer, but should have "
1825 		    "failed", buf[0]);
1826 		ret = B_FALSE;
1827 	}
1828 
1829 	if (errno != EIO) {
1830 		warnx("found wrong errno, expected EIO, but found 0x%x", errno);
1831 		ret = B_FALSE;
1832 	}
1833 
1834 	(void) fclose(f);
1835 	free(c);
1836 	return (ret);
1837 }
1838 
1839 static boolean_t
open_wmemstream_bad_seq_fflush(void)1840 open_wmemstream_bad_seq_fflush(void)
1841 {
1842 	wchar_t *c, test = wstr_const[0];
1843 	size_t s;
1844 	FILE *f;
1845 	char buf[MB_CUR_MAX + 1];
1846 	boolean_t ret = B_TRUE;
1847 
1848 	if (wctomb(buf, test) == -1) {
1849 		warn("failed to convert 光 to multi-byte sequence");
1850 		return (B_FALSE);
1851 	}
1852 
1853 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1854 		warn("failed to call open_wmemstream()");
1855 		return (B_FALSE);
1856 	}
1857 
1858 	if (fputc(buf[0], f) == EOF) {
1859 		warn("failed to write 0x%x to buffer", buf[0]);
1860 		ret = B_FALSE;
1861 	}
1862 
1863 	if (fputc(buf[0], f) == EOF) {
1864 		warn("failed to write bad byte 0x%x to buffer", buf[0]);
1865 		ret = B_FALSE;
1866 	}
1867 
1868 	if (fflush(f) == 0) {
1869 		warn("fflush succeeded, expected failure");
1870 		ret = B_FALSE;
1871 	}
1872 
1873 	if (errno != EIO) {
1874 		warn("found wrong errno, expected EIO, but found 0x%x", errno);
1875 		ret = B_FALSE;
1876 	}
1877 
1878 	(void) fclose(f);
1879 	free(c);
1880 	return (ret);
1881 }
1882 
1883 /*
1884  * When writing individual bytes out, we need to make sure that we don't
1885  * incorrectly count buffered data as offsets like we do for other byte oriented
1886  * consumers of the ftell family.
1887  */
1888 static boolean_t
open_wmemstream_ftell(void)1889 open_wmemstream_ftell(void)
1890 {
1891 	wchar_t *c, test = wstr_const[0];
1892 	size_t s, i, wclen;
1893 	FILE *f;
1894 	char buf[MB_CUR_MAX + 1];
1895 	boolean_t ret = B_TRUE;
1896 	long loc;
1897 
1898 	if ((wclen = wctomb(buf, test)) == -1) {
1899 		warn("failed to convert 光 to multi-byte sequence");
1900 		return (B_FALSE);
1901 	}
1902 
1903 	if ((f = open_wmemstream(&c, &s)) == NULL) {
1904 		warn("failed to call open_wmemstream()");
1905 		return (B_FALSE);
1906 	}
1907 
1908 	if ((loc = ftell(f)) != 0) {
1909 		warnx("stream at bad loc after start, found %ld, expected 0",
1910 		    loc);
1911 		ret = B_FALSE;
1912 	}
1913 
1914 	if (fputwc(test, f) == WEOF) {
1915 		warn("failed to write wide character to stream");
1916 		ret = B_FALSE;
1917 	}
1918 
1919 	if ((loc = ftell(f)) != 1) {
1920 		warnx("stream at bad loc after writing a single wide char, "
1921 		    "found %ld, expected 1", loc);
1922 		ret = B_FALSE;
1923 	}
1924 
1925 	for (i = 0; i < wclen - 1; i++) {
1926 		if (fputc(buf[i], f) == EOF) {
1927 			warn("failed to write mb char 0x%x", buf[i]);
1928 			ret = B_FALSE;
1929 		}
1930 
1931 		if ((loc = ftell(f)) != 1) {
1932 			warnx("stream at bad loc after putting partial mb seq, "
1933 			    "found %ld, expected 1", loc);
1934 			ret = B_FALSE;
1935 		}
1936 	}
1937 
1938 	/*
1939 	 * Only after we advance the final char should it be two.
1940 	 */
1941 	if (fputc(buf[i], f) == EOF) {
1942 		warn("failed to write mb char 0x%x", buf[i]);
1943 		ret = B_FALSE;
1944 	}
1945 
1946 	if ((loc = ftell(f)) != 2) {
1947 		warnx("stream at bad loc after writing a mb seq, "
1948 		    "found %ld, expected 2", loc);
1949 		ret = B_FALSE;
1950 	}
1951 
1952 	if (fflush(f) != 0) {
1953 		warn("failed to flush stream");
1954 		ret = B_FALSE;
1955 	}
1956 
1957 	if (s != 2) {
1958 		warnx("size of stream is wrong, found %zu, expected 2", s);
1959 		ret = B_FALSE;
1960 	}
1961 
1962 	if (s != loc) {
1963 		warnx("size of buffer, %zu does not match pre-fflush "
1964 		    "ftell loc: %ld", s, loc);
1965 		ret = B_FALSE;
1966 	}
1967 
1968 	loc = ftell(f);
1969 	if (s != loc) {
1970 		warnx("size of buffer, %zu does not match post-fflush "
1971 		    "ftell loc: %ld", s, loc);
1972 		ret = B_FALSE;
1973 	}
1974 
1975 	(void) fclose(f);
1976 	free(c);
1977 	return (ret);
1978 }
1979 
1980 
1981 typedef struct memstream_test {
1982 	memstream_test_f	mt_func;
1983 	const char		*mt_test;
1984 } memstream_test_t;
1985 
1986 static const memstream_test_t memstream_tests[] = {
1987 	{ fmemopen_badmode, "fmemopen: bad mode argument" },
1988 	{ fmemopen_zerobuf1, "fmemopen: bad buffer size, valid buf" },
1989 	{ fmemopen_zerobuf2, "fmemopen: bad buffer size, NULL buf" },
1990 	{ fmemopen_nullbuf1, "fmemopen: invalid NULL buf, mode: r" },
1991 	{ fmemopen_nullbuf2, "fmemopen: invalid NULL buf, mode: w" },
1992 	{ fmemopen_nullbuf3, "fmemopen: invalid NULL buf, mode: a" },
1993 	{ fmemopen_nullbuf4, "fmemopen: invalid NULL buf, mode: ax" },
1994 	{ fmemopen_sizemax, "fmemopen: bad open ask for SIZE_MAX bytes" },
1995 	{ fmemopen_cantalloc, "fmemopen: simulate malloc failure at open" },
1996 	{ open_memstream_badbuf, "open_memstream: bad buf" },
1997 	{ open_memstream_badsize, "open_memstream: bad size" },
1998 	{ open_memstream_allnull, "open_memstream: bad buf and size" },
1999 	{ open_memstream_cantalloc, "open_memstream: simulate malloc failure "
2000 	    "at " "open" },
2001 	{ open_wmemstream_badbuf, "open_wmemstream: bad buf" },
2002 	{ open_wmemstream_badsize, "open_wmemstream: bad size" },
2003 	{ open_wmemstream_allnull, "open_wmemstream: bad buf and size" },
2004 	{ open_wmemstream_cantalloc, "open_wmemstream: simulate malloc "
2005 	    "failure at open" },
2006 	{ fmemopen_fill_default, "fmemopen: write beyond end of buffer: putc "
2007 	    "(buf smaller than BUFSIZ)" },
2008 	{ fmemopen_fill_lbuf, "fmemopen: write beyond end of buffer: putc "
2009 	    "(line buffering)" },
2010 	{ fmemopen_fill_nobuf, "fmemopen: write beyond end of buffer: putc "
2011 	    "(no stdio buffering)" },
2012 	{ fmemopen_fwrite_default, "fmemopen: write beyond end of buffer: "
2013 	    "fwrite (buf smaller than BUFSIZ)" },
2014 	{ fmemopen_fwrite_lbuf, "fmemopen: write beyond end of buffer: fwrite "
2015 	    "(line buffering)" },
2016 	{ fmemopen_fwrite_nobuf, "fmemopen: write beyond end of buffer: fwrite "
2017 	    "(no stdio buffering)" },
2018 	{ fmemopen_alt_fwrite_default, "fmemopen: write beyond end of buffer: "
2019 	    "fwrite 2 (buf smaller than BUFSIZ)" },
2020 	{ fmemopen_alt_fwrite_lbuf, "fmemopen: write beyond end of buffer: "
2021 	    "fwrite 2 (line buffering)" },
2022 	{ fmemopen_alt_fwrite_nobuf, "fmemopen: write beyond end of buffer: "
2023 	    "fwrite 2 (no stdio buffering)" },
2024 	{ fmemopen_fputs_default, "fmemopen: write beyond end of buffer: fputs "
2025 	    "(buf smaller than BUFSIZ)" },
2026 	{ fmemopen_fputs_lbuf, "fmemopen: write beyond end of buffer: fputs "
2027 	    "(line buffering)" },
2028 	{ fmemopen_fputs_nobuf, "fmemopen: write beyond end of buffer: fputs "
2029 	    "(no stdio buffering)" },
2030 	{ fmemopen_defseek_r, "fmemopen: default position and log. size, "
2031 	    "mode: r"},
2032 	{ fmemopen_defseek_rp, "fmemopen: default position and log. size, "
2033 	    "mode: r+"},
2034 	{ fmemopen_defseek_w, "fmemopen: default position and log. size, "
2035 	    "mode: w"},
2036 	{ fmemopen_defseek_wp, "fmemopen: default position and log. size, "
2037 	    "mode: w+"},
2038 	{ fmemopen_defseek_a, "fmemopen: default position and log. size, "
2039 	    "mode: a"},
2040 	{ fmemopen_defseek_ap, "fmemopen: default position and log. size, "
2041 	    "mode: a+"},
2042 	{ fmemopen_defseek_a_nbyte, "fmemopen: default position and log. size, "
2043 	    "mode: a, nul byte"},
2044 	{ fmemopen_defseek_ap_nbyte, "fmemopen: default position and log. "
2045 	    "size, mode: a+, nul byte"},
2046 	{ fmemopen_defseek_ap_null, "fmemopen: default position and log. size, "
2047 	    "mode: a+, NULL buf"},
2048 	{ fmemopen_read_eof_fgetc, "fmemopen: read until EOF with fgetc" },
2049 	{ fmemopen_read_eof_fgets, "fmemopen: read until EOF with fgets" },
2050 	{ fmemopen_read_eof_fread, "fmemopen: read until EOF with fread" },
2051 	{ fmemopen_read_eof_fread2, "fmemopen: read until EOF with fread 2" },
2052 	{ fmemopen_bad_seeks, "fmemopen: invalid seeks" },
2053 	{ fmemopen_open_trunc, "fmemopen: w+ mode truncates buffer" },
2054 	{ fmemopen_write_nul, "fmemopen: NULs properly inserted (w)" },
2055 	{ fmemopen_append_nul, "fmemopen: NULs properly inserted (a)" },
2056 	{ fmemopen_read_nul, "fmemopen: read NUL character normally" },
2057 	{ open_memstream_def_seek, "open_memstream: default position and "
2058 	    "logical size" },
2059 	{ open_wmemstream_def_seek, "wopen_memstream: default position and "
2060 	    "logical size" },
2061 	{ open_memstream_no_read, "open_memstream: read doesn't work" },
2062 	{ open_wmemstream_no_read, "open_wmemstream: read doesn't work" },
2063 	{ open_memstream_bad_flush, "open_memstream: flush failure due to "
2064 	    "induced memory failure" },
2065 	{ open_wmemstream_bad_flush, "open_wmemstream: flush failure due to "
2066 	    "induced memory failure" },
2067 	{ memstream_bad_seek, "open_[w]memstream: bad seeks" },
2068 	{ open_memstream_append_nul, "open_memstream: appends NULs" },
2069 	{ open_wmemstream_append_nul, "open_wmemstream: appends NULs" },
2070 	{ open_wmemstream_embed_nuls, "open_wmemstream: handles embedded "
2071 	    "NULs" },
2072 	{ open_wmemstream_wide_write, "open_wmemstream: write wide chars" },
2073 	{ open_wmemstream_seek_grow, "open_wmemstream: seeking doesn't grow" },
2074 	{ open_wmemstream_byte_writes, "open_wmemstream: Write mb sequences" },
2075 	{ open_wmemstream_bad_seq, "open_wmemstream: detect bad utf-8 "
2076 	    "sequence" },
2077 	{ open_wmemstream_bad_seq_fflush, "open_wmemstream: detect bad utf-8 "
2078 	    "sequence 2 (fflush)" },
2079 	{ open_wmemstream_ftell, "open_wmemstream: ftell buffering behavior" }
2080 };
2081 
2082 int
main(void)2083 main(void)
2084 {
2085 	uint_t i;
2086 	uint_t passes = 0;
2087 	uint_t ntests = ARRAY_SIZE(memstream_tests);
2088 
2089 	/*
2090 	 * Set a UTF-8 locale to make sure to exercise open_wmemstream()'s
2091 	 * mbstate logic in a more interesting way than ASCII.
2092 	 */
2093 	(void) setlocale(LC_ALL, "en_US.UTF-8");
2094 	for (i = 0; i < ntests; i++) {
2095 		boolean_t r;
2096 
2097 		r = memstream_tests[i].mt_func();
2098 		(void) fprintf(stderr, "TEST %s: %s\n", r ? "PASSED" : "FAILED",
2099 		    memstream_tests[i].mt_test);
2100 		if (r) {
2101 			passes++;
2102 		}
2103 	}
2104 
2105 	(void) printf("%d/%d test%s passed\n", passes, ntests,
2106 	    passes > 1 ? "s" : "");
2107 	return (passes == ntests ? EXIT_SUCCESS : EXIT_FAILURE);
2108 }
2109