1 | /* Alignment/padding coverage test for string comparison. |
2 | Copyright (C) 2016-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | /* This performs test comparisons with various (mis)alignments and |
20 | characters in the padding. It is partly a regression test for bug |
21 | 20327. */ |
22 | |
23 | #include <limits.h> |
24 | #include <malloc.h> |
25 | #include <stdbool.h> |
26 | #include <stdint.h> |
27 | #include <stdlib.h> |
28 | #include <string.h> |
29 | #include <libc-diag.h> |
30 | |
31 | static int |
32 | signum (int val) |
33 | { |
34 | if (val < 0) |
35 | return -1; |
36 | if (val > 0) |
37 | return 1; |
38 | else |
39 | return 0; |
40 | } |
41 | |
42 | static size_t |
43 | max_size_t (size_t left, size_t right) |
44 | { |
45 | if (left > right) |
46 | return left; |
47 | else |
48 | return right; |
49 | } |
50 | |
51 | /* Wrappers for strncmp and strncasecmp which determine the maximum |
52 | string length in some, either based on the input string length, or |
53 | using fixed constants. */ |
54 | |
55 | static int |
56 | strncmp_no_terminator (const char *left, const char *right) |
57 | { |
58 | size_t left_len = strlen (left); |
59 | size_t right_len = strlen (right); |
60 | return strncmp (left, right, max_size_t (left: left_len, right: right_len)); |
61 | } |
62 | |
63 | static int |
64 | strncasecmp_no_terminator (const char *left, const char *right) |
65 | { |
66 | size_t left_len = strlen (left); |
67 | size_t right_len = strlen (right); |
68 | return strncasecmp (s1: left, s2: right, n: max_size_t (left: left_len, right: right_len)); |
69 | } |
70 | |
71 | static int |
72 | strncmp_terminator (const char *left, const char *right) |
73 | { |
74 | size_t left_len = strlen (left); |
75 | size_t right_len = strlen (right); |
76 | return strncmp (left, right, max_size_t (left: left_len, right: right_len)); |
77 | } |
78 | |
79 | static int |
80 | strncasecmp_terminator (const char *left, const char *right) |
81 | { |
82 | size_t left_len = strlen (left); |
83 | size_t right_len = strlen (right); |
84 | return strncasecmp (s1: left, s2: right, n: max_size_t (left: left_len, right: right_len)); |
85 | } |
86 | |
87 | static int |
88 | strncmp_64 (const char *left, const char *right) |
89 | { |
90 | return strncmp (left, right, 64); |
91 | } |
92 | |
93 | static int |
94 | strncasecmp_64 (const char *left, const char *right) |
95 | { |
96 | return strncasecmp (s1: left, s2: right, n: 64); |
97 | } |
98 | |
99 | static int |
100 | strncmp_max (const char *left, const char *right) |
101 | { |
102 | DIAG_PUSH_NEEDS_COMMENT; |
103 | #if __GNUC_PREREQ (7, 0) |
104 | /* GCC 9 warns about the size passed to strncmp being larger than |
105 | PTRDIFF_MAX; the use of SIZE_MAX is deliberate here. */ |
106 | DIAG_IGNORE_NEEDS_COMMENT (9, "-Wstringop-overflow=" ); |
107 | #endif |
108 | #if __GNUC_PREREQ (11, 0) |
109 | /* Likewise GCC 11, with a different warning option. */ |
110 | DIAG_IGNORE_NEEDS_COMMENT (11, "-Wstringop-overread" ); |
111 | #endif |
112 | return strncmp (left, right, SIZE_MAX); |
113 | DIAG_POP_NEEDS_COMMENT; |
114 | } |
115 | |
116 | static int |
117 | strncasecmp_max (const char *left, const char *right) |
118 | { |
119 | DIAG_PUSH_NEEDS_COMMENT; |
120 | #if __GNUC_PREREQ (7, 0) |
121 | /* GCC 9 warns about the size passed to strncasecmp being larger |
122 | than PTRDIFF_MAX; the use of SIZE_MAX is deliberate here. */ |
123 | DIAG_IGNORE_NEEDS_COMMENT (9, "-Wstringop-overflow=" ); |
124 | #endif |
125 | #if __GNUC_PREREQ (11, 0) |
126 | /* Likewise GCC 11, with a different warning option. */ |
127 | DIAG_IGNORE_NEEDS_COMMENT (11, "-Wstringop-overread" ); |
128 | #endif |
129 | return strncasecmp (s1: left, s2: right, SIZE_MAX); |
130 | DIAG_POP_NEEDS_COMMENT; |
131 | } |
132 | |
133 | int |
134 | do_test (void) |
135 | { |
136 | enum { |
137 | max_align = 64, |
138 | max_string_length = 33 |
139 | }; |
140 | size_t blob_size = max_align + max_string_length + 1; |
141 | char *left = memalign (alignment: max_align, size: blob_size); |
142 | char *right = memalign (alignment: max_align, size: blob_size); |
143 | if (left == NULL || right == NULL) |
144 | { |
145 | printf (format: "error: out of memory\n" ); |
146 | return 1; |
147 | } |
148 | |
149 | const struct |
150 | { |
151 | const char *name; |
152 | int (*implementation) (const char *, const char *); |
153 | } functions[] = |
154 | { |
155 | { "strcmp" , strcmp }, |
156 | { "strcasecmp" , strcasecmp }, |
157 | { "strncmp (without NUL)" , strncmp_no_terminator}, |
158 | { "strncasecmp (without NUL)" , strncasecmp_no_terminator}, |
159 | { "strncmp (with NUL)" , strncmp_terminator}, |
160 | { "strncasecmp (with NUL)" , strncasecmp_terminator}, |
161 | { "strncmp (length 64)" , strncmp_64}, |
162 | { "strncasecmp (length 64)" , strncasecmp_64}, |
163 | { "strncmp (length SIZE_MAX)" , strncmp_max}, |
164 | { "strncasecmp (length SIZE_MAX)" , strncasecmp_max}, |
165 | { NULL, NULL } |
166 | }; |
167 | const char *const strings[] = |
168 | { |
169 | "" , |
170 | "0" , |
171 | "01" , |
172 | "01234567" , |
173 | "0123456789abcde" , |
174 | "0123456789abcdef" , |
175 | "0123456789abcdefg" , |
176 | "1" , |
177 | "10" , |
178 | "123456789abcdef" , |
179 | "123456789abcdefg" , |
180 | "23456789abcdef" , |
181 | "23456789abcdefg" , |
182 | "abcdefghijklmnopqrstuvwxyzABCDEF" , |
183 | NULL |
184 | }; |
185 | const unsigned char pads[] = |
186 | { 0, 1, 32, 64, 128, '0', '1', 'e', 'f', 'g', 127, 192, 255 }; |
187 | |
188 | bool errors = false; |
189 | for (int left_idx = 0; strings[left_idx] != NULL; ++left_idx) |
190 | for (int left_align = 0; left_align < max_align; ++left_align) |
191 | for (unsigned pad_left = 0; pad_left < sizeof (pads); ++pad_left) |
192 | { |
193 | memset (left, pads[pad_left], blob_size); |
194 | strcpy (left + left_align, strings[left_idx]); |
195 | |
196 | for (int right_idx = 0; strings[right_idx] != NULL; ++right_idx) |
197 | for (unsigned pad_right = 0; pad_right < sizeof (pads); |
198 | ++pad_right) |
199 | for (int right_align = 0; right_align < max_align; |
200 | ++right_align) |
201 | { |
202 | memset (right, pads[pad_right], blob_size); |
203 | strcpy (right + right_align, strings[right_idx]); |
204 | |
205 | for (int func = 0; functions[func].name != NULL; ++func) |
206 | { |
207 | int expected = left_idx - right_idx; |
208 | int actual = functions[func].implementation |
209 | (left + left_align, right + right_align); |
210 | if (signum (val: actual) != signum (val: expected)) |
211 | { |
212 | printf (format: "error: mismatch for %s: %d\n" |
213 | " left: \"%s\"\n" |
214 | " right: \"%s\"\n" |
215 | " pad_left = %u, pad_right = %u,\n" |
216 | " left_align = %d, right_align = %d\n" , |
217 | functions[func].name, actual, |
218 | strings[left_idx], strings[right_idx], |
219 | pad_left, pad_right, |
220 | left_align, right_align); |
221 | errors = true; |
222 | } |
223 | } |
224 | } |
225 | } |
226 | free (ptr: right); |
227 | free (ptr: left); |
228 | return errors; |
229 | } |
230 | |
231 | /* The nested loops need a long time to complete on slower |
232 | machines. */ |
233 | #define TIMEOUT 600 |
234 | |
235 | #include <support/test-driver.c> |
236 | |