Fix memory leak in stk_base when Regexp timeout

[Bug #20228]

If rb_reg_check_timeout raises a Regexp::TimeoutError, then the stk_base
will leak.
This commit is contained in:
Peter Zhu 2024-01-30 14:15:56 -05:00
parent a4e4e3b1f1
commit 1c120efe02
3 changed files with 31 additions and 12 deletions

16
re.c
View File

@ -4605,8 +4605,8 @@ re_warn(const char *s)
rb_hrtime_t rb_reg_match_time_limit = 0; rb_hrtime_t rb_reg_match_time_limit = 0;
// This function is periodically called during regexp matching // This function is periodically called during regexp matching
void bool
rb_reg_check_timeout(regex_t *reg, void *end_time_) rb_reg_timeout_p(regex_t *reg, void *end_time_)
{ {
rb_hrtime_t *end_time = (rb_hrtime_t *)end_time_; rb_hrtime_t *end_time = (rb_hrtime_t *)end_time_;
@ -4631,10 +4631,18 @@ rb_reg_check_timeout(regex_t *reg, void *end_time_)
} }
else { else {
if (*end_time < rb_hrtime_now()) { if (*end_time < rb_hrtime_now()) {
// timeout is exceeded // Timeout has exceeded
rb_raise(rb_eRegexpTimeoutError, "regexp match timeout"); return true;
} }
} }
return false;
}
void
rb_reg_raise_timeout(void)
{
rb_raise(rb_eRegexpTimeoutError, "regexp match timeout");
} }
/* /*

View File

@ -2293,7 +2293,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
UChar *pkeep; UChar *pkeep;
char *alloca_base; char *alloca_base;
char *xmalloc_base = NULL; char *xmalloc_base = NULL;
OnigStackType *stk_alloc, *stk_base, *stk, *stk_end; OnigStackType *stk_alloc, *stk_base = NULL, *stk, *stk_end;
OnigStackType *stkp; /* used as any purpose. */ OnigStackType *stkp; /* used as any purpose. */
OnigStackIndex si; OnigStackIndex si;
OnigStackIndex *repeat_stk; OnigStackIndex *repeat_stk;
@ -4202,6 +4202,11 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STACK_SAVE; STACK_SAVE;
xfree(xmalloc_base); xfree(xmalloc_base);
return ONIGERR_UNEXPECTED_BYTECODE; return ONIGERR_UNEXPECTED_BYTECODE;
timeout:
xfree(xmalloc_base);
xfree(stk_base);
HANDLE_REG_TIMEOUT_IN_MATCH_AT;
} }

View File

@ -154,13 +154,18 @@
#ifdef RUBY #ifdef RUBY
# define CHECK_INTERRUPT_IN_MATCH_AT do { \ # define CHECK_INTERRUPT_IN_MATCH_AT do { \
msa->counter++; \ msa->counter++; \
if (msa->counter >= 128) { \ if (msa->counter >= 128) { \
msa->counter = 0; \ msa->counter = 0; \
rb_reg_check_timeout(reg, &msa->end_time); \ if (rb_reg_timeout_p(reg, &msa->end_time)) { \
rb_thread_check_ints(); \ goto timeout; \
} \ } \
rb_thread_check_ints(); \
} \
} while(0) } while(0)
# define HANDLE_REG_TIMEOUT_IN_MATCH_AT do { \
rb_reg_raise_timeout(); \
} while (0)
# define onig_st_init_table st_init_table # define onig_st_init_table st_init_table
# define onig_st_init_table_with_size st_init_table_with_size # define onig_st_init_table_with_size st_init_table_with_size
# define onig_st_init_numtable st_init_numtable # define onig_st_init_numtable st_init_numtable
@ -996,7 +1001,8 @@ extern int onig_st_insert_strend(hash_table_type* table, const UChar* str_key, c
#ifdef RUBY #ifdef RUBY
extern size_t onig_memsize(const regex_t *reg); extern size_t onig_memsize(const regex_t *reg);
extern size_t onig_region_memsize(const struct re_registers *regs); extern size_t onig_region_memsize(const struct re_registers *regs);
void rb_reg_check_timeout(regex_t *reg, void *end_time); bool rb_reg_timeout_p(regex_t *reg, void *end_time);
NORETURN(void rb_reg_raise_timeout(void));
#endif #endif
RUBY_SYMBOL_EXPORT_END RUBY_SYMBOL_EXPORT_END