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 32 handler(int signo) 33 { 34 got_signal = signo; 35 siglongjmp(jmpenv, 1); 36 } 37 38 int 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