Skip to content

Commit b43cae6

Browse files
Fix a bunch of crashes discovered by fuzzing cppfront (#1383)
* Three more fuzz crashes. * Add regression test for crash 10. * Error instead of crash if users write silly things in aliases. * Fix assertion on comments near end of file. * Disallow declarations of parents after functions. * Fix more fuzz crashes. * Rename error test cases to `*-error.cpp2` * Minor tidying to fit house style `exit(1)` -> `exit(EXIT_FAILURE)` Branch and loop bodies are always enclosed in `{` `}` even when they contain a single line --------- Co-authored-by: Herb Sutter <herb.sutter@gmail.com>
1 parent 2f3634d commit b43cae6

29 files changed

+242
-40
lines changed

include/cpp2util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ class contract_group {
561561
std::cerr << ": " << msg;
562562
}
563563
std::cerr << "\n";
564-
std::terminate();
564+
std::exit(EXIT_FAILURE);
565565
}
566566

567567
auto inline cpp2_default = contract_group(
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#else
2+
#else
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
crash_10: (foo: i32) = {
2+
assert( 10LL as i32 == foo$);
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
crash_10: (foo: i32) = {
2+
assert( 10LL as i32 == foo$);
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
crash_89: () = {
2+
f := new<int>(0);
3+
_ = f is decltype.f);
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print: <T: type> (inout out: std::ostream=args: T) requires true = {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
crash_m0b: type = {
2+
}
3+
4+
crash_m0c: type = {
5+
name: i32;
6+
get_name: (this) -> i32 = { return name; }
7+
this: crash_m0b;
8+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
outer: type = {
2+
x: requires true == 42;
3+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
main: () -> int = {
3+
x := crash_m0();
4+
_ = x;
5+
}
6+
7+
crash_m0: type = {
8+
operator-: (this, _) -> int = 0;/* Comment starts here
9+
And continues here
10+
*/
11+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
main: () = {
2+
e: unsigned char
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
crash_96: @print type = {
2+
namespace_alias: namespace = type_alias: type == array<int,10>;
3+
}
4+
5+
crash_96a: @print type = {
6+
test: () = {
7+
namespace_alias: namespace = type_alias: type == array<int,10>;
8+
}
9+
}

regression-tests/test-results/clang-12-c++20/pure2-expected-is-as.cpp.output

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,56 +80,56 @@ In file included from pure2-expected-is-as.cpp:7:
8080
../../../include/cpp2util.h:469:72: error: invalid application of 'sizeof' to a function type
8181
(std::is_floating_point_v<From> && std::is_floating_point_v<To> && sizeof(From) > sizeof(To)) || // NOLINT(misc-redundant-expression)
8282
^~~~~~~~~~~~
83-
../../../include/cpp2util.h:2923:19: note: in instantiation of variable template specialization 'cpp2::impl::is_narrowing_v' requested here
83+
../../../include/cpp2util.h:2924:19: note: in instantiation of variable template specialization 'cpp2::impl::is_narrowing_v' requested here
8484
if constexpr (is_narrowing_v<C, CPP2_TYPEOF(x)>) {
8585
^
8686
pure2-expected-is-as.cpp2:39:28: note: in instantiation of function template specialization 'cpp2::impl::as_<int, double (&)(double) noexcept>' requested here
8787
auto val1 {cpp2::impl::as_<int>(ex1)};
8888
^
8989
In file included from pure2-expected-is-as.cpp:7:
90-
../../../include/cpp2util.h:2943:12: error: no matching function for call to 'as'
90+
../../../include/cpp2util.h:2944:12: error: no matching function for call to 'as'
9191
return as<C>(CPP2_FORWARD(x));
9292
^~~~~
9393
pure2-expected-is-as.cpp2:39:28: note: in instantiation of function template specialization 'cpp2::impl::as_<int, double (&)(double) noexcept>' requested here
9494
auto val1 {cpp2::impl::as_<int>(ex1)};
9595
^
96-
../../../include/cpp2util.h:1901:16: note: candidate template ignored: constraints not satisfied [with C = int, x:auto = double (&)(double) noexcept]
96+
../../../include/cpp2util.h:1902:16: note: candidate template ignored: constraints not satisfied [with C = int, x:auto = double (&)(double) noexcept]
9797
constexpr auto as(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS) -> decltype(auto)
9898
^
99-
../../../include/cpp2util.h:1907:18: note: because 'std::is_scalar_v<std::remove_cvref_t<decltype(x)> >' evaluated to false
99+
../../../include/cpp2util.h:1908:18: note: because 'std::is_scalar_v<std::remove_cvref_t<decltype(x)> >' evaluated to false
100100
(std::is_scalar_v<CPP2_TYPEOF(x)> && !std::is_enum_v<CPP2_TYPEOF(x)>)
101101
^
102-
../../../include/cpp2util.h:1908:17: note: and 'std::is_floating_point_v<std::remove_cvref_t<decltype(x)> >' evaluated to false
102+
../../../include/cpp2util.h:1909:17: note: and 'std::is_floating_point_v<std::remove_cvref_t<decltype(x)> >' evaluated to false
103103
|| std::is_floating_point_v<CPP2_TYPEOF(x)>
104104
^
105-
../../../include/cpp2util.h:1909:17: note: and 'std::is_base_of_v<int, std::remove_cvref_t<decltype(x)> >' evaluated to false
105+
../../../include/cpp2util.h:1910:17: note: and 'std::is_base_of_v<int, std::remove_cvref_t<decltype(x)> >' evaluated to false
106106
|| std::is_base_of_v<C, CPP2_TYPEOF(x)>
107107
^
108-
../../../include/cpp2util.h:1910:17: note: and 'std::is_base_of_v<std::remove_cvref_t<decltype(x)>, int>' evaluated to false
108+
../../../include/cpp2util.h:1911:17: note: and 'std::is_base_of_v<std::remove_cvref_t<decltype(x)>, int>' evaluated to false
109109
|| std::is_base_of_v<CPP2_TYPEOF(x), C>
110110
^
111-
../../../include/cpp2util.h:1911:30: note: and 'C({std::forward<decltype(x)>(x)})' would be invalid: cannot initialize a value of type 'int' with an lvalue of type 'double (double) noexcept'
111+
../../../include/cpp2util.h:1912:30: note: and 'C({std::forward<decltype(x)>(x)})' would be invalid: cannot initialize a value of type 'int' with an lvalue of type 'double (double) noexcept'
112112
|| requires { C{CPP2_FORWARD(x)}; }
113113
^
114114
../../../include/cpp2util.h:327:37: note: expanded from macro 'CPP2_FORWARD'
115115
#define CPP2_FORWARD(x) std::forward<decltype(x)>(x)
116116
^
117-
../../../include/cpp2util.h:2040:6: note: candidate template ignored: constraints not satisfied [with C = int, X = double (&)(double) noexcept]
117+
../../../include/cpp2util.h:2041:6: note: candidate template ignored: constraints not satisfied [with C = int, X = double (&)(double) noexcept]
118118
auto as(X&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS) -> decltype(auto)
119119
^
120-
../../../include/cpp2util.h:2039:23: note: because 'specialization_of_template<double (&)(double) noexcept, std::variant>' evaluated to false
120+
../../../include/cpp2util.h:2040:23: note: because 'specialization_of_template<double (&)(double) noexcept, std::variant>' evaluated to false
121121
template< typename C, specialization_of_template<std::variant> X >
122122
^
123-
../../../include/cpp2util.h:891:7: note: because 'specialization_of_template_helper<C>(std::forward<X>(x))' would be invalid: no matching function for call to 'specialization_of_template_helper'
123+
../../../include/cpp2util.h:892:7: note: because 'specialization_of_template_helper<C>(std::forward<X>(x))' would be invalid: no matching function for call to 'specialization_of_template_helper'
124124
{ specialization_of_template_helper<C>(std::forward<X>(x)) } -> std::same_as<std::true_type>;
125125
^
126-
../../../include/cpp2util.h:2087:16: note: candidate template ignored: constraints not satisfied [with T = int, X = double (&)(double) noexcept]
126+
../../../include/cpp2util.h:2088:16: note: candidate template ignored: constraints not satisfied [with T = int, X = double (&)(double) noexcept]
127127
constexpr auto as( X && x ) -> decltype(auto) {
128128
^
129-
../../../include/cpp2util.h:2086:22: note: because 'same_type_as<double (&)(double) noexcept, std::any>' evaluated to false
129+
../../../include/cpp2util.h:2087:22: note: because 'same_type_as<double (&)(double) noexcept, std::any>' evaluated to false
130130
template<typename T, same_type_as<std::any> X>
131131
^
132-
../../../include/cpp2util.h:921:29: note: because 'std::same_as<std::remove_cvref_t<double (&)(double) noexcept>, std::remove_cvref_t<any> >' evaluated to false
132+
../../../include/cpp2util.h:922:29: note: because 'std::same_as<std::remove_cvref_t<double (&)(double) noexcept>, std::remove_cvref_t<any> >' evaluated to false
133133
concept same_type_as = std::same_as<std::remove_cvref_t<X>, std::remove_cvref_t<C>>;
134134
^
135135
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/concepts:63:19: note: because '__detail::__same_as<double (double) noexcept, std::any>' evaluated to false
@@ -138,19 +138,19 @@ concept same_type_as = std::same_as<std::remove_cvref_t<X>, std::remove_cvref_t<
138138
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/concepts:57:27: note: because 'std::is_same_v<double (double) noexcept, std::any>' evaluated to false
139139
concept __same_as = std::is_same_v<_Tp, _Up>;
140140
^
141-
../../../include/cpp2util.h:2130:16: note: candidate template ignored: constraints not satisfied [with T = int, X = double (&)(double) noexcept]
141+
../../../include/cpp2util.h:2131:16: note: candidate template ignored: constraints not satisfied [with T = int, X = double (&)(double) noexcept]
142142
constexpr auto as( X&& x ) -> decltype(auto) {
143143
^
144-
../../../include/cpp2util.h:2129:22: note: because 'specialization_of_template<double (&)(double) noexcept, std::optional>' evaluated to false
144+
../../../include/cpp2util.h:2130:22: note: because 'specialization_of_template<double (&)(double) noexcept, std::optional>' evaluated to false
145145
template<typename T, specialization_of_template<std::optional> X>
146146
^
147-
../../../include/cpp2util.h:891:7: note: because 'specialization_of_template_helper<C>(std::forward<X>(x))' would be invalid: no matching function for call to 'specialization_of_template_helper'
147+
../../../include/cpp2util.h:892:7: note: because 'specialization_of_template_helper<C>(std::forward<X>(x))' would be invalid: no matching function for call to 'specialization_of_template_helper'
148148
{ specialization_of_template_helper<C>(std::forward<X>(x)) } -> std::same_as<std::true_type>;
149149
^
150-
../../../include/cpp2util.h:1876:16: note: candidate function template not viable: requires 0 arguments, but 1 was provided
150+
../../../include/cpp2util.h:1877:16: note: candidate function template not viable: requires 0 arguments, but 1 was provided
151151
constexpr auto as() -> auto
152152
^
153-
../../../include/cpp2util.h:1887:16: note: candidate function template not viable: requires 0 arguments, but 1 was provided
153+
../../../include/cpp2util.h:1888:16: note: candidate function template not viable: requires 0 arguments, but 1 was provided
154154
constexpr auto as() -> auto
155155
^
156156
pure2-expected-is-as.cpp2:39:37: error: use of undeclared identifier 'ex1'
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mixed-bugfix-for-double-pound-else-error.cpp2...
2+
mixed-bugfix-for-double-pound-else-error.cpp2(2,1): error: #else does not match a prior #if
3+
mixed-bugfix-for-double-pound-else-error.cpp2(3,1): error: #else does not match a prior #if
4+
mixed-bugfix-for-double-pound-else-error.cpp2(3,1): error: #else already encountered for this #if
5+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pure2-bugfix-for-late-comments.cpp
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pure2-bugfix-for-assert-capture-error.cpp2...
2+
pure2-bugfix-for-assert-capture-error.cpp2(2,29): error: $ (capture) cannot appear here - it must appear in an anonymous expression function, a postcondition, or an interpolated string literal (at '$')
3+
pure2-bugfix-for-assert-capture-error.cpp2(2,23): error: expected ')' at the end of the contract (at '==')
4+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pure2-bugfix-for-bad-capture-error.cpp2...
2+
pure2-bugfix-for-bad-capture-error.cpp2(2,29): error: $ (capture) cannot appear here - it must appear in an anonymous expression function, a postcondition, or an interpolated string literal (at '$')
3+
pure2-bugfix-for-bad-capture-error.cpp2(2,23): error: expected ')' at the end of the contract (at '==')
4+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pure2-bugfix-for-bad-decltype-error.cpp2...
2+
pure2-bugfix-for-bad-decltype-error.cpp2(3,14): error: 'decltype' must be followed by a single parenthesized expression
3+
pure2-bugfix-for-bad-decltype-error.cpp2(3,24): error: 'is' must be followed by a type-id or an expression
4+
pure2-bugfix-for-bad-decltype-error.cpp2(3,14): error: 'decltype' must be followed by a single parenthesized expression
5+
pure2-bugfix-for-bad-decltype-error.cpp2(3,24): error: 'is' must be followed by a type-id or an expression
6+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pure2-bugfix-for-bad-parameter-error.cpp2...
2+
pure2-bugfix-for-bad-parameter-error.cpp2(1,50): error: parameter must be initialized with an expression (at ')')
3+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pure2-bugfix-for-functions-before-superclasses-error.cpp2...
2+
pure2-bugfix-for-functions-before-superclasses-error.cpp2(7,3): error: a type cannot declare a parent after defining a function
3+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pure2-bugfix-for-invalid-alias-error.cpp2...
2+
pure2-bugfix-for-invalid-alias-error.cpp2(2,25): error: invalid alias declaration - expected 'type', 'namespace', or a type-id after ':'
3+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
2+
#define CPP2_IMPORT_STD Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
#line 1 "pure2-bugfix-for-late-comments.cpp2"
10+
11+
#line 7 "pure2-bugfix-for-late-comments.cpp2"
12+
class crash_m0;
13+
14+
15+
//=== Cpp2 type definitions and function declarations ===========================
16+
17+
#line 1 "pure2-bugfix-for-late-comments.cpp2"
18+
19+
#line 2 "pure2-bugfix-for-late-comments.cpp2"
20+
[[nodiscard]] auto main() -> int;
21+
22+
#line 7 "pure2-bugfix-for-late-comments.cpp2"
23+
class crash_m0 {
24+
public: [[nodiscard]] auto operator-([[maybe_unused]] auto const& unnamed_param_2) const& -> int;
25+
public: crash_m0() = default;
26+
public: crash_m0(crash_m0 const&) = delete; /* No 'that' constructor, suppress copy */
27+
public: auto operator=(crash_m0 const&) -> void = delete;
28+
29+
30+
#line 11 "pure2-bugfix-for-late-comments.cpp2"
31+
};
32+
33+
34+
//=== Cpp2 function definitions =================================================
35+
36+
#line 1 "pure2-bugfix-for-late-comments.cpp2"
37+
38+
#line 2 "pure2-bugfix-for-late-comments.cpp2"
39+
[[nodiscard]] auto main() -> int{
40+
auto x {crash_m0()};
41+
static_cast<void>(cpp2::move(x));
42+
}
43+
44+
#line 8 "pure2-bugfix-for-late-comments.cpp2"
45+
[[nodiscard]] auto crash_m0::operator-([[maybe_unused]] auto const& unnamed_param_2) const& -> int { return 0; }/* Comment starts here
46+
And continues here
47+
*/
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-bugfix-for-late-comments.cpp2... ok (all Cpp2, passes safety checks)
2+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
pure2-bugfix-for-naked-unsigned-char-error.cpp2...
2+
pure2-bugfix-for-naked-unsigned-char-error.cpp2(2,8): error: 'unsigned char' - did you mean 'u8' (usually best) or 'cpp2::_uchar'?
3+
pure2-bugfix-for-naked-unsigned-char-error.cpp2(2,8): error: 'unsigned char' is an old-style C/C++ multi-word keyword type
4+
- most such types should be used only for interoperability with older code
5+
- using those when you need them is fine, but name them with these short names instead:
6+
short, ushort, int, uint, long, ulong, longlong, ulonglong, longdouble, _schar, _uchar
7+
- see also cpp2util.h > "Convenience names for integer types"
8+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pure2-bugfix-for-namespace-error.cpp2...
2+
pure2-bugfix-for-namespace-error.cpp2(2,22): error: types cannot contain namespaces (at 'namespace')
3+

source/io.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,12 @@ class braces_tracker
362362
else { --else_net_braces; }
363363
}
364364

365-
auto found_preprocessor_else() -> void {
366-
assert (!found_else);
365+
auto found_preprocessor_else_was_there_another() -> bool {
366+
if (found_else) {
367+
return true;
368+
}
367369
found_else = true;
370+
return false;
368371
}
369372

370373
// If the "if" and "else" branches opened/closed the same net number
@@ -469,7 +472,14 @@ class braces_tracker
469472
);
470473
}
471474

472-
preprocessor.back().found_preprocessor_else();
475+
if (preprocessor.back().found_preprocessor_else_was_there_another()) {
476+
// If this is the second or subsequent #else, it doesn't match
477+
// the prior #if, so report an error
478+
errors.emplace_back(
479+
lineno,
480+
"#else already encountered for this #if"
481+
);
482+
};
473483
}
474484

475485
// Exiting an #endif

source/lex.h

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -713,13 +713,19 @@ auto lex_line(
713713
tokens.pop_back();
714714
++num_merged_tokens;
715715
}
716-
717-
tokens.push_back({
718-
&generated_text.back()[0],
719-
std::ssize(generated_text.back()),
720-
pos,
721-
lexeme::Keyword
722-
});
716+
717+
// It's an error to have more than one of these, but we require that
718+
// the number of tokens has not gone down. So just push back as many
719+
// tokens as we merged. This will ensure that the token count remains
720+
// the same.
721+
for (auto i = 0; i < num_merged_tokens; i++) {
722+
tokens.push_back({
723+
&generated_text.back()[0],
724+
std::ssize(generated_text.back()),
725+
pos,
726+
lexeme::Keyword
727+
});
728+
}
723729

724730
if (num_merged_tokens > 1)
725731
{
@@ -750,8 +756,6 @@ auto lex_line(
750756
" short, ushort, int, uint, long, ulong, longlong, ulonglong, longdouble, _schar, _uchar\n"
751757
" - see also cpp2util.h > \"Convenience names for integer types\""
752758
);
753-
754-
return;
755759
}
756760

757761
tokens.push_back(last_token);

0 commit comments

Comments
 (0)