1 /* $OpenBSD: strlcpytest.c,v 1.3 2019/01/25 00:19:26 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 int failures = 0; 45 46 /* Enable malloc security options. */ 47 setenv("MALLOC_OPTIONS", "S", 0); 48 49 bufsize = getpagesize(); /* trigger guard pages easily */ 50 buf = malloc(bufsize); 51 buf2 = malloc(bufsize); 52 if (buf == NULL || buf2 == NULL) { 53 fprintf(stderr, "unable to allocate memory\n"); 54 return 1; 55 } 56 memset(buf, 'z', bufsize); 57 ep = buf + bufsize; 58 59 /* Test copying to a zero-length NULL buffer. */ 60 len = strlcpy(NULL, "abcd", 0); 61 if (len != 4) { 62 fprintf(stderr, 63 "strlcpy: failed zero-length buffer test (1a)\n"); 64 failures++; 65 } 66 67 /* Test copying small string to a large buffer. */ 68 len = strlcpy(buf, "abcd", bufsize); 69 if (len != 4) { 70 fprintf(stderr, "strlcpy: failed large buffer test (2a)\n"); 71 failures++; 72 } 73 /* Make sure we only wrote where expected. */ 74 if (memcmp(buf, "abcd", sizeof("abcd")) != 0) { 75 fprintf(stderr, "strlcpy: failed large buffer test (2b)\n"); 76 failures++; 77 } 78 for (cp = buf + len + 1; cp < ep; cp++) { 79 if (*cp != 'z') { 80 fprintf(stderr, 81 "strlcpy: failed large buffer test (2c)\n"); 82 failures++; 83 break; 84 } 85 } 86 87 /* Test copying large string to a small buffer. */ 88 memset(buf, 'z', bufsize); 89 memset(buf2, 'x', bufsize - 1); 90 buf2[bufsize - 1] = '\0'; 91 len = strlcpy(buf, buf2, bufsize / 2); 92 if (len != bufsize - 1) { 93 fprintf(stderr, "strlcpy: failed small buffer test (3a)\n"); 94 failures++; 95 } 96 /* Make sure we only wrote where expected. */ 97 len = (bufsize / 2) - 1; 98 if (memcmp(buf, buf2, len) != 0 || buf[len] != '\0') { 99 fprintf(stderr, "strlcpy: failed small buffer test (3b)\n"); 100 failures++; 101 } 102 for (cp = buf + len + 1; cp < ep; cp++) { 103 if (*cp != 'z') { 104 fprintf(stderr, 105 "strlcpy: failed small buffer test (3c)\n"); 106 failures++; 107 break; 108 } 109 } 110 111 /* Test copying to a 1-byte buffer. */ 112 memset(buf, 'z', bufsize); 113 len = strlcpy(buf, "abcd", 1); 114 if (len != 4) { 115 fprintf(stderr, "strlcpy: failed 1-byte buffer test (4a)\n"); 116 failures++; 117 } 118 /* Make sure we only wrote where expected. */ 119 if (buf[0] != '\0') { 120 fprintf(stderr, "strlcpy: failed 1-byte buffer test (4b)\n"); 121 failures++; 122 } 123 for (cp = buf + 1; cp < ep; cp++) { 124 if (*cp != 'z') { 125 fprintf(stderr, 126 "strlcpy: failed 1-byte buffer test (4c)\n"); 127 failures++; 128 break; 129 } 130 } 131 132 /* 133 * The following tests should result in SIGSEGV, however some 134 * systems may erroneously report SIGBUS. 135 * These tests assume that strlcpy() is signal-safe. 136 */ 137 memset(&sa, 0, sizeof(sa)); 138 sigemptyset(&sa.sa_mask); 139 sa.sa_handler = handler; 140 sigaction(SIGSEGV, &sa, NULL); 141 sigaction(SIGBUS, &sa, NULL); 142 143 /* Test copying to a NULL buffer with non-zero size. */ 144 got_signal = 0; 145 if (sigsetjmp(jmpenv, 1) == 0) { 146 len = strlcpy(NULL, "abcd", sizeof(buf)); 147 fprintf(stderr, "strlcpy: failed NULL dst test (5a), " 148 "expected signal %d, got len %zu\n", SIGSEGV, len); 149 failures++; 150 } else if (got_signal != SIGSEGV) { 151 fprintf(stderr, "strlcpy: failed NULL dst test (5b), " 152 "expected signal %d, got %d\n", SIGSEGV, got_signal); 153 failures++; 154 } 155 156 /* Test copying from a NULL src. */ 157 got_signal = 0; 158 if (sigsetjmp(jmpenv, 1) == 0) { 159 len = strlcpy(buf, NULL, sizeof(buf)); 160 fprintf(stderr, "strlcpy: failed NULL src test (6a), " 161 "expected signal %d, got len %zu\n", SIGSEGV, len); 162 failures++; 163 } else if (got_signal != SIGSEGV) { 164 fprintf(stderr, "strlcpy: failed NULL src test (6b), " 165 "expected signal %d, got %d\n", SIGSEGV, got_signal); 166 failures++; 167 } 168 169 return failures; 170 } 171