1*de8b408bSmillert /* $OpenBSD: strlcpytest.c,v 1.5 2021/09/27 19:33:58 millert Exp $ */
26f05df2dSmillert
36f05df2dSmillert /*
4bf198cc6Smillert * Copyright (c) 2014 Todd C. Miller <millert@openbsd.org>
56f05df2dSmillert *
66f05df2dSmillert * Permission to use, copy, modify, and distribute this software for any
76f05df2dSmillert * purpose with or without fee is hereby granted, provided that the above
86f05df2dSmillert * copyright notice and this permission notice appear in all copies.
96f05df2dSmillert *
106f05df2dSmillert * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116f05df2dSmillert * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126f05df2dSmillert * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136f05df2dSmillert * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146f05df2dSmillert * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
156f05df2dSmillert * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
166f05df2dSmillert * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176f05df2dSmillert */
186f05df2dSmillert
196f05df2dSmillert #include <sys/types.h>
206f05df2dSmillert
216f05df2dSmillert #include <stdio.h>
226f05df2dSmillert #include <stdlib.h>
236f05df2dSmillert #include <string.h>
24f191fa1fSmillert #include <signal.h>
25f191fa1fSmillert #include <setjmp.h>
266f05df2dSmillert #include <unistd.h>
276f05df2dSmillert
28f191fa1fSmillert volatile sig_atomic_t got_signal;
29f191fa1fSmillert sigjmp_buf jmpenv;
30f191fa1fSmillert
31f191fa1fSmillert void
handler(int signo)32f191fa1fSmillert handler(int signo)
33f191fa1fSmillert {
34f191fa1fSmillert got_signal = signo;
35f191fa1fSmillert siglongjmp(jmpenv, 1);
36f191fa1fSmillert }
37f191fa1fSmillert
38f191fa1fSmillert int
main(int argc,char * argv[])39f191fa1fSmillert main(int argc, char *argv[])
406f05df2dSmillert {
416f05df2dSmillert char *buf, *buf2, *cp, *ep;
42f191fa1fSmillert struct sigaction sa;
436f05df2dSmillert size_t len, bufsize;
44*de8b408bSmillert volatile int failures = 0;
456f05df2dSmillert
466f05df2dSmillert bufsize = getpagesize(); /* trigger guard pages easily */
476f05df2dSmillert buf = malloc(bufsize);
486f05df2dSmillert buf2 = malloc(bufsize);
496f05df2dSmillert if (buf == NULL || buf2 == NULL) {
506f05df2dSmillert fprintf(stderr, "unable to allocate memory\n");
516f05df2dSmillert return 1;
526f05df2dSmillert }
53f191fa1fSmillert memset(buf, 'z', bufsize);
546f05df2dSmillert ep = buf + bufsize;
556f05df2dSmillert
566f05df2dSmillert /* Test copying to a zero-length NULL buffer. */
576f05df2dSmillert len = strlcpy(NULL, "abcd", 0);
586f05df2dSmillert if (len != 4) {
59f191fa1fSmillert fprintf(stderr,
60f191fa1fSmillert "strlcpy: failed zero-length buffer test (1a)\n");
616f05df2dSmillert failures++;
626f05df2dSmillert }
636f05df2dSmillert
646f05df2dSmillert /* Test copying small string to a large buffer. */
656f05df2dSmillert len = strlcpy(buf, "abcd", bufsize);
666f05df2dSmillert if (len != 4) {
67f191fa1fSmillert fprintf(stderr, "strlcpy: failed large buffer test (2a)\n");
686f05df2dSmillert failures++;
696f05df2dSmillert }
706f05df2dSmillert /* Make sure we only wrote where expected. */
716f05df2dSmillert if (memcmp(buf, "abcd", sizeof("abcd")) != 0) {
72f191fa1fSmillert fprintf(stderr, "strlcpy: failed large buffer test (2b)\n");
736f05df2dSmillert failures++;
746f05df2dSmillert }
756f05df2dSmillert for (cp = buf + len + 1; cp < ep; cp++) {
76f191fa1fSmillert if (*cp != 'z') {
77f191fa1fSmillert fprintf(stderr,
78f191fa1fSmillert "strlcpy: failed large buffer test (2c)\n");
796f05df2dSmillert failures++;
806f05df2dSmillert break;
816f05df2dSmillert }
826f05df2dSmillert }
836f05df2dSmillert
846f05df2dSmillert /* Test copying large string to a small buffer. */
85f191fa1fSmillert memset(buf, 'z', bufsize);
866f05df2dSmillert memset(buf2, 'x', bufsize - 1);
876f05df2dSmillert buf2[bufsize - 1] = '\0';
886f05df2dSmillert len = strlcpy(buf, buf2, bufsize / 2);
896f05df2dSmillert if (len != bufsize - 1) {
90f191fa1fSmillert fprintf(stderr, "strlcpy: failed small buffer test (3a)\n");
916f05df2dSmillert failures++;
926f05df2dSmillert }
936f05df2dSmillert /* Make sure we only wrote where expected. */
946f05df2dSmillert len = (bufsize / 2) - 1;
956f05df2dSmillert if (memcmp(buf, buf2, len) != 0 || buf[len] != '\0') {
96f191fa1fSmillert fprintf(stderr, "strlcpy: failed small buffer test (3b)\n");
976f05df2dSmillert failures++;
986f05df2dSmillert }
996f05df2dSmillert for (cp = buf + len + 1; cp < ep; cp++) {
100f191fa1fSmillert if (*cp != 'z') {
101f191fa1fSmillert fprintf(stderr,
102f191fa1fSmillert "strlcpy: failed small buffer test (3c)\n");
1036f05df2dSmillert failures++;
1046f05df2dSmillert break;
1056f05df2dSmillert }
1066f05df2dSmillert }
1076f05df2dSmillert
1086f05df2dSmillert /* Test copying to a 1-byte buffer. */
109f191fa1fSmillert memset(buf, 'z', bufsize);
1106f05df2dSmillert len = strlcpy(buf, "abcd", 1);
1116f05df2dSmillert if (len != 4) {
112f191fa1fSmillert fprintf(stderr, "strlcpy: failed 1-byte buffer test (4a)\n");
1136f05df2dSmillert failures++;
1146f05df2dSmillert }
1156f05df2dSmillert /* Make sure we only wrote where expected. */
1166f05df2dSmillert if (buf[0] != '\0') {
117f191fa1fSmillert fprintf(stderr, "strlcpy: failed 1-byte buffer test (4b)\n");
1186f05df2dSmillert failures++;
1196f05df2dSmillert }
1206f05df2dSmillert for (cp = buf + 1; cp < ep; cp++) {
121f191fa1fSmillert if (*cp != 'z') {
122f191fa1fSmillert fprintf(stderr,
123f191fa1fSmillert "strlcpy: failed 1-byte buffer test (4c)\n");
1246f05df2dSmillert failures++;
1256f05df2dSmillert break;
1266f05df2dSmillert }
1276f05df2dSmillert }
1286f05df2dSmillert
129f191fa1fSmillert /*
130f191fa1fSmillert * The following tests should result in SIGSEGV, however some
131f191fa1fSmillert * systems may erroneously report SIGBUS.
132f191fa1fSmillert * These tests assume that strlcpy() is signal-safe.
133f191fa1fSmillert */
134f191fa1fSmillert memset(&sa, 0, sizeof(sa));
135f191fa1fSmillert sigemptyset(&sa.sa_mask);
136f191fa1fSmillert sa.sa_handler = handler;
137f191fa1fSmillert sigaction(SIGSEGV, &sa, NULL);
138f191fa1fSmillert sigaction(SIGBUS, &sa, NULL);
139f191fa1fSmillert
140f191fa1fSmillert /* Test copying to a NULL buffer with non-zero size. */
141f191fa1fSmillert got_signal = 0;
142f191fa1fSmillert if (sigsetjmp(jmpenv, 1) == 0) {
143f191fa1fSmillert len = strlcpy(NULL, "abcd", sizeof(buf));
144f191fa1fSmillert fprintf(stderr, "strlcpy: failed NULL dst test (5a), "
145f191fa1fSmillert "expected signal %d, got len %zu\n", SIGSEGV, len);
146f191fa1fSmillert failures++;
147f191fa1fSmillert } else if (got_signal != SIGSEGV) {
148f191fa1fSmillert fprintf(stderr, "strlcpy: failed NULL dst test (5b), "
149f191fa1fSmillert "expected signal %d, got %d\n", SIGSEGV, got_signal);
150f191fa1fSmillert failures++;
151f191fa1fSmillert }
152f191fa1fSmillert
153f191fa1fSmillert /* Test copying from a NULL src. */
154f191fa1fSmillert got_signal = 0;
155f191fa1fSmillert if (sigsetjmp(jmpenv, 1) == 0) {
156f191fa1fSmillert len = strlcpy(buf, NULL, sizeof(buf));
157f191fa1fSmillert fprintf(stderr, "strlcpy: failed NULL src test (6a), "
158f191fa1fSmillert "expected signal %d, got len %zu\n", SIGSEGV, len);
159f191fa1fSmillert failures++;
160f191fa1fSmillert } else if (got_signal != SIGSEGV) {
161f191fa1fSmillert fprintf(stderr, "strlcpy: failed NULL src test (6b), "
162f191fa1fSmillert "expected signal %d, got %d\n", SIGSEGV, got_signal);
163f191fa1fSmillert failures++;
164f191fa1fSmillert }
165f191fa1fSmillert
1666f05df2dSmillert return failures;
1676f05df2dSmillert }
168