 |
| Caled |
Posted: Tue Apr 08, 2008 11:30 pm
[2.22] Improvement: compatibility report |
 |
Guinn Wizard
Joined: 03 Mar 2001 Posts: 1127 Location: London
|
Posted: Thu May 01, 2008 12:00 am |
| Quote: |
| There you have it. |
Hmm, I think I missed it actually... |
|
_________________ CMUD Pro, Windows Vista x64
Core2 Q6600, 4GB RAM, GeForce 8800GT
Because you need it for text... ;) |
|
|
 |
oldguy2 Wizard
Joined: 17 Jun 2006 Posts: 1201
|
Posted: Fri May 02, 2008 2:59 pm |
I guess we still need to use the other dll file until 2.24? CMUD version 2.23 still terminates when running the report.
|
|
|
|
 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Fri May 02, 2008 7:53 pm |
I will aim at trying to isolate this. Hopefully it is the regex Zugg posted earlier, in which case, I can work at refactoring it until it works.
|
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Fri May 02, 2008 10:19 pm |
After a few hundred crashes I came up with this smaller regex that seems to match everytime it should and doesn't match when it shouldn't. The key thing is it doesn't cause a crash.
| Code: |
| (?>[^\042\n\r]*(?<!\176)\042)(?:(?:[^\042\045\n\r]|\176\042|\176\045)*?\045\{?[tx-]?\d+\}?(?:[^\042\n\r]|\176\042)*?)\042[^\n\r]* |
I believe there is another regex for the %i..%n references which was also based around the previous bad regex. That one would also need to be changed with additional minor adjustments for the supported letters in the middle.
Considering how many different regex designs I went through that caused crashes I guess all my beta testing time this weekend will be spent trying different dll builds to see if I can make it better. My first step really is to examine and study the source for 7.0 version of the PCRE and see if there is a any significant difference in the stack usage. The configuration of the build oldguy2 tested was the same as the one I made, which means if there is no significant difference in the underlying code then the problem is in something done in my compilation. Hopefully I will have it sorted out in short order. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Sat May 03, 2008 11:21 pm |
This is just plain frustrating. I compared both sources and came up with a 16 byte difference in the stack allocation for the function that is causing the crash. Comparing the assembly I have $2BC for the 7.0 and $3DC for my build of the 7.6, that is 272 bytes too many.
I also skimmed through about half the assembly for the problem function and found it wasn't using many of the allocated addresses. Evidently the compiler optimized those out but increased the allocation amount.
I decided I really should make a base line and turned off all the compiler optimizations. That build increases the file size by 20K over the fully optimized build, and has the stack allocation in the problem spot down to $280. I could barely even recognize the assembly that was produced with all the extra crap.
Hopefully I should get a happy medium as I tweak through the optimization settings. If I can't find one I am liable to go hunting for a new C compiler. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Sun May 04, 2008 12:58 am |
Adjusting the compiler optimizations did the trick. I actually got some better numbers then the version oldguy2 tested. The data he provided to replicate this problem now successuflly passes the compatibilty report. However, I can still create a crash with some regex patterns when used within scripts.
I have emailed Zugg the new dll and some files replicate the further crash. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Mon May 05, 2008 5:23 pm |
I've got the new PCRE.DLL file from Vijilante, but I'm still confused and concerned about this. As far as I can tell, v2.23 increased the stack size in CMUD by a factor of 8 over v2.22. So I don't understand why OldGuy's crash still happened if it's related to stack size issues.
But I'll put the updated PCRE.DLL into v2.24 and also put in the improved regular expression for testing %1..%99 and %i,%j,etc within quotes. Thanks for helping with this Vijilante. It's one of those great examples of how frustrating writing Windows software really is these days with stuff like this that works fine for most people, but not on some computers. |
|
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Mon May 05, 2008 9:16 pm |
Btw, the new regular expression still matches the following case:
| Code: |
| #if ("a"%1"b" = "add") {#NOOP} |
In this case, the %1 isn't really within quotes, but it still gets flagged by the compat report.
Edited: Actually, the old regex has the same problem. So I guess the new one isn't any worse, but I thought the whole idea with the complex regex was to try and catch cases like this. Maybe there just isn't any good way to handle this with a regular expression? |
|
|
|
 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Mon May 05, 2008 9:46 pm |
There isn't really a good way, because the " character is both the opening and the close, and then there is the use of a tilde before it to make things even more confusing. In the old pattern I tried to lock in the start of the string and have the pattern work its way through the text. This is why it tended to recurse so deeply. The new pattern I aimed more to let it fail from time to time allowing the recursion to break and the top matching loop to move along, neither solution is great.
If you look at the regex I use for parsing all the syntaxes for the subregex you will see the way to handle this is through a pattern defined recursion. That is what I did to cover all the nesting of conditionals, but in order to really make it work I had to use callout items to do the variable width look behinds that were needed. Some problems are complex enough that regexes struggle to handle them without supporting code.
Adjusting the new pattern so it was slightly anchored seems to have helped.
| Code: |
| (?<=\n|\r|\A)(?>[^\042\n\r]*(?<!\176)\042)(?:(?:[^\042\045\n\r]|\176\042|\176\045)*?\045\{?[tx-]?\d+\}?(?:[^\042\n\r]|\176\042)*?)\042[^\n\r]* |
|
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Mon May 05, 2008 10:21 pm |
I've got to give you credit Vij...you sure know regular expressions a *lot* better than I do. I'm taking your new pattern on faith :)
I did a bit of google searching on matching quotes with regular expressions, and it does seem to be a classically hard problem. I think this is good enough for what the compatibility report needs. Thanks! |
|
|
|
 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Tue May 06, 2008 10:34 am |
I think I got the best possible regex for this. It drops out like the one above, and also uses a recursion pattern to match quote pairs. I am using a sizable amount of data for tesing on this to make sure it doesn't cause a crash. New patterns in the test alias
| Code: |
<?xml version="1.0" encoding="ISO-8859-1" ?>
<cmud>
<alias name="test" copy="yes">
<value><![CDATA[//%1 in quotes
#SHOW %regex(%alias(a),"((?<=\n|\r|\A)(?<QH>(?>[^\042\n\r]|[\n\r](*FAIL)|(?<=\176)\042)*(?<!\176)\042(?>[^\042\045\n\r]|[\n\r](*FAIL)|(?<=\176)[\042\045])*(?:\042(?&QH))?)\045\{?[tx-]?\d+\}?[^\n\r]*)",$match,$QH) $match $QH
//%i in quotes
#SHOW %regex(%alias(a),"((?<=\n|\r|\A)(?<QH>(?>[^\042\n\r]|[\n\r](*FAIL)|(?<=\176)\042)*(?<!\176)\042(?>[^\042\045\n\r]|[\n\r](*FAIL)|(?<=\176)[\042\045])*(?:\042(?&QH))?)\045\{?[ijklmn]\}?[^\n\r]*)",$match,$QH) $match $QH
//%pat in quotes
#SHOW %regex(%alias(a),"((?<=\n|\r|\A)(?<QH>(?>[^\042\n\r]|[\n\r](*FAIL)|(?<=\176)\042)*(?<!\176)\042(?>[^\042\045\n\r]|[\n\r](*FAIL)|(?<=\176)[\042\045])*(?:\042(?&QH))?)\045pat[^\n\r]*)",$match,$QH) $match $QH]]></value>
</alias>
<alias name="a" copy="yes">
<value>xyzv "abc;def"%1
testing ~"%1"
#IF (%pos( X, $flags)) {kelventari=1} {kelventari=0}
#IF (%pos( E, $flags)) {Equilibrium=1} {Equilibrium=0}
#IF (%pos( C, $flags)) {diffusion=1} {diffusion=0}
#IF (%pos( D, $flags)) {deaf=1} {deaf=0}
#IF (%pos( B, $flags)) {blind=1} {blind=0}
#IF (%pos( Z, $flags)) {LeftBalance=0} {LeftBalance=1}
#IF (%pos( Y, $flags)) {RightBalance=0} {RightBalance=1}
#IF (%pos( W, $flags)) {webbed=1} {webbed=0}
#IF (%pos( H, $flags)) {allhealbal=0} {allhealbal=1}
#IF (%pos( L, $flags)) {levitation=1} {levitation=0}
#IF (%pos( P, $flags)) {hmbal=0} {hmbal=1}
#IF (%pos( I, $flags)) {immunity=1} {immunity=0}
#IF (%pos( F, $flags)) {fire=1} {fire=0}
#IF (%pos( A, $flags)) {alkar=1} {alkar=0}
#IF (%pos( R, $flags)) {resisting=1} {resisting=0}
#IF (%pos( T, $flags)) {lestbal=0} {lestbal=1}
#IF ($stealth == "(-)") {invisibility=1} {invisibility=0}
#IF (%pos( X, $flags)) {kelventari=1} {kelventari=0}
#IF (%pos( E, $flags)) {Equilibrium=1} {Equilibrium=0}
#IF (%pos( C, $flags)) {diffusion=1} {diffusion=0}
#IF (%pos( D, $flags)) {deaf=1} {deaf=0}
#IF (%pos( B, $flags)) {blind=1} {blind=0}
#IF (%pos( Z, $flags)) {LeftBalance=0} {LeftBalance=1}
#IF (%pos( Y, $flags)) {RightBalance=0} {RightBalance=1}
#IF (%pos( W, $flags)) {webbed=1} {webbed=0}
#IF (%pos( H, $flags)) {allhealbal=0} {allhealbal=1}
#IF (%pos( L, $flags)) {levitation=1} {levitation=0}
#IF (%pos( P, $flags)) {hmbal=0} {hmbal=1}
#IF (%pos( I, $flags)) {immunity=1} {immunity=0}
#IF (%pos( F, $flags)) {fire=1} {fire=0}
#IF (%pos( A, $flags)) {alkar=1} {alkar=0}
#IF (%pos( R, $flags)) {resisting=1} {resisting=0}
#IF (%pos( T, $flags)) {lestbal=0} {lestbal=1}
#IF ($stealth == "(-)") {invisibility=1} {invisibility=0}
#IF (%pos( X, $flags)) {kelventari=1} {kelventari=0}
#IF (%pos( E, $flags)) {Equilibrium=1} {Equilibrium=0}
#IF (%pos( C, $flags)) {diffusion=1} {diffusion=0}
#IF (%pos( D, $flags)) {deaf=1} {deaf=0}
#IF (%pos( B, $flags)) {blind=1} {blind=0}
#IF (%pos( Z, $flags)) {LeftBalance=0} {LeftBalance=1}
#IF (%pos( Y, $flags)) {RightBalance=0} {RightBalance=1}
#IF (%pos( W, $flags)) {webbed=1} {webbed=0}
#IF (%pos( H, $flags)) {allhealbal=0} {allhealbal=1}
#IF (%pos( L, $flags)) {levitation=1} {levitation=0}
#IF (%pos( P, $flags)) {hmbal=0} {hmbal=1}
#IF (%pos( I, $flags)) {immunity=1} {immunity=0}
#IF (%pos( F, $flags)) {fire=1} {fire=0}
#IF (%pos( A, $flags)) {alkar=1} {alkar=0}
#IF (%pos( R, $flags)) {resisting=1} {resisting=0}
#IF (%pos( T, $flags)) {lestbal=0} {lestbal=1}
#IF ($stealth == "(-)") {invisibility=1} {invisibility=0}
#IF (%pos( X, $flags)) {kelventari=1} {kelventari=0}
#IF (%pos( E, $flags)) {Equilibrium=1} {Equilibrium=0}
#IF (%pos( C, $flags)) {diffusion=1} {diffusion=0}
#IF (%pos( D, $flags)) {deaf=1} {deaf=0}
#IF (%pos( B, $flags)) {blind=1} {blind=0}
#IF (%pos( Z, $flags)) {LeftBalance=0} {LeftBalance=1}
#IF (%pos( Y, $flags)) {RightBalance=0} {RightBalance=1}
#IF (%pos( W, $flags)) {webbed=1} {webbed=0}
#IF (%pos( H, $flags)) {allhealbal=0} {allhealbal=1}
#IF (%pos( L, $flags)) {levitation=1} {levitation=0}
#IF (%pos( P, $flags)) {hmbal=0} {hmbal=1}
#IF (%pos( I, $flags)) {immunity=1} {immunity=0}
#IF (%pos( F, $flags)) {fire=1} {fire=0}
#IF (%pos( A, $flags)) {alkar=1} {alkar=0}
#IF (%pos( R, $flags)) {resisting=1} {resisting=0}
#IF (%pos( T, $flags)) {lestbal=0} {lestbal=1}
#IF ($stealth == "(-)") {invisibility=1} {invisibility=0}
#IF (%pos( X, $flags)) {kelventari=1} {kelventari=0}
#IF (%pos( E, $flags)) {Equilibrium=1} {Equilibrium=0}
#IF (%pos( C, $flags)) {diffusion=1} {diffusion=0}
#IF (%pos( D, $flags)) {deaf=1} {deaf=0}
#IF (%pos( B, $flags)) {blind=1} {blind=0}
#IF (%pos( Z, $flags)) {LeftBalance=0} {LeftBalance=1}
#IF (%pos( Y, $flags)) {RightBalance=0} {RightBalance=1}
#IF (%pos( W, $flags)) {webbed=1} {webbed=0}
#IF (%pos( H, $flags)) {allhealbal=0} {allhealbal=1}
#IF (%pos( L, $flags)) {levitation=1} {levitation=0}
#IF (%pos( P, $flags)) {hmbal=0} {hmbal=1}
#IF (%pos( I, $flags)) {immunity=1} {immunity=0}
#IF (%pos( F, $flags)) {fire=1} {fire=0}
#IF (%pos( A, $flags)) {alkar=1} {alkar=0}
#IF (%pos( R, $flags)) {resisting=1} {resisting=0}
#IF (%pos( T, $flags)) {lestbal=0} {lestbal=1}
#IF ($stealth == "(-)") {invisibility=1} {invisibility=0}
#IF (%pos( X, $flags)) {kelventari=1} {kelventari=0}
#IF (%pos( E, $flags)) {Equilibrium=1} {Equilibrium=0}
#IF (%pos( C, $flags)) {diffusion=1} {diffusion=0}
#IF (%pos( D, $flags)) {deaf=1} {deaf=0}
#IF (%pos( B, $flags)) {blind=1} {blind=0}
#IF (%pos( Z, $flags)) {LeftBalance=0} {LeftBalance=1}
#IF (%pos( Y, $flags)) {RightBalance=0} {RightBalance=1}
#IF (%pos( W, $flags)) {webbed=1} {webbed=0}
#IF (%pos( H, $flags)) {allhealbal=0} {allhealbal=1}
#IF (%pos( L, $flags)) {levitation=1} {levitation=0}
#IF (%pos( P, $flags)) {hmbal=0} {hmbal=1}
#IF (%pos( I, $flags)) {immunity=1} {immunity=0}
#IF (%pos( F, $flags)) {fire=1} {fire=0}
#IF (%pos( A, $flags)) {alkar=1} {alkar=0}
#IF (%pos( R, $flags)) {resisting=1} {resisting=0}
#IF (%pos( T, $flags)) {lestbal=0} {lestbal=1}
#IF ($stealth == "(-)") {invisibility=1} {invisibility=0}
testing g "%1 x
testing "g" %1 "x"
testing "g %-1 x"
ghijkl;"testing %i sdfk"
testing "gd" %pat(1) "xc"
testing "jkl %pat(1) "</value>
</alias>
</cmud> |
|
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Tue May 06, 2008 12:50 pm |
Bah, I found another way to make the above regexes fail when they should match. I will include a new regex in an email with final updates for %subregex.
|
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Thu Jul 24, 2008 12:12 am |
Time has gone by since:
| Vijilante wrote: |
However, I can still create a crash with some regex patterns when used within scripts.
I have emailed Zugg the new dll and some files replicate the further crash. |
Is there a way to prevent a stack overflow (or infinite loop) and abort instead of crashing / hanging?
I was reading this:
| Quote: |
If you try this regex on a 10x string in RegexBuddy's debugger, it'll take 2558 steps to figure out the final y is missing. For an 11x string, it needs 5118 steps. For 12, it takes 10238 steps. Clearly we have an exponential complexity of O(2^n) here. At 21x the debugger bows out at 2.8 million steps, diagnosing a bad case of catastrophic backtracking.
RegexBuddy is forgiving in that it detects it's going in circles, and aborts the match attempt. Other regex engines (like .NET) will keep going forever, while others will crash with a stack overflow (like Perl, before version 5.10). Stack overflows are particularly nasty on Windows, since they tend to make your application vanish without a trace or explanation. Be very careful if you run a web service that allows users to supply their own regular expressions. People with little regex experience have surprising skill at coming up with exponentially complex regular expressions. |
... and naturally wondered if CMUD now had code to prevent stack overflows (or infinite loops)? |
|
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Thu Jul 24, 2008 4:54 pm |
Detecting a stack overflow and preventing it is very difficult. In this case, CMUD can't do anything because the problem code is in the DLL. Vijilante has talked about looking into the PCRE code itself to improve this. All I can think is that RegexBuddy is not using the PCRE.DLL file like we are. But once CMUD has called the DLL function, there isn't anything it can do to trap a stack overflow. There is no general purpose way to trap a stack overflow like other exceptions. By the time you detect it, it is too late and often impossible to recover from. I've done some research into this for Delphi apps and haven't found anything helpful.
|
|
|
|
 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Thu Jul 24, 2008 9:58 pm |
I am going to ramble a lot in this reply. Many of the details are far too complex to get into, but I would hope the tip of the iceberg is enough to understand why CMud has sunk in the past on this issue.
RegexBuddy does not use PCRE. If you look at thier documentation they wrote thier own engine with the express goal of displaying each step in the standard regex alogarithm. Last time I checked they also didn't support anything that was added to the Perl regex syntax since 5.00.
My current thoughts on eliminating the stack overflow are to:
1. Determine actual stack usage, as I showed above this is dependent on how the dll is compiled, so I have to come up with some assembly to do it. Any code in C would be fair game for the compiler to muck in, and also likely would not give me consistent results. I tend to think that to be the easy part.
2. Determine how much stack is available. This is the hard part. Stacks can be dynamically allocated and I have to probe the applications allocations to determine the one that is applicable. Each thread actually has its own stack area so I have to make sure I am matching to the space I am running in at the time. I would like to make it as a non-OS dependent, ANSI C function. That way it can be included into the PCRE source by the author, and Zugg wouldn't have to be concerned about having this problem when he upgrades to a new version. If I can't come up with a general way to do this then it becomes another of the items in the custom dll, that requires maintainance in order to update the PCRE.
3. Calculate how far the regex can go. Again this is an easy part. Simple math handles it. The PCRE already provides a mechanism to stop the pattern from creating a stack overflow, but the problem is in setting a cuttoff and letting the user know they hit the cutoff instead of just having a no match. It is up to Zugg to decide how he might inform the user of having hit the cutoff. It is up to me to make the cutoff dynamic so that an arbritray limit is not required.
We could currently set the default limit in the inerface low enough to ensure that no overflow can occur, but the actual limit really needs to be dynamic to accomodate different things. Having a trigger pattern fail because of some limit is a problem, having CMud crash is worse. Most trigger patterns can't cause a crash because the data being tested is a single line. The most recent crash found in the Compatibility Report required a much larger dataset, that didn't contain linebreaks. I set up the regex to release on a line break; however, a small changes later produced a regex that didn't crash even under more extreme circumstances. The latest version I have supplied Zugg maintains speed while eliminating the crash, but recognizing the validity of that change comes from study of the PCRE code.
I fully expect users of CMud to write bad regexes. I have made hundreds of them, and caused both CMud and zMud to crash, burn, and die a thousand deaths while testing regexes. That is something I am aiming at preventing, if I can. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Thu Jul 24, 2008 11:17 pm |
Well, you're both clearly aware of all the issues, and it does sound like crashes are very unlikely at this point in time, but obviously crashes with no crash dump can be difficult to track down, especially if it was MUD output that caused the crash instead of the Compatibility Report, so any improvement on this would be great. But I'll leave you to decide what (if anything) to do about it.
|
|
|
|
 |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|