1 /* $OpenBSD: strlcattest.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 <setjmp.h> 25 #include <signal.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, *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 if (buf == NULL) { 49 fprintf(stderr, "unable to allocate memory\n"); 50 return 1; 51 } 52 memset(buf, 'z', bufsize); 53 ep = buf + bufsize; 54 55 /* Test appending to an unterminated string. */ 56 len = strlcat(buf, "abcd", bufsize); 57 if (len != 4 + bufsize) { 58 fprintf(stderr, 59 "strlcat: failed unterminated buffer test (1a)\n"); 60 failures++; 61 } 62 /* Make sure we only wrote where expected. */ 63 for (cp = buf; cp < ep; cp++) { 64 if (*cp != 'z') { 65 fprintf(stderr, 66 "strlcat: failed unterminated buffer test (1b)\n"); 67 failures++; 68 break; 69 } 70 } 71 72 /* Test appending to a full string. */ 73 ep[-1] = '\0'; 74 len = strlcat(buf, "abcd", bufsize); 75 if (len != 4 + bufsize - 1) { 76 fprintf(stderr, "strlcat: failed full buffer test (2a)\n"); 77 failures++; 78 } 79 /* Make sure we only wrote where expected. */ 80 for (cp = buf; cp < ep - 1; cp++) { 81 if (*cp != 'z') { 82 fprintf(stderr, 83 "strlcat: failed full buffer test (2b)\n"); 84 failures++; 85 break; 86 } 87 } 88 89 /* Test appending to an empty string. */ 90 ep[-1] = 'z'; 91 buf[0] = '\0'; 92 len = strlcat(buf, "abcd", bufsize); 93 if (len != 4) { 94 fprintf(stderr, "strlcat: failed empty buffer test (3a)\n"); 95 failures++; 96 } 97 /* Make sure we only wrote where expected. */ 98 if (memcmp(buf, "abcd", sizeof("abcd")) != 0) { 99 fprintf(stderr, "strlcat: failed empty buffer test (3b)\n"); 100 failures++; 101 } 102 for (cp = buf + len + 1; cp < ep; cp++) { 103 if (*cp != 'z') { 104 fprintf(stderr, 105 "strlcat: failed empty buffer test (3c)\n"); 106 failures++; 107 break; 108 } 109 } 110 111 /* Test appending to a NUL-terminated string. */ 112 memcpy(buf, "abcd", sizeof("abcd")); 113 len = strlcat(buf, "efgh", bufsize); 114 if (len != 8) { 115 fprintf(stderr, "strlcat: failed empty buffer test (4a)\n"); 116 failures++; 117 } 118 /* Make sure we only wrote where expected. */ 119 if (memcmp(buf, "abcdefgh", sizeof("abcdefgh")) != 0) { 120 fprintf(stderr, "strlcat: failed empty buffer test (4b)\n"); 121 failures++; 122 } 123 for (cp = buf + len + 1; cp < ep; cp++) { 124 if (*cp != 'z') { 125 fprintf(stderr, 126 "strlcat: failed empty 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 strlcat() 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 = strlcat(NULL, "abcd", sizeof(buf)); 147 fprintf(stderr, "strlcat: 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, "strlcat: 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 memcpy(buf, "abcd", sizeof("abcd")); 158 got_signal = 0; 159 if (sigsetjmp(jmpenv, 1) == 0) { 160 len = strlcat(buf, NULL, sizeof(buf)); 161 fprintf(stderr, "strlcat: failed NULL src test (6a), " 162 "expected signal %d, got len %zu\n", SIGSEGV, len); 163 failures++; 164 } else if (got_signal != SIGSEGV) { 165 fprintf(stderr, "strlcat: failed NULL src test (6b), " 166 "expected signal %d, got %d\n", SIGSEGV, got_signal); 167 failures++; 168 } 169 170 return failures; 171 } 172