Skip to content

Commit 00672d6

Browse files
authored
Expand cpp/polynomial-redos test coverage (Phase 3 follow-up)
1 parent 8a8590c commit 00672d6

2 files changed

Lines changed: 143 additions & 26 deletions

File tree

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,49 @@
11
edges
2-
| test.cpp:99:27:99:30 | **argv | test.cpp:109:28:109:32 | *input | provenance | TaintFunction |
3-
| test.cpp:99:27:99:30 | **argv | test.cpp:124:27:124:31 | *input | provenance | TaintFunction |
4-
| test.cpp:99:27:99:30 | **argv | test.cpp:161:27:161:31 | *input | provenance | TaintFunction |
5-
| test.cpp:99:27:99:30 | **argv | test.cpp:171:26:171:30 | *input | provenance | TaintFunction |
6-
| test.cpp:99:27:99:30 | **argv | test.cpp:177:27:177:31 | *input | provenance | TaintFunction |
7-
| test.cpp:99:27:99:30 | **argv | test.cpp:184:28:184:32 | *input | provenance | TaintFunction |
8-
| test.cpp:99:27:99:30 | **argv | test.cpp:271:27:271:31 | *input | provenance | TaintFunction |
9-
| test.cpp:99:27:99:30 | **argv | test.cpp:300:28:300:32 | *input | provenance | TaintFunction |
2+
| test.cpp:107:27:107:30 | **argv | test.cpp:117:28:117:32 | *input | provenance | TaintFunction |
3+
| test.cpp:107:27:107:30 | **argv | test.cpp:132:27:132:31 | *input | provenance | TaintFunction |
4+
| test.cpp:107:27:107:30 | **argv | test.cpp:169:27:169:31 | *input | provenance | TaintFunction |
5+
| test.cpp:107:27:107:30 | **argv | test.cpp:179:26:179:30 | *input | provenance | TaintFunction |
6+
| test.cpp:107:27:107:30 | **argv | test.cpp:185:27:185:31 | *input | provenance | TaintFunction |
7+
| test.cpp:107:27:107:30 | **argv | test.cpp:192:28:192:32 | *input | provenance | TaintFunction |
8+
| test.cpp:107:27:107:30 | **argv | test.cpp:279:27:279:31 | *input | provenance | TaintFunction |
9+
| test.cpp:107:27:107:30 | **argv | test.cpp:308:28:308:32 | *input | provenance | TaintFunction |
10+
| test.cpp:107:27:107:30 | **argv | test.cpp:342:27:342:31 | *input | provenance | TaintFunction |
11+
| test.cpp:107:27:107:30 | **argv | test.cpp:349:27:349:31 | *input | provenance | TaintFunction |
12+
| test.cpp:107:27:107:30 | **argv | test.cpp:421:26:421:30 | *input | provenance | TaintFunction |
13+
| test.cpp:369:27:369:57 | *call to getenv | test.cpp:369:27:369:57 | *call to getenv | provenance | |
14+
| test.cpp:369:27:369:57 | *call to getenv | test.cpp:372:28:372:28 | *s | provenance | TaintFunction |
15+
| test.cpp:380:20:380:22 | fread output argument | test.cpp:383:28:383:28 | *s | provenance | TaintFunction |
1016
nodes
11-
| test.cpp:99:27:99:30 | **argv | semmle.label | **argv |
12-
| test.cpp:109:28:109:32 | *input | semmle.label | *input |
13-
| test.cpp:124:27:124:31 | *input | semmle.label | *input |
14-
| test.cpp:161:27:161:31 | *input | semmle.label | *input |
15-
| test.cpp:171:26:171:30 | *input | semmle.label | *input |
16-
| test.cpp:177:27:177:31 | *input | semmle.label | *input |
17-
| test.cpp:184:28:184:32 | *input | semmle.label | *input |
18-
| test.cpp:271:27:271:31 | *input | semmle.label | *input |
19-
| test.cpp:300:28:300:32 | *input | semmle.label | *input |
17+
| test.cpp:107:27:107:30 | **argv | semmle.label | **argv |
18+
| test.cpp:117:28:117:32 | *input | semmle.label | *input |
19+
| test.cpp:132:27:132:31 | *input | semmle.label | *input |
20+
| test.cpp:169:27:169:31 | *input | semmle.label | *input |
21+
| test.cpp:179:26:179:30 | *input | semmle.label | *input |
22+
| test.cpp:185:27:185:31 | *input | semmle.label | *input |
23+
| test.cpp:192:28:192:32 | *input | semmle.label | *input |
24+
| test.cpp:279:27:279:31 | *input | semmle.label | *input |
25+
| test.cpp:308:28:308:32 | *input | semmle.label | *input |
26+
| test.cpp:342:27:342:31 | *input | semmle.label | *input |
27+
| test.cpp:349:27:349:31 | *input | semmle.label | *input |
28+
| test.cpp:369:27:369:57 | *call to getenv | semmle.label | *call to getenv |
29+
| test.cpp:369:27:369:57 | *call to getenv | semmle.label | *call to getenv |
30+
| test.cpp:372:28:372:28 | *s | semmle.label | *s |
31+
| test.cpp:380:20:380:22 | fread output argument | semmle.label | fread output argument |
32+
| test.cpp:383:28:383:28 | *s | semmle.label | *s |
33+
| test.cpp:421:26:421:30 | *input | semmle.label | *input |
2034
subpaths
2135
#select
22-
| test.cpp:109:28:109:32 | *input | test.cpp:99:27:99:30 | **argv | test.cpp:109:28:109:32 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:108:29:108:31 | \\s+ | regular expression | test.cpp:99:27:99:30 | **argv | user-provided value |
23-
| test.cpp:124:27:124:31 | *input | test.cpp:99:27:99:30 | **argv | test.cpp:124:27:124:31 | *input | This $@ that depends on a $@ may run slow on strings starting with '0.9' and with many repetitions of '99'. | test.cpp:123:33:123:35 | \\d+ | regular expression | test.cpp:99:27:99:30 | **argv | user-provided value |
24-
| test.cpp:161:27:161:31 | *input | test.cpp:99:27:99:30 | **argv | test.cpp:161:27:161:31 | *input | This $@ that depends on a $@ may run slow on strings starting with ' ' and with many repetitions of ' '. | test.cpp:160:29:160:31 | \\s+ | regular expression | test.cpp:99:27:99:30 | **argv | user-provided value |
25-
| test.cpp:161:27:161:31 | *input | test.cpp:99:27:99:30 | **argv | test.cpp:161:27:161:31 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:160:24:160:26 | \\s+ | regular expression | test.cpp:99:27:99:30 | **argv | user-provided value |
26-
| test.cpp:171:26:171:30 | *input | test.cpp:99:27:99:30 | **argv | test.cpp:171:26:171:30 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:170:29:170:31 | \\s+ | regular expression | test.cpp:99:27:99:30 | **argv | user-provided value |
27-
| test.cpp:177:27:177:31 | *input | test.cpp:99:27:99:30 | **argv | test.cpp:177:27:177:31 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:176:29:176:31 | \\s+ | regular expression | test.cpp:99:27:99:30 | **argv | user-provided value |
28-
| test.cpp:184:28:184:32 | *input | test.cpp:99:27:99:30 | **argv | test.cpp:184:28:184:32 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:183:29:183:31 | \\s+ | regular expression | test.cpp:99:27:99:30 | **argv | user-provided value |
29-
| test.cpp:271:27:271:31 | *input | test.cpp:99:27:99:30 | **argv | test.cpp:271:27:271:31 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:270:29:270:31 | \\s+ | regular expression | test.cpp:99:27:99:30 | **argv | user-provided value |
30-
| test.cpp:300:28:300:32 | *input | test.cpp:99:27:99:30 | **argv | test.cpp:300:28:300:32 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:299:29:299:31 | \\s+ | regular expression | test.cpp:99:27:99:30 | **argv | user-provided value |
36+
| test.cpp:117:28:117:32 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:117:28:117:32 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:116:29:116:31 | \\s+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
37+
| test.cpp:132:27:132:31 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:132:27:132:31 | *input | This $@ that depends on a $@ may run slow on strings starting with '0.9' and with many repetitions of '99'. | test.cpp:131:33:131:35 | \\d+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
38+
| test.cpp:169:27:169:31 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:169:27:169:31 | *input | This $@ that depends on a $@ may run slow on strings starting with ' ' and with many repetitions of ' '. | test.cpp:168:29:168:31 | \\s+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
39+
| test.cpp:169:27:169:31 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:169:27:169:31 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:168:24:168:26 | \\s+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
40+
| test.cpp:179:26:179:30 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:179:26:179:30 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:178:29:178:31 | \\s+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
41+
| test.cpp:185:27:185:31 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:185:27:185:31 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:184:29:184:31 | \\s+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
42+
| test.cpp:192:28:192:32 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:192:28:192:32 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:191:29:191:31 | \\s+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
43+
| test.cpp:279:27:279:31 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:279:27:279:31 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:278:29:278:31 | \\s+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
44+
| test.cpp:308:28:308:32 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:308:28:308:32 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:307:29:307:31 | \\s+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
45+
| test.cpp:342:27:342:31 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:342:27:342:31 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of '9'. | test.cpp:341:25:341:27 | \\d+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
46+
| test.cpp:349:27:349:31 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:349:27:349:31 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of 'a'. | test.cpp:348:26:348:27 | .* | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |
47+
| test.cpp:372:28:372:28 | *s | test.cpp:369:27:369:57 | *call to getenv | test.cpp:372:28:372:28 | *s | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:371:29:371:31 | \\s+ | regular expression | test.cpp:369:27:369:57 | *call to getenv | user-provided value |
48+
| test.cpp:383:28:383:28 | *s | test.cpp:380:20:380:22 | fread output argument | test.cpp:383:28:383:28 | *s | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.cpp:382:29:382:31 | \\s+ | regular expression | test.cpp:380:20:380:22 | fread output argument | user-provided value |
49+
| test.cpp:421:26:421:30 | *input | test.cpp:107:27:107:30 | **argv | test.cpp:421:26:421:30 | *input | This $@ that depends on a $@ may run slow on strings with many repetitions of 'a'. | test.cpp:420:25:420:26 | a+ | regular expression | test.cpp:107:27:107:30 | **argv | user-provided value |

cpp/ql/test/query-tests/Security/CWE/CWE-1333/test.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ class regex_token_iterator {
6464
typedef regex_iterator<const char*, char> cregex_iterator;
6565
typedef regex_token_iterator<const char*, char> cregex_token_iterator;
6666

67+
// Stubs for a couple of C standard-library sources modeled by
68+
// `semmle.code.cpp.security.FlowSources` so that we can exercise
69+
// non-`argv` sources in the polynomial-ReDoS test suite.
70+
char* getenv(const char* name);
71+
typedef unsigned long size_t;
72+
struct FILE;
73+
size_t fread(void* ptr, size_t sz, size_t n, FILE* stream);
74+
6775
} // namespace std
6876

6977
// -----------------------------------------------------------------------------
@@ -323,5 +331,95 @@ int main(int argc, char** argv) {
323331
std::regex_replace(input, re, std::string(""));
324332
}
325333

334+
// -------------------------------------------------------------------------
335+
// 7. Additional superlinear patterns (ported from Java/JS polynomial suites).
336+
// -------------------------------------------------------------------------
337+
338+
// BAD: (\d+)*$ — the outer `*` allows quadratic backtracking on non-matching
339+
// digit-heavy input (Java polynomial-ReDoS test `Test.java`).
340+
{
341+
std::regex re("(\\d+)*$");
342+
std::regex_search(input, re);
343+
}
344+
345+
// BAD: .*.*=.* — classic polynomial pattern, cf. JS
346+
// `polynomial-redos/tst.js`.
347+
{
348+
std::regex re(".*.*=.*");
349+
std::regex_search(input, re);
350+
}
351+
352+
// GOOD (observed): `^(\w+\s?)*$` — a "trim-and-split" style pattern that
353+
// the shared engine's polynomial analysis does not currently flag as
354+
// super-linear (the optional `\s?` inside the outer `*` does not
355+
// produce a polynomial-backtracking pivot term). The Java and
356+
// JavaScript polynomial-ReDoS queries behave the same way on this
357+
// shape; documented here as a negative case rather than divergence.
358+
{
359+
std::regex re("^(\\w+\\s?)*$");
360+
std::regex_match(input, re);
361+
}
362+
363+
// -------------------------------------------------------------------------
364+
// 8. Source variety — non-`argv` C++ threat-model sources.
365+
// -------------------------------------------------------------------------
366+
367+
// BAD: `std::getenv` is modeled as a LocalFlowSource.
368+
{
369+
const char* env = std::getenv("USER_REGEX_INPUT");
370+
std::string s(env);
371+
std::regex re("^\\s+|\\s+$");
372+
std::regex_replace(s, re, std::string(""));
373+
}
374+
375+
// BAD: `std::fread` is modeled as a RemoteFlowSource (bytes read from a
376+
// stream / file). This exercises the RemoteFlowSource half of the
377+
// `FlowSource` union used by `PolynomialRedosConfig::isSource`.
378+
{
379+
char buf[256];
380+
std::fread(buf, 1, sizeof(buf), (std::FILE*)0);
381+
std::string s(buf);
382+
std::regex re("^\\s+|\\s+$");
383+
std::regex_replace(s, re, std::string(""));
384+
}
385+
386+
// -------------------------------------------------------------------------
387+
// 9. Sanitized / length-checked user input (GOOD).
388+
//
389+
// The polynomial ReDoS query treats calls to `LengthRestrictedFunction`s
390+
// as barriers, so user input that has been laundered through such a
391+
// getter (here: `Request::getHeader`, which is a header-style getter)
392+
// should not produce an alert even though the pattern is superlinear
393+
// and the *original* value was user-controlled.
394+
// -------------------------------------------------------------------------
395+
396+
// GOOD: `argv[1]` is user-controlled but is passed through a
397+
// header-style barrier before reaching the regex match.
398+
{
399+
Request req;
400+
std::string sanitized = req.getHeader(argv[1]);
401+
std::regex re("^\\s+|\\s+$");
402+
std::regex_replace(sanitized, re, std::string(""));
403+
}
404+
405+
// -------------------------------------------------------------------------
406+
// 10. Overlap sanity: an exponential pattern on user input.
407+
//
408+
// The shared engine splits reporting between `cpp/redos` (exponential)
409+
// and `cpp/polynomial-redos` (polynomial). Many "exponential" nested-
410+
// quantifier patterns also contain a polynomial-backtracking sub-term,
411+
// so BOTH queries fire — matching the behaviour observed in the Java
412+
// and JavaScript suites. `(a+)+b` is such a pattern: `cpp/redos`
413+
// reports the outer nested quantifier, and `cpp/polynomial-redos`
414+
// reports the inner `a+` term.
415+
// -------------------------------------------------------------------------
416+
417+
// BAD (also reported by cpp/redos): the inner `a+` is a polynomial
418+
// backtracking term on user input.
419+
{
420+
std::regex re("(a+)+b");
421+
std::regex_match(input, re);
422+
}
423+
326424
return 0;
327425
}

0 commit comments

Comments
 (0)