1// RUN: %clangxx -O0 -g %s -o %t && %run %t
2/// Check that REG_STARTEND is handled correctly.
3/// This is a regression test for https://github.com/google/sanitizers/issues/1371
4/// Previously, on GLibc systems, the interceptor was calling __compat_regexec
5/// (regexec@GLIBC_2.2.5) insead of the newer __regexec (regexec@GLIBC_2.3.4).
6/// The __compat_regexec strips the REG_STARTEND flag but does not report an error
7/// if other flags are present. This can result in infinite loops for programs that
8/// use REG_STARTEND to find all matches inside a buffer (since ignoring REG_STARTEND
9/// means that the search always starts from the first character).
10
11#include <assert.h>
12#include <regex.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16
17/// REG_STARTEND is a BSD extension not supported everywhere.
18#ifdef REG_STARTEND
19void test_matched(const regex_t *preg, const char *string, size_t start,
20 size_t end, const char *expected) {
21 regmatch_t match[1];
22 match[0].rm_so = start;
23 match[0].rm_eo = end;
24 int rv = regexec(preg: preg, String: string, nmatch: 1, pmatch: match, REG_STARTEND);
25 int matchlen = (int)(match[0].rm_eo - match[0].rm_so);
26 const char *matchstart = string + match[0].rm_so;
27 if (rv == 0) {
28 if (expected == nullptr) {
29 fprintf(stderr, format: "ERROR: expected no match but got '%.*s'\n",
30 matchlen, matchstart);
31 abort();
32 } else if ((size_t)matchlen != strlen(s: expected) ||
33 memcmp(s1: matchstart, s2: expected, n: strlen(s: expected)) != 0) {
34 fprintf(stderr, format: "ERROR: expected '%s' match but got '%.*s'\n",
35 expected, matchlen, matchstart);
36 abort();
37 }
38 } else if (rv == REG_NOMATCH) {
39 if (expected != nullptr) {
40 fprintf(stderr, format: "ERROR: expected '%s' match but got no match\n", expected);
41 abort();
42 }
43 } else {
44 printf(format: "ERROR: unexpected regexec return value %d\n", rv);
45 abort();
46 }
47}
48
49int main(void) {
50 regex_t regex;
51 int rv = regcomp(preg: &regex, pattern: "[A-Z][A-Z]", cflags: 0);
52 assert(!rv);
53 test_matched(preg: &regex, string: "ABCD", start: 0, end: 4, expected: "AB");
54 test_matched(preg: &regex, string: "ABCD", start: 0, end: 1, expected: nullptr); // Not long enough
55 test_matched(preg: &regex, string: "ABCD", start: 1, end: 4, expected: "BC");
56 test_matched(preg: &regex, string: "ABCD", start: 1, end: 2, expected: nullptr); // Not long enough
57 test_matched(preg: &regex, string: "ABCD", start: 2, end: 4, expected: "CD");
58 test_matched(preg: &regex, string: "ABCD", start: 2, end: 3, expected: nullptr); // Not long enough
59 test_matched(preg: &regex, string: "ABCD", start: 3, end: 4, expected: nullptr); // Not long enough
60 regfree(preg: &regex);
61 printf(format: "Successful test\n");
62 return 0;
63}
64#else
65int main(void) {
66 return 0;
67}
68#endif
69

source code of compiler-rt/test/sanitizer_common/TestCases/Posix/regex_startend.cpp