mirror of
https://github.com/boostorg/regex.git
synced 2025-07-23 09:07:25 +02:00
Set a limit on max recursions.
Applies to last remaining recursive calls in regex creation, and format string parsing. Added tests, and updated CI.
This commit is contained in:
@ -90,6 +90,8 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define BOOST_REGEX_MAX_RECURSION_DEPTH 100
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
|
@ -93,3 +93,7 @@
|
||||
|
||||
// define this if you want regex to use __cdecl calling convensions, even when __fastcall is available:
|
||||
// #define BOOST_REGEX_NO_FASTCALL
|
||||
|
||||
// define this to control how deep non-avoidable recursive function calls can go, typical expressions
|
||||
// and format strings should only recurse a handful of times, this is mainly to prevent DOS type attacks.
|
||||
// #define BOOST_REGEX_MAX_RECURSION_DEPTH 100
|
||||
|
@ -248,7 +248,7 @@ private:
|
||||
void fixup_recursions(re_syntax_base* state);
|
||||
void create_startmaps(re_syntax_base* state);
|
||||
int calculate_backstep(re_syntax_base* state);
|
||||
void create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask);
|
||||
void create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask, unsigned recursion_count = 0);
|
||||
unsigned get_restart_type(re_syntax_base* state);
|
||||
void set_all_masks(unsigned char* bits, unsigned char);
|
||||
bool is_bad_repeat(re_syntax_base* pt);
|
||||
@ -1074,7 +1074,7 @@ struct recursion_saver
|
||||
};
|
||||
|
||||
template <class charT, class traits>
|
||||
void basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask)
|
||||
void basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask, unsigned recursion_count)
|
||||
{
|
||||
recursion_saver saved_recursions(&m_recursion_checks);
|
||||
int not_last_jump = 1;
|
||||
@ -1085,6 +1085,28 @@ void basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state,
|
||||
// track case sensitivity:
|
||||
bool l_icase = m_icase;
|
||||
|
||||
if (recursion_count > BOOST_REGEX_MAX_RECURSION_DEPTH)
|
||||
{
|
||||
// Oops error:
|
||||
if (0 == this->m_pdata->m_status) // update the error code if not already set
|
||||
this->m_pdata->m_status = boost::regex_constants::error_complexity;
|
||||
//
|
||||
// clear the expression, we should be empty:
|
||||
//
|
||||
this->m_pdata->m_expression = 0;
|
||||
this->m_pdata->m_expression_len = 0;
|
||||
//
|
||||
// and throw if required:
|
||||
//
|
||||
if (0 == (this->flags() & regex_constants::no_except))
|
||||
{
|
||||
std::string message = "Expression complexity exceeded.";
|
||||
boost::regex_error e(message, boost::regex_constants::error_complexity, 0);
|
||||
e.raise();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while(state)
|
||||
{
|
||||
switch(state->type)
|
||||
@ -1122,7 +1144,7 @@ void basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state,
|
||||
}
|
||||
// now figure out if we can match a NULL string at this point:
|
||||
if(pnull)
|
||||
create_startmap(state->next.p, 0, pnull, mask);
|
||||
create_startmap(state->next.p, 0, pnull, mask, ++recursion_count);
|
||||
return;
|
||||
}
|
||||
case syntax_element_recurse:
|
||||
@ -1184,7 +1206,7 @@ void basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state,
|
||||
case syntax_element_word_start:
|
||||
{
|
||||
// recurse, then AND with all the word characters:
|
||||
create_startmap(state->next.p, l_map, pnull, mask);
|
||||
create_startmap(state->next.p, l_map, pnull, mask, ++recursion_count);
|
||||
if(l_map)
|
||||
{
|
||||
l_map[0] |= mask_init;
|
||||
@ -1199,7 +1221,7 @@ void basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state,
|
||||
case syntax_element_word_end:
|
||||
{
|
||||
// recurse, then AND with all the word characters:
|
||||
create_startmap(state->next.p, l_map, pnull, mask);
|
||||
create_startmap(state->next.p, l_map, pnull, mask, ++recursion_count);
|
||||
if(l_map)
|
||||
{
|
||||
l_map[0] |= mask_init;
|
||||
@ -1291,11 +1313,11 @@ void basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state,
|
||||
return;
|
||||
}
|
||||
set_bad_repeat(state);
|
||||
create_startmap(state->next.p, l_map, pnull, mask);
|
||||
create_startmap(state->next.p, l_map, pnull, mask, ++recursion_count);
|
||||
if((state->type == syntax_element_alt)
|
||||
|| (static_cast<re_repeat*>(state)->min == 0)
|
||||
|| (not_last_jump == 0))
|
||||
create_startmap(rep->alt.p, l_map, pnull, mask);
|
||||
create_startmap(rep->alt.p, l_map, pnull, mask, ++recursion_count);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -1354,7 +1376,7 @@ void basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state,
|
||||
if(ok && ((m_recursion_checks[static_cast<re_brace*>(state)->index] & 2u) == 0))
|
||||
{
|
||||
m_recursion_checks[static_cast<re_brace*>(state)->index] |= 2u;
|
||||
create_startmap(p->next.p, l_map, pnull, mask);
|
||||
create_startmap(p->next.p, l_map, pnull, mask, ++recursion_count);
|
||||
}
|
||||
}
|
||||
state = state->next.p;
|
||||
|
@ -97,11 +97,11 @@ private:
|
||||
|
||||
void put(char_type c);
|
||||
void put(const sub_match_type& sub);
|
||||
void format_all();
|
||||
void format_all(unsigned recursion_count = 0);
|
||||
void format_perl();
|
||||
void format_escape();
|
||||
void format_conditional();
|
||||
void format_until_scope_end();
|
||||
void format_conditional(unsigned recursion_count);
|
||||
void format_until_scope_end(unsigned recursion_count);
|
||||
bool handle_perl_verb(bool have_brace);
|
||||
|
||||
inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const std::integral_constant<bool, false>&)
|
||||
@ -199,8 +199,13 @@ OutputIterator basic_regex_formatter<OutputIterator, Results, traits, ForwardIte
|
||||
}
|
||||
|
||||
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
||||
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_all()
|
||||
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_all(unsigned recursion_count)
|
||||
{
|
||||
if (recursion_count > BOOST_REGEX_MAX_RECURSION_DEPTH)
|
||||
{
|
||||
// We need to protect ourselves from bad format strings used as DOS attacks:
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Excessive recursion in format string, this looks like a deliberately malformed expression."));
|
||||
}
|
||||
// over and over:
|
||||
while(m_position != m_end)
|
||||
{
|
||||
@ -224,7 +229,7 @@ void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format
|
||||
++m_position;
|
||||
bool have_conditional = m_have_conditional;
|
||||
m_have_conditional = false;
|
||||
format_until_scope_end();
|
||||
format_until_scope_end(recursion_count);
|
||||
m_have_conditional = have_conditional;
|
||||
if(m_position == m_end)
|
||||
return;
|
||||
@ -255,7 +260,7 @@ void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format
|
||||
if(m_flags & boost::regex_constants::format_all)
|
||||
{
|
||||
++m_position;
|
||||
format_conditional();
|
||||
format_conditional(recursion_count);
|
||||
break;
|
||||
}
|
||||
put(*m_position);
|
||||
@ -644,7 +649,7 @@ void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format
|
||||
}
|
||||
|
||||
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
||||
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_conditional()
|
||||
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_conditional(unsigned recursion_count)
|
||||
{
|
||||
if(m_position == m_end)
|
||||
{
|
||||
@ -692,7 +697,7 @@ void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format
|
||||
if(m_results[v].matched)
|
||||
{
|
||||
m_have_conditional = true;
|
||||
format_all();
|
||||
format_all(++recursion_count);
|
||||
m_have_conditional = false;
|
||||
if((m_position != m_end) && (*m_position == static_cast<char_type>(':')))
|
||||
{
|
||||
@ -702,7 +707,7 @@ void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format
|
||||
output_state saved_state = m_state;
|
||||
m_state = output_none;
|
||||
// format the rest of this scope:
|
||||
format_until_scope_end();
|
||||
format_until_scope_end(recursion_count);
|
||||
// restore output state:
|
||||
m_state = saved_state;
|
||||
}
|
||||
@ -714,7 +719,7 @@ void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format
|
||||
m_state = output_none;
|
||||
// format until ':' or ')':
|
||||
m_have_conditional = true;
|
||||
format_all();
|
||||
format_all(++recursion_count);
|
||||
m_have_conditional = false;
|
||||
// restore state:
|
||||
m_state = saved_state;
|
||||
@ -723,17 +728,17 @@ void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format
|
||||
// skip the ':':
|
||||
++m_position;
|
||||
// format the rest of this scope:
|
||||
format_until_scope_end();
|
||||
format_until_scope_end(recursion_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
||||
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_until_scope_end()
|
||||
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_until_scope_end(unsigned recursion_count)
|
||||
{
|
||||
do
|
||||
{
|
||||
format_all();
|
||||
format_all(++recursion_count);
|
||||
if((m_position == m_end) || (*m_position == static_cast<char_type>(')')))
|
||||
return;
|
||||
put(*m_position++);
|
||||
|
Reference in New Issue
Block a user