1 /* $OpenBSD: strlcpytest.c,v 1.5 2021/09/27 19:33:58 millert Exp $ */
2
3 /*
4 * Copyright (c) 2014 Todd C. Miller <millert@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <signal.h>
25 #include <setjmp.h>
26 #include <unistd.h>
27
28 volatile sig_atomic_t got_signal;
29 sigjmp_buf jmpenv;
30
31 void
handler(int signo)32 handler(int signo)
33 {
34 got_signal = signo;
35 siglongjmp(jmpenv, 1);
36 }
37
38 int
main(int argc,char * argv[])39 main(int argc, char *argv[])
40 {
41 char *buf, *buf2, *cp, *ep;
42 struct sigaction sa;
43 size_t len, bufsize;
44 volatile int failures = 0;
45
46 bufsize = getpagesize(); /* trigger guard pages easily */
47 buf = malloc(bufsize);
48 buf2 = malloc(bufsize);
49 if (buf == NULL || buf2 == NULL) {
50 fprintf(stderr, "unable to allocate memory\n");
51 return 1;
52 }
53 memset(buf, 'z', bufsize);
54 ep = buf + bufsize;
55
56 /* Test copying to a zero-length NULL buffer. */
57 len = strlcpy(NULL, "abcd", 0);
58 if (len != 4) {
59 fprintf(stderr,
60 "strlcpy: failed zero-length buffer test (1a)\n");
61 failures++;
62 }
63
64 /* Test copying small string to a large buffer. */
65 len = strlcpy(buf, "abcd", bufsize);
66 if (len != 4) {
67 fprintf(stderr, "strlcpy: failed large buffer test (2a)\n");
68 failures++;
69 }
70 /* Make sure we only wrote where expected. */
71 if (memcmp(buf, "abcd", sizeof("abcd")) != 0) {
72 fprintf(stderr, "strlcpy: failed large buffer test (2b)\n");
73 failures++;
74 }
75 for (cp = buf + len + 1; cp < ep; cp++) {
76 if (*cp != 'z') {
77 fprintf(stderr,
78 "strlcpy: failed large buffer test (2c)\n");
79 failures++;
80 break;
81 }
82 }
83
84 /* Test copying large string to a small buffer. */
85 memset(buf, 'z', bufsize);
86 memset(buf2, 'x', bufsize - 1);
87 buf2[bufsize - 1] = '\0';
88 len = strlcpy(buf, buf2, bufsize / 2);
89 if (len != bufsize - 1) {
90 fprintf(stderr, "strlcpy: failed small buffer test (3a)\n");
91 failures++;
92 }
93 /* Make sure we only wrote where expected. */
94 len = (bufsize / 2) - 1;
95 if (memcmp(buf, buf2, len) != 0 || buf[len] != '\0') {
96 fprintf(stderr, "strlcpy: failed small buffer test (3b)\n");
97 failures++;
98 }
99 for (cp = buf + len + 1; cp < ep; cp++) {
100 if (*cp != 'z') {
101 fprintf(stderr,
102 "strlcpy: failed small buffer test (3c)\n");
103 failures++;
104 break;
105 }
106 }
107
108 /* Test copying to a 1-byte buffer. */
109 memset(buf, 'z', bufsize);
110 len = strlcpy(buf, "abcd", 1);
111 if (len != 4) {
112 fprintf(stderr, "strlcpy: failed 1-byte buffer test (4a)\n");
113 failures++;
114 }
115 /* Make sure we only wrote where expected. */
116 if (buf[0] != '\0') {
117 fprintf(stderr, "strlcpy: failed 1-byte buffer test (4b)\n");
118 failures++;
119 }
120 for (cp = buf + 1; cp < ep; cp++) {
121 if (*cp != 'z') {
122 fprintf(stderr,
123 "strlcpy: failed 1-byte buffer test (4c)\n");
124 failures++;
125 break;
126 }
127 }
128
129 /*
130 * The following tests should result in SIGSEGV, however some
131 * systems may erroneously report SIGBUS.
132 * These tests assume that strlcpy() is signal-safe.
133 */
134 memset(&sa, 0, sizeof(sa));
135 sigemptyset(&sa.sa_mask);
136 sa.sa_handler = handler;
137 sigaction(SIGSEGV, &sa, NULL);
138 sigaction(SIGBUS, &sa, NULL);
139
140 /* Test copying to a NULL buffer with non-zero size. */
141 got_signal = 0;
142 if (sigsetjmp(jmpenv, 1) == 0) {
143 len = strlcpy(NULL, "abcd", sizeof(buf));
144 fprintf(stderr, "strlcpy: failed NULL dst test (5a), "
145 "expected signal %d, got len %zu\n", SIGSEGV, len);
146 failures++;
147 } else if (got_signal != SIGSEGV) {
148 fprintf(stderr, "strlcpy: failed NULL dst test (5b), "
149 "expected signal %d, got %d\n", SIGSEGV, got_signal);
150 failures++;
151 }
152
153 /* Test copying from a NULL src. */
154 got_signal = 0;
155 if (sigsetjmp(jmpenv, 1) == 0) {
156 len = strlcpy(buf, NULL, sizeof(buf));
157 fprintf(stderr, "strlcpy: failed NULL src test (6a), "
158 "expected signal %d, got len %zu\n", SIGSEGV, len);
159 failures++;
160 } else if (got_signal != SIGSEGV) {
161 fprintf(stderr, "strlcpy: failed NULL src test (6b), "
162 "expected signal %d, got %d\n", SIGSEGV, got_signal);
163 failures++;
164 }
165
166 return failures;
167 }
168