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);
ATF_TC_BODY(nul,tc)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);
ATF_TC_BODY(not_found,tc)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
do_found_test(char buf[],size_t first,size_t second)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);
ATF_TC_BODY(found,tc)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
do_values_test(char buf[],size_t len,size_t i,int c)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);
ATF_TC_BODY(values,tc)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
ATF_TP_ADD_TCS(tp)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