1 /* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023, 2026 Robert Clausecker <fuz@FreeBSD.org> 5 * 6 * Adapted from memrchr_test.c. 7 */ 8 9 #include <sys/cdefs.h> 10 11 #include <dlfcn.h> 12 #include <limits.h> 13 #include <stdio.h> 14 #include <string.h> 15 16 #include <atf-c.h> 17 18 static char *(*strrchr_fn)(const char *, int); 19 20 /* 21 * Check that when looking for the character NUL, we find the 22 * string terminator, and not some NUL character after it. 23 */ 24 ATF_TC_WITHOUT_HEAD(nul); 25 ATF_TC_BODY(nul, tc) 26 { 27 size_t i, j, k; 28 char buf[1+15+64]; /* offset [0+15] + 64 buffer bytes + sentinels */ 29 30 buf[0] = '\0'; 31 memset(buf + 1, '-', sizeof(buf) - 1); 32 33 for (i = 0; i < 16; i++) 34 for (j = 0; j < 64; j++) 35 for (k = j; k < 64; k++) { 36 buf[i + j + 1] = '\0'; 37 buf[i + k + 1] = '\0'; 38 ATF_CHECK_EQ(strrchr_fn(buf + i + 1, '\0'), buf + i + j + 1); 39 buf[i + j + 1] = '-'; 40 buf[i + k + 1] = '-'; 41 } 42 } 43 44 /* 45 * Check that if the character 'X' does not occur in the string 46 * (but occurs before and after it), we correctly return NULL. 47 */ 48 ATF_TC_WITHOUT_HEAD(not_found); 49 ATF_TC_BODY(not_found, tc) 50 { 51 size_t i, j; 52 char buf[1+15+64+2]; /* offset [0..15] + 64 buffer bytes + sentinels */ 53 54 buf[0] = 'X'; 55 memset(buf + 1, '-', sizeof(buf) - 1); 56 57 for (i = 0; i < 16; i++) 58 for (j = 0; j < 64; j++) { 59 buf[i + j + 1] = '\0'; 60 buf[i + j + 2] = 'X'; 61 ATF_CHECK_EQ(strrchr_fn(buf + i + 1, 'X'), NULL); 62 buf[i + j + 1] = '-'; 63 buf[i + j + 2] = '-'; 64 } 65 } 66 67 static void 68 do_found_test(char buf[], size_t first, size_t second) 69 { 70 /* invariant: first <= second */ 71 72 buf[first] = 'X'; 73 buf[second] = 'X'; 74 ATF_CHECK_EQ(strrchr_fn(buf, 'X'), buf + second); 75 buf[first] = '-'; 76 buf[second] = '-'; 77 } 78 79 /* 80 * Check that if the character 'X' occurs in the string multiple 81 * times (i. e. twice), its last encounter is returned. 82 */ 83 ATF_TC_WITHOUT_HEAD(found); 84 ATF_TC_BODY(found, tc) 85 { 86 size_t i, j, k, l; 87 char buf[1+15+64+2]; 88 89 buf[0] = 'X'; 90 memset(buf + 1, '-', sizeof(buf) - 1); 91 92 for (i = 0; i < 16; i++) 93 for (j = 0; j < 64; j++) 94 for (k = 0; k < j; k++) 95 for (l = 0; l <= k; l++) { 96 buf[i + j + 1] = '\0'; 97 buf[i + j + 2] = 'X'; 98 do_found_test(buf + i + 1, l, k); 99 buf[i + j + 1] = '-'; 100 buf[i + j + 2] = '-'; 101 } 102 } 103 104 static void 105 do_values_test(char buf[], size_t len, size_t i, int c) 106 { 107 /* sentinels */ 108 buf[-1] = c; 109 buf[len] = '\0'; 110 buf[len + 1] = 'c'; 111 112 /* fill the string with some other character, but not with NUL */ 113 memset(buf, c == UCHAR_MAX ? c - 1 : c + 1, len); 114 115 if (i < len) { 116 buf[i] = c; 117 ATF_CHECK_EQ(strrchr_fn(buf, c), buf + i); 118 } else 119 ATF_CHECK_EQ(strrchr_fn(buf, c), c == 0 ? buf + len : NULL); 120 } 121 122 /* 123 * Check that the character is found regardless of its value. 124 * This catches arithmetic (overflow) errors in incorrect SWAR 125 * implementations of byte-parallel character matching. 126 */ 127 ATF_TC_WITHOUT_HEAD(values); 128 ATF_TC_BODY(values, tc) 129 { 130 size_t i, j, k; 131 int c; 132 char buf[1+15+64+2]; 133 134 for (i = 0; i < 16; i++) 135 for (j = 0; j < 64; j++) 136 for (k = 0; k <= j; k++) 137 for (c = 0; c <= UCHAR_MAX; c++) 138 do_values_test(buf + i + 1, j, k, c); 139 } 140 141 ATF_TP_ADD_TCS(tp) 142 { 143 void *dl_handle; 144 145 dl_handle = dlopen(NULL, RTLD_LAZY); 146 strrchr_fn = dlsym(dl_handle, "test_strrchr"); 147 if (strrchr_fn == NULL) 148 strrchr_fn = strrchr; 149 150 ATF_TP_ADD_TC(tp, nul); 151 ATF_TP_ADD_TC(tp, not_found); 152 ATF_TP_ADD_TC(tp, found); 153 ATF_TP_ADD_TC(tp, values); 154 155 return (atf_no_error()); 156 } 157