// TODO: remove need for this option /* { dg-additional-options "-fanalyzer-checker=taint" } */ /* We need this, otherwise the warnings are emitted inside the macros, which makes it hard to write the DejaGnu directives. */ /* { dg-additional-options " -ftrack-macro-expansion=0" } */ #include "analyzer-decls.h" /* An assertion macro that has a call to a __noreturn__ function. */ extern void my_assert_fail (const char *expr, const char *file, int line) __attribute__ ((__noreturn__)); #define MY_ASSERT_1(EXPR) \ do { if (!(EXPR)) my_assert_fail (#EXPR, __FILE__, __LINE__); } while (0) int test_not_tainted_MY_ASSERT_1 (int n) { MY_ASSERT_1 (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ return n * n; } int __attribute__((tainted_args)) test_tainted_MY_ASSERT_1 (int n) { MY_ASSERT_1 (n > 0); /* { dg-warning "use of attacked-controlled value in condition for assertion \\\[CWE-617\\\] \\\[-Wanalyzer-tainted-assertion\\\]" "warning" } */ /* { dg-message "treating 'my_assert_fail' as an assertion failure handler due to '__attribute__\\(\\(__noreturn__\\)\\)'" "final event" { target *-*-* } .-1 } */ return n * n; } /* An assertion macro that has a call to __builtin_unreachable. */ #define MY_ASSERT_2(EXPR) \ do { if (!(EXPR)) __builtin_unreachable (); } while (0) int test_not_tainted_MY_ASSERT_2 (int n) { MY_ASSERT_2 (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ return n * n; } int __attribute__((tainted_args)) test_tainted_MY_ASSERT_2 (int n) { MY_ASSERT_2 (n > 0); /* { dg-warning "-Wanalyzer-tainted-assertion" "warning" } */ /* { dg-message "treating '__builtin_unreachable' as an assertion failure handler" "final event" { target *-*-* } .-1 } */ return n * n; } /* An assertion macro that's preprocessed away. The analyzer doesn't see this, so can't warn. */ #define MY_ASSERT_3(EXPR) do { } while (0) int test_not_tainted_MY_ASSERT_3 (int n) { MY_ASSERT_3 (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ return n * n; } int __attribute__((tainted_args)) test_tainted_MY_ASSERT_3 (int n) { MY_ASSERT_3 (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ return n * n; } /* A macro that isn't an assertion. */ extern void do_something_benign (); #define NOT_AN_ASSERT(EXPR) \ do { if (!(EXPR)) do_something_benign (); } while (0) int test_not_tainted_NOT_AN_ASSERT (int n) { NOT_AN_ASSERT (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ return n * n; } int __attribute__((tainted_args)) test_tainted_NOT_AN_ASSERT (int n) { NOT_AN_ASSERT (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ return n * n; } /* A condition that isn't an assertion. */ int __attribute__((tainted_args)) test_tainted_condition (int n) { if (n > 0) /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ return 1; else return -1; } /* More complicated expressions in assertions. */ int g; void __attribute__((tainted_args)) test_compound_condition_in_assert_1 (int n) { MY_ASSERT_1 ((n * 2) < (g + 3)); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } void __attribute__((tainted_args)) test_compound_condition_in_assert_2 (int x, int y) { MY_ASSERT_1 (x < 100 && y < 100); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } void __attribute__((tainted_args)) test_compound_condition_in_assert_3 (int x, int y) { MY_ASSERT_1 (x < 100 || y < 100); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } void __attribute__((tainted_args)) test_sanitized_expression_in_assert (int n) { __analyzer_dump_state ("taint", n); /* { dg-warning "state: 'tainted'" } */ if (n < 0 || n >= 100) return; __analyzer_dump_state ("taint", n); /* { dg-warning "state: 'stop'" } */ MY_ASSERT_1 (n < 200); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ } void __attribute__((tainted_args)) test_sanitization_then_ok_assertion (unsigned n) { if (n >= 100) return; /* Shouldn't warn here, as g isn't attacker-controlled. */ MY_ASSERT_1 (g > 42); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ } void __attribute__((tainted_args)) test_good_assert_then_bad_assert (unsigned n) { /* Shouldn't warn here, as g isn't attacker-controlled. */ MY_ASSERT_1 (g > 42); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ /* ...but n is: */ MY_ASSERT_1 (n < 100); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } void __attribute__((tainted_args)) test_bad_assert_then_good_assert (unsigned n) { MY_ASSERT_1 (n < 100); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ MY_ASSERT_1 (g > 42); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ } /* */ void __attribute__((tainted_args)) test_zero_MY_ASSERT_1 (unsigned n) { if (n >= 100) MY_ASSERT_1 (0); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } void __attribute__((tainted_args)) test_nonzero_MY_ASSERT_1 (unsigned n) { if (n >= 100) MY_ASSERT_1 (1); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ } void __attribute__((tainted_args)) test_zero_MY_ASSERT_2 (unsigned n) { if (n >= 100) MY_ASSERT_2 (0); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } void __attribute__((tainted_args)) test_nonzero_MY_ASSERT_2 (unsigned n) { if (n >= 100) MY_ASSERT_2 (1); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ } /* Assertions that call a subroutine to do validity checking. */ static int __analyzer_valid_1 (int x) { return x < 100; } void __attribute__((tainted_args)) test_assert_calling_valid_1 (int n) { MY_ASSERT_1 (__analyzer_valid_1 (n)); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } static int __analyzer_valid_2 (int x) { return x < 100; } void __attribute__((tainted_args)) test_assert_calling_valid_2 (int n) { MY_ASSERT_1 (__analyzer_valid_2 (n)); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } static int __analyzer_valid_3 (int x, int y) { if (x >= 100) return 0; if (y >= 100) return 0; return 1; } void __attribute__((tainted_args)) test_assert_calling_valid_3 (int a, int b) { MY_ASSERT_1 (__analyzer_valid_3 (a, b)); /* { dg-warning "-Wanalyzer-tainted-assertion" "TODO" { xfail *-*-* } } */ } /* 'switch' statements with supposedly unreachable cases/defaults. */ int __attribute__((tainted_args)) test_switch_default (int n) { switch (n) /* { dg-message "use of attacker-controlled value for control flow" "why" } */ /* { dg-message "following 'default:' branch" "dest" { target *-*-* } .-1 } */ { case 0: return 5; case 1: return 22; case 2: return -1; default: /* The wording is rather inaccurate here. */ __builtin_unreachable (); /* { dg-warning "use of attacked-controlled value in condition for assertion" } */ } } int __attribute__((tainted_args)) test_switch_unhandled_case (int n) { switch (n) /* { dg-message "use of attacker-controlled value for control flow" "why" } */ /* { dg-message "following 'default:' branch" "dest" { target *-*-* } .-1 } */ { case 0: return 5; case 1: return 22; case 2: return -1; } /* The wording is rather inaccurate here. */ __builtin_unreachable (); /* { dg-warning "use of attacked-controlled value in condition for assertion" } */ } int __attribute__((tainted_args)) test_switch_bogus_case_MY_ASSERT_1 (int n) { switch (n) { default: case 0: return 5; case 1: return 22; case 2: return -1; case 42: MY_ASSERT_1 (0); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } } int __attribute__((tainted_args)) test_switch_bogus_case_MY_ASSERT_2 (int n) { switch (n) { default: case 0: return 5; case 1: return 22; case 2: return -1; case 42: MY_ASSERT_2 (0); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ } } int __attribute__((tainted_args)) test_switch_bogus_case_unreachable (int n) { switch (n) { default: case 0: return 5; case 1: return 22; case 2: return -1; case 42: /* This case gets optimized away before we see it. */ __builtin_unreachable (); } } /* Contents of a struct. */ struct s { int x; int y; }; int __attribute__((tainted_args)) test_assert_struct (struct s *p) { MY_ASSERT_1 (p->x < p->y); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ }