Skip to content

Commit 2fcd8c1

Browse files
committed
MDEV-13173 An RLIKE that previously worked on 10.0 now returns "Got error 'pcre_exec: recursion limit of 100 exceeded' from regexp"
1. use Regexp_processor_pcre::set_recursion_limit() to set the recursion limit depending on the current available stack size 2. make pcre stack frame to be estimated no less than 500 bytes. sometimes pcre estimates it too low, even though the manual says 500+16 bytes (it was estimated only 188 for me, actual frame size was 512). 3. do it for embedded too
1 parent dc8b2fb commit 2fcd8c1

File tree

7 files changed

+87
-7
lines changed

7 files changed

+87
-7
lines changed

mysql-test/r/func_regexp_pcre.result

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,3 +879,35 @@ SELECT 1 FROM dual WHERE ('Alpha,Bravo,Charlie,Delta,Echo,Foxtrot,StrataCentral,
879879
1
880880
Warnings:
881881
Warning 1139 Got error 'pcre_exec: recursion limit of NUM exceeded' from regexp
882+
SELECT CONCAT(REPEAT('100,',500),'101') RLIKE '^(([1-9][0-9]*),)*[1-9][0-9]*$';
883+
CONCAT(REPEAT('100,',500),'101') RLIKE '^(([1-9][0-9]*),)*[1-9][0-9]*$'
884+
1
885+
SELECT CONCAT(REPEAT('100,',600),'101') RLIKE '^(([1-9][0-9]*),)*[1-9][0-9]*$';
886+
CONCAT(REPEAT('100,',600),'101') RLIKE '^(([1-9][0-9]*),)*[1-9][0-9]*$'
887+
0
888+
Warnings:
889+
Warning 1139 Got error 'pcre_exec: recursion limit of NUM exceeded' from regexp
890+
SELECT REGEXP_INSTR(CONCAT(REPEAT('100,',500),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$');
891+
REGEXP_INSTR(CONCAT(REPEAT('100,',500),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$')
892+
1
893+
SELECT REGEXP_INSTR(CONCAT(REPEAT('100,',600),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$');
894+
REGEXP_INSTR(CONCAT(REPEAT('100,',600),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$')
895+
0
896+
Warnings:
897+
Warning 1139 Got error 'pcre_exec: recursion limit of NUM exceeded' from regexp
898+
SELECT LENGTH(REGEXP_SUBSTR(CONCAT(REPEAT('100,',500/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$'));
899+
LENGTH(REGEXP_SUBSTR(CONCAT(REPEAT('100,',500/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$'))
900+
671
901+
SELECT LENGTH(REGEXP_SUBSTR(CONCAT(REPEAT('100,',600/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$'));
902+
LENGTH(REGEXP_SUBSTR(CONCAT(REPEAT('100,',600/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$'))
903+
0
904+
Warnings:
905+
Warning 1139 Got error 'pcre_exec: recursion limit of NUM exceeded' from regexp
906+
SELECT LENGTH(REGEXP_REPLACE(CONCAT(REPEAT('100,',500/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$', ''));
907+
LENGTH(REGEXP_REPLACE(CONCAT(REPEAT('100,',500/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$', ''))
908+
0
909+
SELECT LENGTH(REGEXP_REPLACE(CONCAT(REPEAT('100,',600/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$', ''));
910+
LENGTH(REGEXP_REPLACE(CONCAT(REPEAT('100,',600/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$', ''))
911+
803
912+
Warnings:
913+
Warning 1139 Got error 'pcre_exec: recursion limit of NUM exceeded' from regexp

mysql-test/t/func_regexp_pcre.test

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,22 @@ SELECT CAST(0xE001 AS BINARY) REGEXP @regCheck;
430430
--echo # MDEV-12420: Testing recursion overflow
431431
--replace_regex /[0-9]+ exceeded/NUM exceeded/
432432
SELECT 1 FROM dual WHERE ('Alpha,Bravo,Charlie,Delta,Echo,Foxtrot,StrataCentral,Golf,Hotel,India,Juliet,Kilo,Lima,Mike,StrataL3,November,Oscar,StrataL2,Sand,P3,P4SwitchTest,Arsys,Poppa,ExtensionMgr,Arp,Quebec,Romeo,StrataApiV2,PtReyes,Sierra,SandAcl,Arrow,Artools,BridgeTest,Tango,SandT,PAlaska,Namespace,Agent,Qos,PatchPanel,ProjectReport,Ark,Gimp,Agent,SliceAgent,Arnet,Bgp,Ale,Tommy,Central,AsicPktTestLib,Hsc,SandL3,Abuild,Pca9555,Standby,ControllerDut,CalSys,SandLib,Sb820,PointV2,BfnLib,Evpn,BfnSdk,Sflow,ManagementActive,AutoTest,GatedTest,Bgp,Sand,xinetd,BfnAgentLib,bf-utils,Hello,BfnState,Eos,Artest,Qos,Scd,ThermoMgr,Uniform,EosUtils,Eb,FanController,Central,BfnL3,BfnL2,tcp_wrappers,Victor,Environment,Route,Failover,Whiskey,Xray,Gimp,BfnFixed,Strata,SoCal,XApi,Msrp,XpProfile,tcpdump,PatchPanel,ArosTest,FhTest,Arbus,XpAcl,MacConc,XpApi,telnet,QosTest,Alpha2,BfnVlan,Stp,VxlanControllerTest,MplsAgent,Bravo2,Lanz,BfnMbb,Intf,XCtrl,Unicast,SandTunnel,L3Unicast,Ipsec,MplsTest,Rsvp,EthIntf,StageMgr,Sol,MplsUtils,Nat,Ira,P4NamespaceDut,Counters,Charlie2,Aqlc,Mlag,Power,OpenFlow,Lag,RestApi,BfdTest,strongs,Sfa,CEosUtils,Adt746,MaintenanceMode,MlagDut,EosImage,IpEth,MultiProtocol,Launcher,Max3179,Snmp,Acl,IpEthTest,PhyEee,bf-syslibs,tacc,XpL2,p4-ar-switch,p4-bf-switch,LdpTest,BfnPhy,Mirroring,Phy6,Ptp' REGEXP '^((?!\b(Strata|StrataApi|StrataApiV2)\b).)*$');
433+
434+
#
435+
# MDEV-13173 An RLIKE that previously worked on 10.0 now returns "Got error 'pcre_exec: recursion limit of 100 exceeded' from regexp"
436+
#
437+
SELECT CONCAT(REPEAT('100,',500),'101') RLIKE '^(([1-9][0-9]*),)*[1-9][0-9]*$';
438+
--replace_regex /[0-9]+ exceeded/NUM exceeded/
439+
SELECT CONCAT(REPEAT('100,',600),'101') RLIKE '^(([1-9][0-9]*),)*[1-9][0-9]*$';
440+
441+
SELECT REGEXP_INSTR(CONCAT(REPEAT('100,',500),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$');
442+
--replace_regex /[0-9]+ exceeded/NUM exceeded/
443+
SELECT REGEXP_INSTR(CONCAT(REPEAT('100,',600),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$');
444+
445+
SELECT LENGTH(REGEXP_SUBSTR(CONCAT(REPEAT('100,',500/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$'));
446+
--replace_regex /[0-9]+ exceeded/NUM exceeded/
447+
SELECT LENGTH(REGEXP_SUBSTR(CONCAT(REPEAT('100,',600/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$'));
448+
449+
SELECT LENGTH(REGEXP_REPLACE(CONCAT(REPEAT('100,',500/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$', ''));
450+
--replace_regex /[0-9]+ exceeded/NUM exceeded/
451+
SELECT LENGTH(REGEXP_REPLACE(CONCAT(REPEAT('100,',600/3),'101'), '^(([1-9][0-9]*),)*[1-9][0-9]*$', ''));

sql/item_cmpfunc.cc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5110,7 +5110,7 @@ void Regexp_processor_pcre::set_recursion_limit(THD *thd)
51105110
DBUG_ASSERT(thd == current_thd);
51115111
stack_used= available_stack_size(thd->thread_stack, &stack_used);
51125112
m_pcre_extra.match_limit_recursion=
5113-
(my_thread_stack_size - stack_used)/my_pcre_frame_size;
5113+
(my_thread_stack_size - STACK_MIN_SIZE - stack_used)/my_pcre_frame_size;
51145114
}
51155115

51165116

@@ -5372,6 +5372,12 @@ void Regexp_processor_pcre::fix_owner(Item_func *owner,
53725372
}
53735373

53745374

5375+
bool Item_func_regex::fix_fields(THD *thd, Item **ref)
5376+
{
5377+
re.set_recursion_limit(thd);
5378+
return Item_bool_func::fix_fields(thd, ref);
5379+
}
5380+
53755381
void
53765382
Item_func_regex::fix_length_and_dec()
53775383
{
@@ -5398,6 +5404,13 @@ longlong Item_func_regex::val_int()
53985404
}
53995405

54005406

5407+
bool Item_func_regexp_instr::fix_fields(THD *thd, Item **ref)
5408+
{
5409+
re.set_recursion_limit(thd);
5410+
return Item_int_func::fix_fields(thd, ref);
5411+
}
5412+
5413+
54015414
void
54025415
Item_func_regexp_instr::fix_length_and_dec()
54035416
{

sql/item_cmpfunc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,7 @@ class Item_func_regex :public Item_bool_func
16521652
DBUG_VOID_RETURN;
16531653
}
16541654
longlong val_int();
1655+
bool fix_fields(THD *thd, Item **ref);
16551656
void fix_length_and_dec();
16561657
const char *func_name() const { return "regexp"; }
16571658

@@ -1679,6 +1680,7 @@ class Item_func_regexp_instr :public Item_int_func
16791680
DBUG_VOID_RETURN;
16801681
}
16811682
longlong val_int();
1683+
bool fix_fields(THD *thd, Item **ref);
16821684
void fix_length_and_dec();
16831685
const char *func_name() const { return "regexp_instr"; }
16841686
};

sql/item_strfunc.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,13 @@ void Item_func_replace::fix_length_and_dec()
13431343

13441344

13451345
/*********************************************************************/
1346+
bool Item_func_regexp_replace::fix_fields(THD *thd, Item **ref)
1347+
{
1348+
re.set_recursion_limit(thd);
1349+
return Item_str_func::fix_fields(thd, ref);
1350+
}
1351+
1352+
13461353
void Item_func_regexp_replace::fix_length_and_dec()
13471354
{
13481355
if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 3))
@@ -1478,6 +1485,13 @@ String *Item_func_regexp_replace::val_str(String *str)
14781485
}
14791486

14801487

1488+
bool Item_func_regexp_substr::fix_fields(THD *thd, Item **ref)
1489+
{
1490+
re.set_recursion_limit(thd);
1491+
return Item_str_func::fix_fields(thd, ref);
1492+
}
1493+
1494+
14811495
void Item_func_regexp_substr::fix_length_and_dec()
14821496
{
14831497
if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 2))

sql/item_strfunc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ class Item_func_regexp_replace :public Item_str_func
231231
DBUG_VOID_RETURN;
232232
}
233233
String *val_str(String *str);
234+
bool fix_fields(THD *thd, Item **ref);
234235
void fix_length_and_dec();
235236
const char *func_name() const { return "regexp_replace"; }
236237
};
@@ -251,6 +252,7 @@ class Item_func_regexp_substr :public Item_str_func
251252
DBUG_VOID_RETURN;
252253
}
253254
String *val_str(String *str);
255+
bool fix_fields(THD *thd, Item **ref);
254256
void fix_length_and_dec();
255257
const char *func_name() const { return "regexp_substr"; }
256258
};

sql/mysqld.cc

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3451,7 +3451,6 @@ sizeof(load_default_groups)/sizeof(load_default_groups[0]);
34513451
#endif
34523452

34533453

3454-
#ifndef EMBEDDED_LIBRARY
34553454
/**
34563455
This function is used to check for stack overrun for pathological
34573456
cases of regular expressions and 'like' expressions.
@@ -3480,8 +3479,6 @@ check_enough_stack_size(int recurse_level)
34803479
return 0;
34813480
return check_enough_stack_size_slow();
34823481
}
3483-
#endif
3484-
34853482

34863483

34873484
/*
@@ -3503,11 +3500,12 @@ static void init_pcre()
35033500
{
35043501
pcre_malloc= pcre_stack_malloc= my_str_malloc_mysqld;
35053502
pcre_free= pcre_stack_free= my_str_free_mysqld;
3506-
#ifndef EMBEDDED_LIBRARY
35073503
pcre_stack_guard= check_enough_stack_size_slow;
35083504
/* See http://pcre.org/original/doc/html/pcrestack.html */
3509-
my_pcre_frame_size= -pcre_exec(NULL, NULL, NULL, -999, -999, 0, NULL, 0) + 16;
3510-
#endif
3505+
my_pcre_frame_size= -pcre_exec(NULL, NULL, NULL, -999, -999, 0, NULL, 0);
3506+
// pcre can underestimate its stack usage. Use a safe value, as in the manual
3507+
set_if_bigger(my_pcre_frame_size, 500);
3508+
my_pcre_frame_size += 16; // Again, safety margin, see the manual
35113509
}
35123510

35133511

0 commit comments

Comments
 (0)