diff --git a/base/third_party/symbolize/demangle.cc b/base/third_party/symbolize/demangle.cc index 9276c5b879a8c..2632646dd4072 100644 --- a/base/third_party/symbolize/demangle.cc +++ b/base/third_party/symbolize/demangle.cc @@ -34,13 +34,14 @@ // // Note that we only have partial C++0x support yet. -#include // for NULL - #include "demangle.h" -#include "utilities.h" #if defined(GLOG_OS_WINDOWS) #include +#else +#include +#include +#include #endif _START_GOOGLE_NAMESPACE_ @@ -49,117 +50,199 @@ _START_GOOGLE_NAMESPACE_ typedef struct { const char *abbrev; const char *real_name; + // Number of arguments in context, or 0 if disallowed. + int arity; } AbbrevPair; // List of operators from Itanium C++ ABI. static const AbbrevPair kOperatorList[] = { - { "nw", "new" }, - { "na", "new[]" }, - { "dl", "delete" }, - { "da", "delete[]" }, - { "ps", "+" }, - { "ng", "-" }, - { "ad", "&" }, - { "de", "*" }, - { "co", "~" }, - { "pl", "+" }, - { "mi", "-" }, - { "ml", "*" }, - { "dv", "/" }, - { "rm", "%" }, - { "an", "&" }, - { "or", "|" }, - { "eo", "^" }, - { "aS", "=" }, - { "pL", "+=" }, - { "mI", "-=" }, - { "mL", "*=" }, - { "dV", "/=" }, - { "rM", "%=" }, - { "aN", "&=" }, - { "oR", "|=" }, - { "eO", "^=" }, - { "ls", "<<" }, - { "rs", ">>" }, - { "lS", "<<=" }, - { "rS", ">>=" }, - { "eq", "==" }, - { "ne", "!=" }, - { "lt", "<" }, - { "gt", ">" }, - { "le", "<=" }, - { "ge", ">=" }, - { "nt", "!" }, - { "aa", "&&" }, - { "oo", "||" }, - { "pp", "++" }, - { "mm", "--" }, - { "cm", "," }, - { "pm", "->*" }, - { "pt", "->" }, - { "cl", "()" }, - { "ix", "[]" }, - { "qu", "?" }, - { "st", "sizeof" }, - { "sz", "sizeof" }, - { NULL, NULL }, + // New has special syntax (not currently supported). + {"nw", "new", 0}, + {"na", "new[]", 0}, + + // Works except that the 'gs' prefix is not supported. + {"dl", "delete", 1}, + {"da", "delete[]", 1}, + + {"ps", "+", 1}, // "positive" + {"ng", "-", 1}, // "negative" + {"ad", "&", 1}, // "address-of" + {"de", "*", 1}, // "dereference" + {"co", "~", 1}, + + {"pl", "+", 2}, + {"mi", "-", 2}, + {"ml", "*", 2}, + {"dv", "/", 2}, + {"rm", "%", 2}, + {"an", "&", 2}, + {"or", "|", 2}, + {"eo", "^", 2}, + {"aS", "=", 2}, + {"pL", "+=", 2}, + {"mI", "-=", 2}, + {"mL", "*=", 2}, + {"dV", "/=", 2}, + {"rM", "%=", 2}, + {"aN", "&=", 2}, + {"oR", "|=", 2}, + {"eO", "^=", 2}, + {"ls", "<<", 2}, + {"rs", ">>", 2}, + {"lS", "<<=", 2}, + {"rS", ">>=", 2}, + {"eq", "==", 2}, + {"ne", "!=", 2}, + {"lt", "<", 2}, + {"gt", ">", 2}, + {"le", "<=", 2}, + {"ge", ">=", 2}, + {"nt", "!", 1}, + {"aa", "&&", 2}, + {"oo", "||", 2}, + {"pp", "++", 1}, + {"mm", "--", 1}, + {"cm", ",", 2}, + {"pm", "->*", 2}, + {"pt", "->", 0}, // Special syntax + {"cl", "()", 0}, // Special syntax + {"ix", "[]", 2}, + {"qu", "?", 3}, + {"st", "sizeof", 0}, // Special syntax + {"sz", "sizeof", 1}, // Not a real operator name, but used in expressions. + {nullptr, nullptr, 0}, }; // List of builtin types from Itanium C++ ABI. +// +// Invariant: only one- or two-character type abbreviations here. static const AbbrevPair kBuiltinTypeList[] = { - { "v", "void" }, - { "w", "wchar_t" }, - { "b", "bool" }, - { "c", "char" }, - { "a", "signed char" }, - { "h", "unsigned char" }, - { "s", "short" }, - { "t", "unsigned short" }, - { "i", "int" }, - { "j", "unsigned int" }, - { "l", "long" }, - { "m", "unsigned long" }, - { "x", "long long" }, - { "y", "unsigned long long" }, - { "n", "__int128" }, - { "o", "unsigned __int128" }, - { "f", "float" }, - { "d", "double" }, - { "e", "long double" }, - { "g", "__float128" }, - { "z", "ellipsis" }, - { NULL, NULL } + {"v", "void", 0}, + {"w", "wchar_t", 0}, + {"b", "bool", 0}, + {"c", "char", 0}, + {"a", "signed char", 0}, + {"h", "unsigned char", 0}, + {"s", "short", 0}, + {"t", "unsigned short", 0}, + {"i", "int", 0}, + {"j", "unsigned int", 0}, + {"l", "long", 0}, + {"m", "unsigned long", 0}, + {"x", "long long", 0}, + {"y", "unsigned long long", 0}, + {"n", "__int128", 0}, + {"o", "unsigned __int128", 0}, + {"f", "float", 0}, + {"d", "double", 0}, + {"e", "long double", 0}, + {"g", "__float128", 0}, + {"z", "ellipsis", 0}, + + {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) + {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) + {"Dc", "decltype(auto)", 0}, + {"Da", "auto", 0}, + {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) + {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits) + {"Di", "char32_t", 0}, + {"Du", "char8_t", 0}, + {"Ds", "char16_t", 0}, + {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) + {nullptr, nullptr, 0}, }; // List of substitutions Itanium C++ ABI. static const AbbrevPair kSubstitutionList[] = { - { "St", "" }, - { "Sa", "allocator" }, - { "Sb", "basic_string" }, - // std::basic_string,std::allocator > - { "Ss", "string"}, - // std::basic_istream > - { "Si", "istream" }, - // std::basic_ostream > - { "So", "ostream" }, - // std::basic_iostream > - { "Sd", "iostream" }, - { NULL, NULL } + {"St", "", 0}, + {"Sa", "allocator", 0}, + {"Sb", "basic_string", 0}, + // std::basic_string,std::allocator > + {"Ss", "string", 0}, + // std::basic_istream > + {"Si", "istream", 0}, + // std::basic_ostream > + {"So", "ostream", 0}, + // std::basic_iostream > + {"Sd", "iostream", 0}, + {nullptr, nullptr, 0}, }; -// State needed for demangling. +// State needed for demangling. This struct is copied in almost every stack +// frame, so every byte counts. +typedef struct { + int mangled_idx; // Cursor of mangled name. + int out_cur_idx; // Cursor of output string. + int prev_name_idx; // For constructors/destructors. + unsigned int prev_name_length : 16; // For constructors/destructors. + signed int nest_level : 15; // For nested names. + unsigned int append : 1; // Append flag. + // Note: for some reason MSVC can't pack "bool append : 1" into the same int + // with the above two fields, so we use an int instead. Amusingly it can pack + // "signed bool" as expected, but relying on that to continue to be a legal + // type seems ill-advised (as it's illegal in at least clang). +} ParseState; + +static_assert(sizeof(ParseState) == 4 * sizeof(int), + "unexpected size of ParseState"); + +// One-off state for demangling that's not subject to backtracking -- either +// constant data, data that's intentionally immune to backtracking (steps), or +// data that would never be changed by backtracking anyway (recursion_depth). +// +// Only one copy of this exists for each call to Demangle, so the size of this +// struct is nearly inconsequential. typedef struct { - const char *mangled_cur; // Cursor of mangled name. - char *out_cur; // Cursor of output string. - const char *out_begin; // Beginning of output string. - const char *out_end; // End of output string. - const char *prev_name; // For constructors/destructors. - ssize_t prev_name_length; // For constructors/destructors. - short nest_level; // For nested names. - bool append; // Append flag. - bool overflowed; // True if output gets overflowed. + const char *mangled_begin; // Beginning of input string. + char *out; // Beginning of output string. + int out_end_idx; // One past last allowed output character. + int recursion_depth; // For stack exhaustion prevention. + int steps; // Cap how much work we'll do, regardless of depth. + ParseState parse_state; // Backtrackable state copied for most frames. } State; +namespace { +// Prevent deep recursion / stack exhaustion. +// Also prevent unbounded handling of complex inputs. +class ComplexityGuard { + public: + explicit ComplexityGuard(State *state) : state_(state) { + ++state->recursion_depth; + ++state->steps; + } + ~ComplexityGuard() { --state_->recursion_depth; } + + // 256 levels of recursion seems like a reasonable upper limit on depth. + // 128 is not enough to demagle synthetic tests from demangle_unittest.txt: + // "_ZaaZZZZ..." and "_ZaaZcvZcvZ..." + static constexpr int kRecursionDepthLimit = 256; + + // We're trying to pick a charitable upper-limit on how many parse steps are + // necessary to handle something that a human could actually make use of. + // This is mostly in place as a bound on how much work we'll do if we are + // asked to demangle an mangled name from an untrusted source, so it should be + // much larger than the largest expected symbol, but much smaller than the + // amount of work we can do in, e.g., a second. + // + // Some real-world symbols from an arbitrary binary started failing between + // 2^12 and 2^13, so we multiply the latter by an extra factor of 16 to set + // the limit. + // + // Spending one second on 2^17 parse steps would require each step to take + // 7.6us, or ~30000 clock cycles, so it's safe to say this can be done in + // under a second. + static constexpr int kParseStepsLimit = 1 << 17; + + bool IsTooComplex() const { + return state_->recursion_depth > kRecursionDepthLimit || + state_->steps > kParseStepsLimit; + } + + private: + State *state_; +}; +} // namespace + // We don't use strlen() in libc since it's not guaranteed to be async // signal safe. static size_t StrLen(const char *str) { @@ -172,8 +255,8 @@ static size_t StrLen(const char *str) { } // Returns true if "str" has at least "n" characters remaining. -static bool AtLeastNumCharsRemaining(const char *str, ssize_t n) { - for (ssize_t i = 0; i < n; ++i) { +static bool AtLeastNumCharsRemaining(const char *str, size_t n) { + for (size_t i = 0; i < n; ++i) { if (str[i] == '\0') { return false; } @@ -184,32 +267,42 @@ static bool AtLeastNumCharsRemaining(const char *str, ssize_t n) { // Returns true if "str" has "prefix" as a prefix. static bool StrPrefix(const char *str, const char *prefix) { size_t i = 0; - while (str[i] != '\0' && prefix[i] != '\0' && - str[i] == prefix[i]) { + while (str[i] != '\0' && prefix[i] != '\0' && str[i] == prefix[i]) { ++i; } return prefix[i] == '\0'; // Consumed everything in "prefix". } -static void InitState(State *state, const char *mangled, - char *out, size_t out_size) { - state->mangled_cur = mangled; - state->out_cur = out; - state->out_begin = out; - state->out_end = out + out_size; - state->prev_name = NULL; - state->prev_name_length = -1; - state->nest_level = -1; - state->append = true; - state->overflowed = false; +static void InitState(State* state, + const char* mangled, + char* out, + size_t out_size) { + state->mangled_begin = mangled; + state->out = out; + state->out_end_idx = static_cast(out_size); + state->recursion_depth = 0; + state->steps = 0; + + state->parse_state.mangled_idx = 0; + state->parse_state.out_cur_idx = 0; + state->parse_state.prev_name_idx = 0; + state->parse_state.prev_name_length = 0; + state->parse_state.nest_level = -1; + state->parse_state.append = true; +} + +static inline const char *RemainingInput(State *state) { + return &state->mangled_begin[state->parse_state.mangled_idx]; } -// Returns true and advances "mangled_cur" if we find "one_char_token" -// at "mangled_cur" position. It is assumed that "one_char_token" does +// Returns true and advances "mangled_idx" if we find "one_char_token" +// at "mangled_idx" position. It is assumed that "one_char_token" does // not contain '\0'. static bool ParseOneCharToken(State *state, const char one_char_token) { - if (state->mangled_cur[0] == one_char_token) { - ++state->mangled_cur; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == one_char_token) { + ++state->parse_state.mangled_idx; return true; } return false; @@ -219,9 +312,11 @@ static bool ParseOneCharToken(State *state, const char one_char_token) { // at "mangled_cur" position. It is assumed that "two_char_token" does // not contain '\0'. static bool ParseTwoCharToken(State *state, const char *two_char_token) { - if (state->mangled_cur[0] == two_char_token[0] && - state->mangled_cur[1] == two_char_token[1]) { - state->mangled_cur += 2; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == two_char_token[0] && + RemainingInput(state)[1] == two_char_token[1]) { + state->parse_state.mangled_idx += 2; return true; } return false; @@ -230,21 +325,35 @@ static bool ParseTwoCharToken(State *state, const char *two_char_token) { // Returns true and advances "mangled_cur" if we find any character in // "char_class" at "mangled_cur" position. static bool ParseCharClass(State *state, const char *char_class) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == '\0') { + return false; + } const char *p = char_class; for (; *p != '\0'; ++p) { - if (state->mangled_cur[0] == *p) { - ++state->mangled_cur; + if (RemainingInput(state)[0] == *p) { + ++state->parse_state.mangled_idx; return true; } } return false; } -// This function is used for handling an optional non-terminal. -static bool Optional(bool) { - return true; +static bool ParseDigit(State *state, int *digit) { + char c = RemainingInput(state)[0]; + if (ParseCharClass(state, "0123456789")) { + if (digit != nullptr) { + *digit = c - '0'; + } + return true; + } + return false; } +// This function is used for handling an optional non-terminal. +static bool Optional(bool /*status*/) { return true; } + // This function is used for handling + syntax. typedef bool (*ParseFunc)(State *); static bool OneOrMore(ParseFunc parse_func, State *state) { @@ -266,146 +375,179 @@ static bool ZeroOrMore(ParseFunc parse_func, State *state) { return true; } -// Append "str" at "out_cur". If there is an overflow, "overflowed" -// is set to true for later use. The output string is ensured to +// Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is +// set to out_end_idx+1. The output string is ensured to // always terminate with '\0' as long as there is no overflow. -static void Append(State *state, const char * const str, ssize_t length) { - for (ssize_t i = 0; i < length; ++i) { - if (state->out_cur + 1 < state->out_end) { // +1 for '\0' - *state->out_cur = str[i]; - ++state->out_cur; +static void Append(State *state, const char *const str, const size_t length) { + for (size_t i = 0; i < length; ++i) { + if (state->parse_state.out_cur_idx + 1 < + state->out_end_idx) { // +1 for '\0' + state->out[state->parse_state.out_cur_idx++] = str[i]; } else { - state->overflowed = true; + // signal overflow + state->parse_state.out_cur_idx = state->out_end_idx + 1; break; } } - if (!state->overflowed) { - *state->out_cur = '\0'; // Terminate it with '\0' + if (state->parse_state.out_cur_idx < state->out_end_idx) { + state->out[state->parse_state.out_cur_idx] = + '\0'; // Terminate it with '\0' } } // We don't use equivalents in libc to avoid locale issues. -static bool IsLower(char c) { - return c >= 'a' && c <= 'z'; -} +static bool IsLower(char c) { return c >= 'a' && c <= 'z'; } static bool IsAlpha(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } -static bool IsDigit(char c) { - return c >= '0' && c <= '9'; -} +static bool IsDigit(char c) { return c >= '0' && c <= '9'; } // Returns true if "str" is a function clone suffix. These suffixes are used -// by GCC 4.5.x and later versions to indicate functions which have been -// cloned during optimization. We treat any sequence (.+.+)+ as -// a function clone suffix. +// by GCC 4.5.x and later versions (and our locally-modified version of GCC +// 4.4.x) to indicate functions which have been cloned during optimization. +// We treat any sequence (.+.+)+ as a function clone suffix. +// Additionally, '_' is allowed along with the alphanumeric sequence. static bool IsFunctionCloneSuffix(const char *str) { size_t i = 0; while (str[i] != '\0') { - // Consume a single .+.+ sequence. - if (str[i] != '.' || !IsAlpha(str[i + 1])) { - return false; + bool parsed = false; + // Consume a single [. | _]*[.]* sequence. + if (str[i] == '.' && (IsAlpha(str[i + 1]) || str[i + 1] == '_')) { + parsed = true; + i += 2; + while (IsAlpha(str[i]) || str[i] == '_') { + ++i; + } } - i += 2; - while (IsAlpha(str[i])) { - ++i; + if (str[i] == '.' && IsDigit(str[i + 1])) { + parsed = true; + i += 2; + while (IsDigit(str[i])) { + ++i; + } } - if (str[i] != '.' || !IsDigit(str[i + 1])) { + if (!parsed) return false; - } - i += 2; - while (IsDigit(str[i])) { - ++i; - } } return true; // Consumed everything in "str". } +static bool EndsWith(State *state, const char chr) { + return state->parse_state.out_cur_idx > 0 && + state->parse_state.out_cur_idx < state->out_end_idx && + chr == state->out[state->parse_state.out_cur_idx - 1]; +} + // Append "str" with some tweaks, iff "append" state is true. -// Returns true so that it can be placed in "if" conditions. -static void MaybeAppendWithLength(State *state, const char * const str, - ssize_t length) { - if (state->append && length > 0) { +static void MaybeAppendWithLength(State *state, const char *const str, + const size_t length) { + if (state->parse_state.append && length > 0) { // Append a space if the output buffer ends with '<' and "str" // starts with '<' to avoid <<<. - if (str[0] == '<' && state->out_begin < state->out_cur && - state->out_cur[-1] == '<') { + if (str[0] == '<' && EndsWith(state, '<')) { Append(state, " ", 1); } - // Remember the last identifier name for ctors/dtors. - if (IsAlpha(str[0]) || str[0] == '_') { - state->prev_name = state->out_cur; - state->prev_name_length = length; + // Remember the last identifier name for ctors/dtors, + // but only if we haven't yet overflown the buffer. + if (state->parse_state.out_cur_idx < state->out_end_idx && + (IsAlpha(str[0]) || str[0] == '_')) { + state->parse_state.prev_name_idx = state->parse_state.out_cur_idx; + state->parse_state.prev_name_length = static_cast(length); } Append(state, str, length); } } -// A convenient wrapper arount MaybeAppendWithLength(). -static bool MaybeAppend(State *state, const char * const str) { - if (state->append) { +// Appends a positive decimal number to the output if appending is enabled. +static bool MaybeAppendDecimal(State *state, int val) { + // Max {32-64}-bit unsigned int is 20 digits. + constexpr size_t kMaxLength = 20; + char buf[kMaxLength]; + + // We can't use itoa or sprintf as neither is specified to be + // async-signal-safe. + if (state->parse_state.append) { + // We can't have a one-before-the-beginning pointer, so instead start with + // one-past-the-end and manipulate one character before the pointer. + char *p = &buf[kMaxLength]; + do { // val=0 is the only input that should write a leading zero digit. + *--p = static_cast((val % 10) + '0'); + val /= 10; + } while (p > buf && val != 0); + + // 'p' landed on the last character we set. How convenient. + Append(state, p, kMaxLength - static_cast(p - buf)); + } + + return true; +} + +// A convenient wrapper around MaybeAppendWithLength(). +// Returns true so that it can be placed in "if" conditions. +static bool MaybeAppend(State *state, const char *const str) { + if (state->parse_state.append) { size_t length = StrLen(str); - MaybeAppendWithLength(state, str, static_cast(length)); + MaybeAppendWithLength(state, str, length); } return true; } // This function is used for handling nested names. static bool EnterNestedName(State *state) { - state->nest_level = 0; + state->parse_state.nest_level = 0; return true; } // This function is used for handling nested names. -static bool LeaveNestedName(State *state, short prev_value) { - state->nest_level = prev_value; +static bool LeaveNestedName(State *state, int16_t prev_value) { + state->parse_state.nest_level = prev_value; return true; } // Disable the append mode not to print function parameters, etc. static bool DisableAppend(State *state) { - state->append = false; + state->parse_state.append = false; return true; } // Restore the append mode to the previous state. static bool RestoreAppend(State *state, bool prev_value) { - state->append = prev_value; + state->parse_state.append = prev_value; return true; } // Increase the nest level for nested names. static void MaybeIncreaseNestLevel(State *state) { - if (state->nest_level > -1) { - ++state->nest_level; + if (state->parse_state.nest_level > -1) { + ++state->parse_state.nest_level; } } // Appends :: for nested names if necessary. static void MaybeAppendSeparator(State *state) { - if (state->nest_level >= 1) { + if (state->parse_state.nest_level >= 1) { MaybeAppend(state, "::"); } } // Cancel the last separator if necessary. static void MaybeCancelLastSeparator(State *state) { - if (state->nest_level >= 1 && state->append && - state->out_begin <= state->out_cur - 2) { - state->out_cur -= 2; - *state->out_cur = '\0'; + if (state->parse_state.nest_level >= 1 && state->parse_state.append && + state->parse_state.out_cur_idx >= 2) { + state->parse_state.out_cur_idx -= 2; + state->out[state->parse_state.out_cur_idx] = '\0'; } } // Returns true if the identifier of the given length pointed to by // "mangled_cur" is anonymous namespace. -static bool IdentifierIsAnonymousNamespace(State *state, ssize_t length) { +static bool IdentifierIsAnonymousNamespace(State *state, size_t length) { + // Returns true if "anon_prefix" is a proper prefix of "mangled_cur". static const char anon_prefix[] = "_GLOBAL__N_"; - return (length > static_cast(sizeof(anon_prefix)) - - 1 && // Should be longer. - StrPrefix(state->mangled_cur, anon_prefix)); + return (length > (sizeof(anon_prefix) - 1) && + StrPrefix(RemainingInput(state), anon_prefix)); } // Forward declarations of our parsing functions. @@ -413,24 +555,24 @@ static bool ParseMangledName(State *state); static bool ParseEncoding(State *state); static bool ParseName(State *state); static bool ParseUnscopedName(State *state); -static bool ParseUnscopedTemplateName(State *state); static bool ParseNestedName(State *state); static bool ParsePrefix(State *state); static bool ParseUnqualifiedName(State *state); static bool ParseSourceName(State *state); static bool ParseLocalSourceName(State *state); +static bool ParseUnnamedTypeName(State *state); static bool ParseNumber(State *state, int *number_out); static bool ParseFloatNumber(State *state); static bool ParseSeqId(State *state); -static bool ParseIdentifier(State *state, ssize_t length); -static bool ParseAbiTags(State *state); -static bool ParseAbiTag(State *state); -static bool ParseOperatorName(State *state); +static bool ParseIdentifier(State *state, size_t length); +static bool ParseOperatorName(State *state, int *arity); static bool ParseSpecialName(State *state); static bool ParseCallOffset(State *state); static bool ParseNVOffset(State *state); static bool ParseVOffset(State *state); +static bool ParseAbiTags(State *state); static bool ParseCtorDtorName(State *state); +static bool ParseDecltype(State *state); static bool ParseType(State *state); static bool ParseCVQualifiers(State *state); static bool ParseBuiltinType(State *state); @@ -443,11 +585,15 @@ static bool ParseTemplateParam(State *state); static bool ParseTemplateTemplateParam(State *state); static bool ParseTemplateArgs(State *state); static bool ParseTemplateArg(State *state); +static bool ParseBaseUnresolvedName(State *state); +static bool ParseUnresolvedName(State *state); static bool ParseExpression(State *state); static bool ParseExprPrimary(State *state); +static bool ParseExprCastValue(State *state); static bool ParseLocalName(State *state); +static bool ParseLocalNameSuffix(State *state); static bool ParseDiscriminator(State *state); -static bool ParseSubstitution(State *state); +static bool ParseSubstitution(State *state, bool accept_std); // Implementation note: the following code is a straightforward // translation of the Itanium C++ ABI defined in BNF with a couple of @@ -459,11 +605,12 @@ static bool ParseSubstitution(State *state); // - Reorder patterns to give greedier functions precedence // We'll mark "Less greedy than" for these cases in the code // -// Each parsing function changes the state and returns true on -// success. Otherwise, don't change the state and returns false. To -// ensure that the state isn't changed in the latter case, we save the -// original state before we call more than one parsing functions -// consecutively with &&, and restore the state if unsuccessful. See +// Each parsing function changes the parse state and returns true on +// success, or returns false and doesn't change the parse state (note: +// the parse-steps counter increases regardless of success or failure). +// To ensure that the parse state isn't changed in the latter case, we +// save the original state before we call multiple parsing functions +// consecutively with &&, and restore it if unsuccessful. See // ParseEncoding() as an example of this convention. We follow the // convention throughout the code. // @@ -477,10 +624,12 @@ static bool ParseSubstitution(State *state); // // Reference: // - Itanium C++ ABI -// +// // ::= _Z static bool ParseMangledName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); } @@ -488,13 +637,18 @@ static bool ParseMangledName(State *state) { // ::= <(data) name> // ::= static bool ParseEncoding(State *state) { - State copy = *state; - if (ParseName(state) && ParseBareFunctionType(state)) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + // Implementing the first two productions together as + // [] avoids exponential blowup of backtracking. + // + // Since Optional(...) can't fail, there's no need to copy the state for + // backtracking. + if (ParseName(state) && Optional(ParseBareFunctionType(state))) { return true; } - *state = copy; - if (ParseName(state) || ParseSpecialName(state)) { + if (ParseSpecialName(state)) { return true; } return false; @@ -505,60 +659,73 @@ static bool ParseEncoding(State *state) { // ::= // ::= static bool ParseName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; if (ParseNestedName(state) || ParseLocalName(state)) { return true; } - State copy = *state; - if (ParseUnscopedTemplateName(state) && + // We reorganize the productions to avoid re-parsing unscoped names. + // - Inline productions: + // ::= + // ::= + // ::= + // - Merge the two productions that start with unscoped-name: + // ::= [] + + ParseState copy = state->parse_state; + // "std<...>" isn't a valid name. + if (ParseSubstitution(state, /*accept_std=*/false) && ParseTemplateArgs(state)) { return true; } - *state = copy; + state->parse_state = copy; - // Less greedy than . - if (ParseUnscopedName(state)) { - return true; - } - return false; + // Note there's no need to restore state after this since only the first + // subparser can fail. + return ParseUnscopedName(state) && Optional(ParseTemplateArgs(state)); } // ::= // ::= St static bool ParseUnscopedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; if (ParseUnqualifiedName(state)) { return true; } - State copy = *state; - if (ParseTwoCharToken(state, "St") && - MaybeAppend(state, "std::") && + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "St") && MaybeAppend(state, "std::") && ParseUnqualifiedName(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } -// ::= -// ::= -static bool ParseUnscopedTemplateName(State *state) { - return ParseUnscopedName(state) || ParseSubstitution(state); +// ::= R // lvalue method reference qualifier +// ::= O // rvalue method reference qualifier +static inline bool ParseRefQualifier(State *state) { + return ParseCharClass(state, "OR"); } -// ::= N [] E -// ::= N [] E +// ::= N [] [] +// E +// ::= N [] [] +// E static bool ParseNestedName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'N') && - EnterNestedName(state) && + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'N') && EnterNestedName(state) && Optional(ParseCVQualifiers(state)) && - ParsePrefix(state) && + Optional(ParseRefQualifier(state)) && ParsePrefix(state) && LeaveNestedName(state, copy.nest_level) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; return false; } @@ -574,12 +741,15 @@ static bool ParseNestedName(State *state) { // ::= // ::= static bool ParsePrefix(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; bool has_something = false; while (true) { MaybeAppendSeparator(state); if (ParseTemplateParam(state) || - ParseSubstitution(state) || - ParseUnscopedName(state)) { + ParseSubstitution(state, /*accept_std=*/true) || + ParseUnscopedName(state) || + (ParseOneCharToken(state, 'M') && ParseUnnamedTypeName(state))) { has_something = true; MaybeIncreaseNestLevel(state); continue; @@ -594,40 +764,112 @@ static bool ParsePrefix(State *state) { return true; } -// ::= -// ::= +// ::= [] +// ::= [] // ::= [] // ::= [] +// ::= [] +// +// is a GCC extension; see below. static bool ParseUnqualifiedName(State *state) { - return (ParseOperatorName(state) || - ParseCtorDtorName(state) || - (ParseSourceName(state) && Optional(ParseAbiTags(state))) || - (ParseLocalSourceName(state) && Optional(ParseAbiTags(state)))); + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || + ParseSourceName(state) || ParseLocalSourceName(state) || + ParseUnnamedTypeName(state)) { + return ParseAbiTags(state); + } + return false; +} + +// ::= [] +// ::= B +static bool ParseAbiTags(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + while (ParseOneCharToken(state, 'B')) { + ParseState copy = state->parse_state; + MaybeAppend(state, "[abi:"); + + if (!ParseSourceName(state)) { + state->parse_state = copy; + return false; + } + MaybeAppend(state, "]"); + } + + return true; } // ::= static bool ParseSourceName(State *state) { - State copy = *state; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; int length = -1; - if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { + if (ParseNumber(state, &length) && + ParseIdentifier(state, static_cast(length))) { return true; } - *state = copy; + state->parse_state = copy; return false; } // ::= L [] // // References: -// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 -// http://gcc.gnu.org/viewcvs?view=rev&revision=124467 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 +// https://gcc.gnu.org/viewcvs?view=rev&revision=124467 static bool ParseLocalSourceName(State *state) { - State copy = *state; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && Optional(ParseDiscriminator(state))) { return true; } - *state = copy; + state->parse_state = copy; + return false; +} + +// ::= Ut [<(nonnegative) number>] _ +// ::= +// ::= Ul E [<(nonnegative) number>] _ +// ::= <(parameter) type>+ +static bool ParseUnnamedTypeName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + // Type's 1-based index n is encoded as { "", n == 1; itoa(n-2), otherwise }. + // Optionally parse the encoded value into 'which' and add 2 to get the index. + int which = -1; + + // Unnamed type local to function or class. + if (ParseTwoCharToken(state, "Ut") && Optional(ParseNumber(state, &which)) && + which <= std::numeric_limits::max() - 2 && // Don't overflow. + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "{unnamed type#"); + MaybeAppendDecimal(state, 2 + which); + MaybeAppend(state, "}"); + return true; + } + state->parse_state = copy; + + // Closure type. + which = -1; + if (ParseTwoCharToken(state, "Ul") && DisableAppend(state) && + OneOrMore(ParseType, state) && RestoreAppend(state, copy.append) && + ParseOneCharToken(state, 'E') && Optional(ParseNumber(state, &which)) && + which <= std::numeric_limits::max() - 2 && // Don't overflow. + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "{lambda()#"); + MaybeAppendDecimal(state, 2 + which); + MaybeAppend(state, "}"); + return true; + } + state->parse_state = copy; + return false; } @@ -635,23 +877,32 @@ static bool ParseLocalSourceName(State *state) { // If "number_out" is non-null, then *number_out is set to the value of the // parsed number on success. static bool ParseNumber(State *state, int *number_out) { - int sign = 1; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + bool negative = false; if (ParseOneCharToken(state, 'n')) { - sign = -1; + negative = true; } - const char *p = state->mangled_cur; - int number = 0; - for (;*p != '\0'; ++p) { + const char *p = RemainingInput(state); + uint64_t number = 0; + for (; *p != '\0'; ++p) { if (IsDigit(*p)) { - number = number * 10 + (*p - '0'); + number = number * 10 + static_cast(*p - '0'); } else { break; } } - if (p != state->mangled_cur) { // Conversion succeeded. - state->mangled_cur = p; - if (number_out != NULL) { - *number_out = number * sign; + // Apply the sign with uint64_t arithmetic so overflows aren't UB. Gives + // "incorrect" results for out-of-range inputs, but negative values only + // appear for literals, which aren't printed. + if (negative) { + number = ~number + 1; + } + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); + if (number_out != nullptr) { + // Note: possibly truncate "number". + *number_out = static_cast(number); } return true; } @@ -661,14 +912,16 @@ static bool ParseNumber(State *state, int *number_out) { // Floating-point literals are encoded using a fixed-length lowercase // hexadecimal string. static bool ParseFloatNumber(State *state) { - const char *p = state->mangled_cur; - for (;*p != '\0'; ++p) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + const char *p = RemainingInput(state); + for (; *p != '\0'; ++p) { if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { break; } } - if (p != state->mangled_cur) { // Conversion succeeded. - state->mangled_cur = p; + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); return true; } return false; @@ -677,93 +930,85 @@ static bool ParseFloatNumber(State *state) { // The is a sequence number in base 36, // using digits and upper case letters static bool ParseSeqId(State *state) { - const char *p = state->mangled_cur; - for (;*p != '\0'; ++p) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + const char *p = RemainingInput(state); + for (; *p != '\0'; ++p) { if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { break; } } - if (p != state->mangled_cur) { // Conversion succeeded. - state->mangled_cur = p; + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); return true; } return false; } // ::= (of given length) -static bool ParseIdentifier(State *state, ssize_t length) { - if (length == -1 || - !AtLeastNumCharsRemaining(state->mangled_cur, length)) { +static bool ParseIdentifier(State *state, size_t length) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (!AtLeastNumCharsRemaining(RemainingInput(state), length)) { return false; } if (IdentifierIsAnonymousNamespace(state, length)) { MaybeAppend(state, "(anonymous namespace)"); } else { - MaybeAppendWithLength(state, state->mangled_cur, length); + MaybeAppendWithLength(state, RemainingInput(state), length); } - state->mangled_cur += length; + state->parse_state.mangled_idx += length; return true; } -// ::= [] -static bool ParseAbiTags(State *state) { - State copy = *state; - DisableAppend(state); - if (OneOrMore(ParseAbiTag, state)) { - RestoreAppend(state, copy.append); - return true; - } - *state = copy; - return false; -} - -// ::= B -static bool ParseAbiTag(State *state) { - return ParseOneCharToken(state, 'B') && ParseSourceName(state); -} - // ::= nw, and other two letters cases // ::= cv # (cast) // ::= v # vendor extended operator -static bool ParseOperatorName(State *state) { - if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) { +static bool ParseOperatorName(State *state, int *arity) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (!AtLeastNumCharsRemaining(RemainingInput(state), 2)) { return false; } // First check with "cv" (cast) case. - State copy = *state; - if (ParseTwoCharToken(state, "cv") && - MaybeAppend(state, "operator ") && - EnterNestedName(state) && - ParseType(state) && + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") && + EnterNestedName(state) && ParseType(state) && LeaveNestedName(state, copy.nest_level)) { + if (arity != nullptr) { + *arity = 1; + } return true; } - *state = copy; + state->parse_state = copy; // Then vendor extended operators. - if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") && + if (ParseOneCharToken(state, 'v') && ParseDigit(state, arity) && ParseSourceName(state)) { return true; } - *state = copy; + state->parse_state = copy; // Other operator names should start with a lower alphabet followed // by a lower/upper alphabet. - if (!(IsLower(state->mangled_cur[0]) && - IsAlpha(state->mangled_cur[1]))) { + if (!(IsLower(RemainingInput(state)[0]) && + IsAlpha(RemainingInput(state)[1]))) { return false; } // We may want to perform a binary search if we really need speed. const AbbrevPair *p; - for (p = kOperatorList; p->abbrev != NULL; ++p) { - if (state->mangled_cur[0] == p->abbrev[0] && - state->mangled_cur[1] == p->abbrev[1]) { + for (p = kOperatorList; p->abbrev != nullptr; ++p) { + if (RemainingInput(state)[0] == p->abbrev[0] && + RemainingInput(state)[1] == p->abbrev[1]) { + if (arity != nullptr) { + *arity = p->arity; + } MaybeAppend(state, "operator"); if (IsLower(*p->real_name)) { // new, delete, etc. MaybeAppend(state, " "); } MaybeAppend(state, p->real_name); - state->mangled_cur += 2; + state->parse_state.mangled_idx += 2; return true; } } @@ -774,6 +1019,7 @@ static bool ParseOperatorName(State *state) { // ::= TT // ::= TI // ::= TS +// ::= TH # thread-local // ::= Tc <(base) encoding> // ::= GV <(object) name> // ::= T <(base) encoding> @@ -789,123 +1035,156 @@ static bool ParseOperatorName(State *state) { // Note: we don't care much about them since they don't appear in // stack traces. The are special data. static bool ParseSpecialName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'T') && - ParseCharClass(state, "VTIS") && + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) && ParseCallOffset(state) && ParseEncoding(state)) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseTwoCharToken(state, "GV") && - ParseName(state)) { + if (ParseTwoCharToken(state, "GV") && ParseName(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) && ParseEncoding(state)) { return true; } - *state = copy; + state->parse_state = copy; // G++ extensions if (ParseTwoCharToken(state, "TC") && ParseType(state) && - ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && - DisableAppend(state) && - ParseType(state)) { + ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && + DisableAppend(state) && ParseType(state)) { RestoreAppend(state, copy.append); return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseTwoCharToken(state, "GR") && ParseName(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") && ParseCallOffset(state) && ParseEncoding(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } // ::= h _ // ::= v _ static bool ParseCallOffset(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'h') && - ParseNVOffset(state) && ParseOneCharToken(state, '_')) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'h') && ParseNVOffset(state) && + ParseOneCharToken(state, '_')) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseOneCharToken(state, 'v') && - ParseVOffset(state) && ParseOneCharToken(state, '_')) { + if (ParseOneCharToken(state, 'v') && ParseVOffset(state) && + ParseOneCharToken(state, '_')) { return true; } - *state = copy; + state->parse_state = copy; return false; } // ::= <(offset) number> static bool ParseNVOffset(State *state) { - return ParseNumber(state, NULL); + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return ParseNumber(state, nullptr); } // ::= <(offset) number> _ <(virtual offset) number> static bool ParseVOffset(State *state) { - State copy = *state; - if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && - ParseNumber(state, NULL)) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && + ParseNumber(state, nullptr)) { return true; } - *state = copy; + state->parse_state = copy; return false; } -// ::= C1 | C2 | C3 +// ::= C1 | C2 | C3 | CI1 | CI2 +// // ::= D0 | D1 | D2 +// # GCC extensions: "unified" constructor/destructor. See +// # +// https://github.com/gcc-mirror/gcc/blob/7ad17b583c3643bd4557f29b8391ca7ef08391f5/gcc/cp/mangle.c#L1847 +// ::= C4 | D4 static bool ParseCtorDtorName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'C') && - ParseCharClass(state, "123")) { - const char * const prev_name = state->prev_name; - const ssize_t prev_name_length = state->prev_name_length; - MaybeAppendWithLength(state, prev_name, prev_name_length); - return true; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'C')) { + if (ParseCharClass(state, "1234")) { + const char *const prev_name = + state->out + state->parse_state.prev_name_idx; + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); + return true; + } else if (ParseOneCharToken(state, 'I') && ParseCharClass(state, "12") && + ParseClassEnumType(state)) { + return true; + } } - *state = copy; + state->parse_state = copy; - if (ParseOneCharToken(state, 'D') && - ParseCharClass(state, "012")) { - const char * const prev_name = state->prev_name; - const ssize_t prev_name_length = state->prev_name_length; + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "0124")) { + const char *const prev_name = state->out + state->parse_state.prev_name_idx; MaybeAppend(state, "~"); - MaybeAppendWithLength(state, prev_name, prev_name_length); + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); return true; } - *state = copy; + state->parse_state = copy; + return false; +} + +// ::= Dt E # decltype of an id-expression or class +// # member access (C++0x) +// ::= DT E # decltype of an expression (C++0x) +static bool ParseDecltype(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && + ParseExpression(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return false; } @@ -918,67 +1197,87 @@ static bool ParseCtorDtorName(State *state) { // ::= U # vendor extended type qualifier // ::= // ::= -// ::= +// ::= # note: just an alias for // ::= // ::= // ::= // ::= +// ::= // ::= // ::= Dp # pack expansion of (C++0x) -// ::= Dt E # decltype of an id-expression or class -// # member access (C++0x) -// ::= DT E # decltype of an expression (C++0x) +// ::= Dv _ # GNU vector extension // static bool ParseType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + // We should check CV-qualifers, and PRGC things first. - State copy = *state; - if (ParseCVQualifiers(state) && ParseType(state)) { - return true; + // + // CV-qualifiers overlap with some operator names, but an operator name is not + // valid as a type. To avoid an ambiguity that can lead to exponential time + // complexity, refuse to backtrack the CV-qualifiers. + // + // _Z4aoeuIrMvvE + // => _Z 4aoeuI rM v v E + // aoeu + // => _Z 4aoeuI r Mv v E + // aoeu + // + // By consuming the CV-qualifiers first, the former parse is disabled. + if (ParseCVQualifiers(state)) { + const bool result = ParseType(state); + if (!result) state->parse_state = copy; + return result; } - *state = copy; + state->parse_state = copy; - if (ParseCharClass(state, "OPRCG") && ParseType(state)) { - return true; + // Similarly, these tag characters can overlap with other s resulting in + // two different parse prefixes that land on in the same + // place, such as "C3r1xI...". So, disable the "ctor-name = C3" parse by + // refusing to backtrack the tag characters. + if (ParseCharClass(state, "OPRCG")) { + const bool result = ParseType(state); + if (!result) state->parse_state = copy; + return result; } - *state = copy; + state->parse_state = copy; if (ParseTwoCharToken(state, "Dp") && ParseType(state)) { return true; } - *state = copy; - - if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && - ParseExpression(state) && ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseBuiltinType(state) || - ParseFunctionType(state) || - ParseClassEnumType(state) || - ParseArrayType(state) || - ParsePointerToMemberType(state) || - ParseSubstitution(state)) { + if (ParseBuiltinType(state) || ParseFunctionType(state) || + ParseClassEnumType(state) || ParseArrayType(state) || + ParsePointerToMemberType(state) || ParseDecltype(state) || + // "std" on its own isn't a type. + ParseSubstitution(state, /*accept_std=*/false)) { return true; } - if (ParseTemplateTemplateParam(state) && - ParseTemplateArgs(state)) { + if (ParseTemplateTemplateParam(state) && ParseTemplateArgs(state)) { return true; } - *state = copy; + state->parse_state = copy; // Less greedy than . if (ParseTemplateParam(state)) { return true; } + if (ParseTwoCharToken(state, "Dv") && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + return false; } @@ -986,6 +1285,8 @@ static bool ParseType(State *state) { // We don't allow empty to avoid infinite loop in // ParseType(). static bool ParseCVQualifiers(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; int num_cv_qualifiers = 0; num_cv_qualifiers += ParseOneCharToken(state, 'r'); num_cv_qualifiers += ParseOneCharToken(state, 'V'); @@ -993,208 +1294,499 @@ static bool ParseCVQualifiers(State *state) { return num_cv_qualifiers > 0; } -// ::= v, etc. +// ::= v, etc. # single-character builtin types // ::= u +// ::= Dd, etc. # two-character builtin types +// +// Not supported: +// ::= DF _ # _FloatN (N bits) +// static bool ParseBuiltinType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; const AbbrevPair *p; - for (p = kBuiltinTypeList; p->abbrev != NULL; ++p) { - if (state->mangled_cur[0] == p->abbrev[0]) { + for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) { + // Guaranteed only 1- or 2-character strings in kBuiltinTypeList. + if (p->abbrev[1] == '\0') { + if (ParseOneCharToken(state, p->abbrev[0])) { + MaybeAppend(state, p->real_name); + return true; + } + } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) { MaybeAppend(state, p->real_name); - ++state->mangled_cur; return true; } } - State copy = *state; + ParseState copy = state->parse_state; if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } -// ::= F [Y] E +// ::= Do # non-throwing +// exception-specification (e.g., +// noexcept, throw()) +// ::= DO E # computed (instantiation-dependent) +// noexcept +// ::= Dw + E # dynamic exception specification +// with instantiation-dependent types +static bool ParseExceptionSpec(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (ParseTwoCharToken(state, "Do")) return true; + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "DO") && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + if (ParseTwoCharToken(state, "Dw") && OneOrMore(ParseType, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= [exception-spec] F [Y] [O] E static bool ParseFunctionType(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'F') && - Optional(ParseOneCharToken(state, 'Y')) && - ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') && + Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) && + Optional(ParseOneCharToken(state, 'O')) && + ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; return false; } // ::= <(signature) type>+ static bool ParseBareFunctionType(State *state) { - State copy = *state; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; DisableAppend(state); if (OneOrMore(ParseType, state)) { RestoreAppend(state, copy.append); MaybeAppend(state, "()"); return true; } - *state = copy; + state->parse_state = copy; return false; } // ::= static bool ParseClassEnumType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; return ParseName(state); } // ::= A <(positive dimension) number> _ <(element) type> // ::= A [<(dimension) expression>] _ <(element) type> static bool ParseArrayType(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) && + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'A') && ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) && ParseOneCharToken(state, '_') && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } // ::= M <(class) type> <(member) type> static bool ParsePointerToMemberType(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'M') && ParseType(state) && - ParseType(state)) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } // ::= T_ // ::= T _ static bool ParseTemplateParam(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; if (ParseTwoCharToken(state, "T_")) { MaybeAppend(state, "?"); // We don't support template substitutions. return true; } - State copy = *state; - if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) && + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'T') && ParseNumber(state, nullptr) && ParseOneCharToken(state, '_')) { MaybeAppend(state, "?"); // We don't support template substitutions. return true; } - *state = copy; + state->parse_state = copy; return false; } - // ::= // ::= static bool ParseTemplateTemplateParam(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; return (ParseTemplateParam(state) || - ParseSubstitution(state)); + // "std" on its own isn't a template. + ParseSubstitution(state, /*accept_std=*/false)); } // ::= I + E static bool ParseTemplateArgs(State *state) { - State copy = *state; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; DisableAppend(state); - if (ParseOneCharToken(state, 'I') && - OneOrMore(ParseTemplateArg, state) && + if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && ParseOneCharToken(state, 'E')) { RestoreAppend(state, copy.append); MaybeAppend(state, "<>"); return true; } - *state = copy; + state->parse_state = copy; return false; } // ::= // ::= -// ::= I * E # argument pack // ::= J * E # argument pack // ::= X E static bool ParseTemplateArg(State *state) { - State copy = *state; - if ((ParseOneCharToken(state, 'I') || ParseOneCharToken(state, 'J')) && - ZeroOrMore(ParseTemplateArg, state) && + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'J') && ZeroOrMore(ParseTemplateArg, state) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; + + // There can be significant overlap between the following leading to + // exponential backtracking: + // + // ::= L E + // e.g. L 2xxIvE 1 E + // ==> + // e.g. L 2xx IvE + // + // This means parsing an entire twice, and can contain + // , so this can generate exponential backtracking. There is + // only overlap when the remaining input starts with "L ", so + // parse all cases that can start this way jointly to share the common prefix. + // + // We have: + // + // ::= + // ::= + // + // First, drop all the productions of that must start with something + // other than 'L'. All that's left is ; inline it. + // + // ::= # starts with 'N' + // ::= + // ::= + // ::= # starts with 'Z' + // + // Drop and inline again: + // + // ::= + // ::= + // ::= # starts with 'S' + // + // Merge the first two, inline , drop last: + // + // ::= [] + // ::= St [] # starts with 'S' + // + // Drop and inline: + // + // ::= [] # starts with lowercase + // ::= [] # starts with 'C' or 'D' + // ::= [] # starts with digit + // ::= [] + // ::= [] # starts with 'U' + // + // One more time: + // + // ::= L [] + // + // Likewise with : + // + // ::= L E + // ::= LZ E # cannot overlap; drop + // ::= L E # cannot overlap; drop + // + // By similar reasoning as shown above, the only s starting with + // are " []". Inline this. + // + // ::= L [] E + // + // Now inline both of these into : + // + // ::= L [] + // ::= L [] E + // + // Merge them and we're done: + // + // ::= L [] [ E] + if (ParseLocalSourceName(state) && Optional(ParseTemplateArgs(state))) { + copy = state->parse_state; + if (ParseExprCastValue(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return true; + } - if (ParseType(state) || - ParseExprPrimary(state)) { + // Now that the overlapping cases can't reach this code, we can safely call + // both of these. + if (ParseType(state) || ParseExprPrimary(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'X') && ParseExpression(state) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; return false; } -// ::= -// ::= -// ::= -// ::= -// ::= -// +// ::= [] +// ::= +// ::= +static inline bool ParseUnresolvedType(State *state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + return (ParseTemplateParam(state) && Optional(ParseTemplateArgs(state))) || + ParseDecltype(state) || ParseSubstitution(state, /*accept_std=*/false); +} + +// ::= [] +static inline bool ParseSimpleId(State *state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + + // Note: cannot be followed by a parameter pack; see comment in + // ParseUnresolvedType. + return ParseSourceName(state) && Optional(ParseTemplateArgs(state)); +} + +// ::= [] +// ::= on [] +// ::= dn +static bool ParseBaseUnresolvedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (ParseSimpleId(state)) { + return true; + } + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "on") && ParseOperatorName(state, nullptr) && + Optional(ParseTemplateArgs(state))) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "dn") && + (ParseUnresolvedType(state) || ParseSimpleId(state))) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= [gs] +// ::= sr +// ::= srN + E +// +// ::= [gs] sr + E +// +static bool ParseUnresolvedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + ParseState copy = state->parse_state; + if (Optional(ParseTwoCharToken(state, "gs")) && + ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseUnresolvedType(state) && + ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseOneCharToken(state, 'N') && + ParseUnresolvedType(state) && + OneOrMore(/* ::= */ ParseSimpleId, state) && + ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (Optional(ParseTwoCharToken(state, "gs")) && + ParseTwoCharToken(state, "sr") && + OneOrMore(/* ::= */ ParseSimpleId, state) && + ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= <1-ary operator-name> +// ::= <2-ary operator-name> +// ::= <3-ary operator-name> +// ::= cl + E +// ::= cp * E # Clang-specific. +// ::= cv # type (expression) +// ::= cv _ * E # type (expr-list) // ::= st +// ::= +// ::= +// ::= +// ::= dt # expr.name +// ::= pt # expr->name +// ::= sp # argument pack expansion // ::= sr // ::= sr +// ::= fp <(top-level) CV-qualifiers> _ +// ::= fp <(top-level) CV-qualifiers> _ +// ::= fL p <(top-level) CV-qualifiers> _ +// ::= fL p <(top-level) CV-qualifiers> _ static bool ParseExpression(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; if (ParseTemplateParam(state) || ParseExprPrimary(state)) { return true; } - State copy = *state; - if (ParseOperatorName(state) && - ParseExpression(state) && - ParseExpression(state) && - ParseExpression(state)) { + ParseState copy = state->parse_state; + + // Object/function call expression. + if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) && + ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseOperatorName(state) && - ParseExpression(state) && - ParseExpression(state)) { + // Clang-specific "cp * E" + // https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338 + if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) && + ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseOperatorName(state) && - ParseExpression(state)) { + // Function-param expression (level 0). + if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { return true; } - *state = copy; + state->parse_state = copy; + // Function-param expression (level 1+). + if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) && + ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + // Parse the conversion expressions jointly to avoid re-parsing the in + // their common prefix. Parsed as: + // ::= cv + // ::= _ * E + // ::= + // + // Also don't try ParseOperatorName after seeing "cv", since ParseOperatorName + // also needs to accept "cv " in other contexts. + if (ParseTwoCharToken(state, "cv")) { + if (ParseType(state)) { + ParseState copy2 = state->parse_state; + if (ParseOneCharToken(state, '_') && ZeroOrMore(ParseExpression, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy2; + if (ParseExpression(state)) { + return true; + } + } + } else { + // Parse unary, binary, and ternary operator expressions jointly, taking + // care not to re-parse subexpressions repeatedly. Parse like: + // ::= + // [] + // ::= [] + int arity = -1; + if (ParseOperatorName(state, &arity) && + arity > 0 && // 0 arity => disabled. + (arity < 3 || ParseExpression(state)) && + (arity < 2 || ParseExpression(state)) && + (arity < 1 || ParseExpression(state))) { + return true; + } + } + state->parse_state = copy; + + // sizeof type if (ParseTwoCharToken(state, "st") && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseTwoCharToken(state, "sr") && ParseType(state) && - ParseUnqualifiedName(state) && - ParseTemplateArgs(state)) { + // Object and pointer member access expressions. + if ((ParseTwoCharToken(state, "dt") || ParseTwoCharToken(state, "pt")) && + ParseExpression(state) && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseTwoCharToken(state, "sr") && ParseType(state) && - ParseUnqualifiedName(state)) { + // Pointer-to-member access expressions. This parses the same as a binary + // operator, but it's implemented separately because "ds" shouldn't be + // accepted in other contexts that parse an operator name. + if (ParseTwoCharToken(state, "ds") && ParseExpression(state) && + ParseExpression(state)) { return true; } - *state = copy; - return false; + state->parse_state = copy; + + // Parameter pack expansion + if (ParseTwoCharToken(state, "sp") && ParseExpression(state)) { + return true; + } + state->parse_state = copy; + + return ParseUnresolvedName(state); } // ::= L <(value) number> E @@ -1202,116 +1794,194 @@ static bool ParseExpression(State *state) { // ::= L E // // A bug in g++'s C++ ABI version 2 (-fabi-version=2). // ::= LZ E +// +// Warning, subtle: the "bug" LZ production above is ambiguous with the first +// production where starts with , which can lead to +// exponential backtracking in two scenarios: +// +// - When whatever follows the E in the in the first production is +// not a name, we backtrack the whole and re-parse the whole thing. +// +// - When whatever follows the in the first production is not a +// number and this may be followed by a name, we backtrack the +// and re-parse it. +// +// Moreover this ambiguity isn't always resolved -- for example, the following +// has two different parses: +// +// _ZaaILZ4aoeuE1x1EvE +// => operator&& +// => operator&&<(aoeu::x)(1), void> +// +// To resolve this, we just do what GCC's demangler does, and refuse to parse +// casts to types. static bool ParseExprPrimary(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'L') && ParseType(state) && - ParseNumber(state, NULL) && - ParseOneCharToken(state, 'E')) { - return true; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + + // The "LZ" special case: if we see LZ, we commit to accept "LZ E" + // or fail, no backtracking. + if (ParseTwoCharToken(state, "LZ")) { + if (ParseEncoding(state) && ParseOneCharToken(state, 'E')) { + return true; + } + + state->parse_state = copy; + return false; } - *state = copy; + // The merged cast production. if (ParseOneCharToken(state, 'L') && ParseType(state) && - ParseFloatNumber(state) && - ParseOneCharToken(state, 'E')) { + ParseExprCastValue(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'L') && ParseMangledName(state) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) && - ParseOneCharToken(state, 'E')) { + return false; +} + +// or , followed by 'E', as described above ParseExprPrimary. +static bool ParseExprCastValue(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + // We have to be able to backtrack after accepting a number because we could + // have e.g. "7fffE", which will accept "7" as a number but then fail to find + // the 'E'. + ParseState copy = state->parse_state; + if (ParseNumber(state, nullptr) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; + + if (ParseFloatNumber(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; return false; } -// := Z <(function) encoding> E <(entity) name> -// [] -// := Z <(function) encoding> E s [] -static bool ParseLocalName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && - ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") && - ParseName(state) && Optional(ParseDiscriminator(state))) { +// ::= Z <(function) encoding> E <(entity) name> [] +// ::= Z <(function) encoding> E s [] +// +// Parsing a common prefix of these two productions together avoids an +// exponential blowup of backtracking. Parse like: +// := Z E +// ::= s [] +// ::= [] + +static bool ParseLocalNameSuffix(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (MaybeAppend(state, "::") && ParseName(state) && + Optional(ParseDiscriminator(state))) { return true; } - *state = copy; + // Since we're not going to overwrite the above "::" by re-parsing the + // (whose trailing '\0' byte was in the byte now holding the + // first ':'), we have to rollback the "::" if the parse failed. + if (state->parse_state.append) { + state->out[state->parse_state.out_cur_idx - 2] = '\0'; + } + + return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state)); +} + +static bool ParseLocalName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && - ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) { + ParseOneCharToken(state, 'E') && ParseLocalNameSuffix(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } // := _ <(non-negative) number> static bool ParseDiscriminator(State *state) { - State copy = *state; - if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) { return true; } - *state = copy; + state->parse_state = copy; return false; } // ::= S_ // ::= S _ // ::= St, etc. -static bool ParseSubstitution(State *state) { +// +// "St" is special in that it's not valid as a standalone name, and it *is* +// allowed to precede a name without being wrapped in "N...E". This means that +// if we accept it on its own, we can accept "St1a" and try to parse +// template-args, then fail and backtrack, accept "St" on its own, then "1a" as +// an unqualified name and re-parse the same template-args. To block this +// exponential backtracking, we disable it with 'accept_std=false' in +// problematic contexts. +static bool ParseSubstitution(State *state, bool accept_std) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; if (ParseTwoCharToken(state, "S_")) { MaybeAppend(state, "?"); // We don't support substitutions. return true; } - State copy = *state; + ParseState copy = state->parse_state; if (ParseOneCharToken(state, 'S') && ParseSeqId(state) && ParseOneCharToken(state, '_')) { MaybeAppend(state, "?"); // We don't support substitutions. return true; } - *state = copy; + state->parse_state = copy; // Expand abbreviations like "St" => "std". if (ParseOneCharToken(state, 'S')) { const AbbrevPair *p; - for (p = kSubstitutionList; p->abbrev != NULL; ++p) { - if (state->mangled_cur[0] == p->abbrev[1]) { + for (p = kSubstitutionList; p->abbrev != nullptr; ++p) { + if (RemainingInput(state)[0] == p->abbrev[1] && + (accept_std || p->abbrev[1] != 't')) { MaybeAppend(state, "std"); if (p->real_name[0] != '\0') { MaybeAppend(state, "::"); MaybeAppend(state, p->real_name); } - ++state->mangled_cur; + ++state->parse_state.mangled_idx; return true; } } } - *state = copy; + state->parse_state = copy; return false; } // Parse , optionally followed by either a function-clone suffix // or version suffix. Returns true only if all of "mangled_cur" was consumed. static bool ParseTopLevelMangledName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; if (ParseMangledName(state)) { - if (state->mangled_cur[0] != '\0') { + if (RemainingInput(state)[0] != '\0') { // Drop trailing function clone suffix, if any. - if (IsFunctionCloneSuffix(state->mangled_cur)) { + if (IsFunctionCloneSuffix(RemainingInput(state))) { return true; } // Append trailing version suffix if any. // ex. _Z3foo@@GLIBCXX_3.4 - if (state->mangled_cur[0] == '@') { - MaybeAppend(state, state->mangled_cur); + if (RemainingInput(state)[0] == '@') { + MaybeAppend(state, RemainingInput(state)); return true; } return false; // Unconsumed suffix. @@ -1320,6 +1990,10 @@ static bool ParseTopLevelMangledName(State *state) { } return false; } + +static bool Overflowed(const State *state) { + return state->parse_state.out_cur_idx >= state->out_end_idx; +} #endif // The demangler entry point. @@ -1356,7 +2030,8 @@ bool Demangle(const char *mangled, char *out, size_t out_size) { #else State state; InitState(&state, mangled, out, out_size); - return ParseTopLevelMangledName(&state) && !state.overflowed; + return ParseTopLevelMangledName(&state) && !Overflowed(&state) && + state.parse_state.out_cur_idx > 0; #endif } diff --git a/base/third_party/symbolize/demangle.h b/base/third_party/symbolize/demangle.h index 416f7ee153560..26e821a53c2cb 100644 --- a/base/third_party/symbolize/demangle.h +++ b/base/third_party/symbolize/demangle.h @@ -70,6 +70,8 @@ #ifndef BASE_DEMANGLE_H_ #define BASE_DEMANGLE_H_ +#include + #include "config.h" #include "glog/logging.h"