Author: thebeing
Date: Fri Jun 17 11:04:04 2016
New Revision: 39872

URL: http://svn.gna.org/viewcvs/gnustep?rev=39872&view=rev
Log:
Implement resource limits for regular expression evaluation. Tweaked
to roughly match the Cocoa behaviour, but can be changed through 
the GSRegularExpressionWorkLimit user default.

Modified:
    libs/base/trunk/ChangeLog
    libs/base/trunk/Headers/Foundation/NSRegularExpression.h
    libs/base/trunk/Source/NSRegularExpression.m
    libs/base/trunk/Tests/base/NSRegularExpression/basic.m

Modified: libs/base/trunk/ChangeLog
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/ChangeLog?rev=39872&r1=39871&r2=39872&view=diff
==============================================================================
--- libs/base/trunk/ChangeLog   (original)
+++ libs/base/trunk/ChangeLog   Fri Jun 17 11:04:04 2016
@@ -1,3 +1,13 @@
+2016-06-17  Niels Grewe <niels.gr...@halbordnung.de>
+
+       * Headers/Foundation/NSRegularExpression.h
+       * Source/NSRegularExpression.m
+       * Tests/base/NSRegularExpression/basic.m:
+
+       Implement resource limits for regular expression evaluation. Tweaked
+       to roughly match the Cocoa behaviour, but can be changed through 
+       the GSRegularExpressionWorkLimit user default.
+
 2016-06-17  Niels Grewe <niels.gr...@halbordnung.de>
 
        * Source/NSRegularExpression.m: Implement -isEqual: and -hash

Modified: libs/base/trunk/Headers/Foundation/NSRegularExpression.h
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/Headers/Foundation/NSRegularExpression.h?rev=39872&r1=39871&r2=39872&view=diff
==============================================================================
--- libs/base/trunk/Headers/Foundation/NSRegularExpression.h    (original)
+++ libs/base/trunk/Headers/Foundation/NSRegularExpression.h    Fri Jun 17 
11:04:04 2016
@@ -1,18 +1,19 @@
+
 /* Definition of class NSRegularExpression
    Copyright (C) 2011 Free Software Foundation, Inc.
-   
+
    This file is part of the GNUstep Library.
-   
+
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.
-   
+
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.
-   
+
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
@@ -72,7 +73,19 @@
 #ifndef GSREGEXTYPE
 #  define GSREGEXTYPE void
 #endif
-
+/**
+ * NSRegularExpression is used to inspect and manipulate strings using regular
+ * expressions. The interface is thread safe: The same NSRegularExpression
+ * object may be used to concurrently perform matching on multiple threads.
+ *
+ * To guard against regular expressions with extremely poor performance, the
+ * underlying matcher will abort after a certain number of steps. This is
+ * controlled using the GSRegularExpressionWorkLimit user default. The value of
+ * this default key represents the number of steps executed by the match 
engine,
+ * so it is only indirectly correlated with the time taken to execute the
+ * pattern, but it usually in the order of milliseconds. The preset 1500,
+ * setting value to 0 disables the work limit.
+ */
 @interface NSRegularExpression : NSObject <NSCoding, NSCopying>
 {
 #if    GS_EXPOSE(NSRegularExpression)
@@ -153,4 +166,3 @@
 #endif /* GS_API_MACOSX */
 
 #endif /* _NSRegualrExpression_h_GNUSTEP_BASE_INCLUDE */
-

Modified: libs/base/trunk/Source/NSRegularExpression.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/Source/NSRegularExpression.m?rev=39872&r1=39871&r2=39872&view=diff
==============================================================================
--- libs/base/trunk/Source/NSRegularExpression.m        (original)
+++ libs/base/trunk/Source/NSRegularExpression.m        Fri Jun 17 11:04:04 2016
@@ -46,6 +46,8 @@
 #import "Foundation/NSTextCheckingResult.h"
 #import "Foundation/NSArray.h"
 #import "Foundation/NSCoder.h"
+#import "Foundation/NSUserDefaults.h"
+#import "Foundation/NSNotification.h"
 
 
 /**
@@ -293,6 +295,48 @@
   return stop;
 }
 
+
+#define DEFAULT_WORK_LIMIT 1500
+/**
+ * The work limit specifies the number of iterations the matcher will do before
+ * aborting an operation. This ensures that degenerate pattern/input
+ * combinations don't send the application into what for all intents and
+ * purposes seems like an infinite loop.
+ */
+static int32_t _workLimit = DEFAULT_WORK_LIMIT;
+
++ (void) _defaultsChanged: (NSNotification*)n
+{
+  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
+  id value = [defs objectForKey: @"GSRegularExpressionWorkLimit"];
+  int32_t newLimit = DEFAULT_WORK_LIMIT;
+  if ([value respondsToSelector: @selector(intValue)])
+    {
+      int32_t v = [value intValue];
+      if (v >= 0)
+        {
+          newLimit = v;
+        }
+    }
+  _workLimit = newLimit;
+}
+
++ (void) initialize
+{
+  if (self == [NSRegularExpression class])
+    {
+      [[NSNotificationCenter defaultCenter]
+        addObserver: self
+           selector: @selector(_defaultsChanged:)
+              name: NSUserDefaultsDidChangeNotification
+            object: nil];
+      [self _defaultsChanged: nil];
+    }
+}
+
+
+
+
 /**
  * Sets up a libicu regex object for use.  Note: the documentation states that
  * NSRegularExpression must be thread safe.  To accomplish this, we store a
@@ -328,6 +372,7 @@
     {
       uregex_useTransparentBounds(r, TRUE, &s);
     }
+  uregex_setTimeLimit(r, _workLimit, &s);
   if (U_FAILURE(s))
     {
       uregex_close(r);
@@ -363,6 +408,7 @@
     {
       uregex_useTransparentBounds(r, TRUE, &s);
     }
+  uregex_setTimeLimit(r, _workLimit, &s);
   if (U_FAILURE(s))
     {
       uregex_close(r);

Modified: libs/base/trunk/Tests/base/NSRegularExpression/basic.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/Tests/base/NSRegularExpression/basic.m?rev=39872&r1=39871&r2=39872&view=diff
==============================================================================
--- libs/base/trunk/Tests/base/NSRegularExpression/basic.m      (original)
+++ libs/base/trunk/Tests/base/NSRegularExpression/basic.m      Fri Jun 17 
11:04:04 2016
@@ -1,22 +1,102 @@
+
 #import "ObjectTesting.h"
 #import <Foundation/NSAutoreleasePool.h>
 #import <Foundation/NSRegularExpression.h>
+#import <Foundation/NSDate.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSUserDefaults.h>
+#import <Foundation/NSRunLoop.h>
+#import <Foundation/NSThread.h>
+#import <Foundation/NSValue.h>
+
+@interface DegeneratePatternTest : NSObject
+{
+  NSRegularExpression *expression;
+  NSString* input;
+}
+@end
+
+@implementation DegeneratePatternTest
+
+- (instancetype) init
+{
+  if (nil == (self = [super init]))
+    {
+      return nil;
+    }
+  expression =
+    [[NSRegularExpression alloc] initWithPattern: 
@"^(([a-z])+.)+[A-Z]([a-z])+$"
+                                         options: 0
+                                          error: NULL];
+  ASSIGN(input, 
@"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!");
+  return self;
+}
+
+- (void) runTest: (id)obj
+{
+  NSAutoreleasePool *pool = [NSAutoreleasePool new];
+  [expression matchesInString: input
+                      options: 0
+                        range: NSMakeRange(0, [input length])];
+  DESTROY(pool);
+}
+
+- (void) dealloc
+{
+  DESTROY(expression);
+  DESTROY(input);
+  [super dealloc];
+}
+@end
+
 
 int main()
 {
   NSAutoreleasePool   *arp = [NSAutoreleasePool new];
+# ifdef GNUSTEP
+    // Ensure that a deterministic limit is set up for this process
+    NSUserDefaults *dflts = [NSUserDefaults standardUserDefaults];
+    NSDictionary *domain = [NSDictionary dictionaryWithObjectsAndKeys:
+     [NSNumber numberWithInt: 1500], @"GSRegularExpressionWorkLimit", nil];
+    [dflts setVolatileDomain: domain
+                     forName: @"GSTestDomain"];
+# endif
   id testObj = [[NSRegularExpression alloc] initWithPattern: @"^a"
                                                     options: 0
                                                       error: NULL];
 
   test_NSObject(@"NSRegularExpression",
-                [NSArray arrayWithObject: 
+                [NSArray arrayWithObject:
                   [[NSRegularExpression alloc] initWithPattern: @"^a"
                                                        options: 0
                                                          error: NULL]]);
   test_NSCopying(@"NSRegularExpression",@"NSRegularExpression",
                  [NSArray arrayWithObject:testObj],NO,NO);
-   
+
+
+  /* To test whether we correctly bail out of processing degenerate patterns,
+   * we spin up a new thread and evaluate an expression there. The expectation
+   * is that the thread should terminate within a few seconds.
+   *
+   * NOTE: Since we cannot terminate the thread in case of a failure, this
+   * test should be run last.
+   */
+  DegeneratePatternTest *test = [DegeneratePatternTest new];
+  NSThread *thread = [[NSThread alloc] initWithTarget: test
+                                              selector: @selector(runTest:)
+                                                object: nil];
+  [thread start];
+  [thread setName: @"PatternTestRunner"];
+  NSDate *started = [NSDate date];
+  NSRunLoop *rl = [NSRunLoop currentRunLoop];
+  /* We spin the runloop for a bit while we wait for the other thread to bail
+   * out */
+  while ([thread isExecuting] && abs([started timeIntervalSinceNow] < 10.0f))
+    {
+      [rl runMode: NSDefaultRunLoopMode
+       beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
+    }
+  PASS(NO == [thread isExecuting], "Faulty regular expression terminated");
   [arp release]; arp = nil;
   return 0;
 }


_______________________________________________
Gnustep-cvs mailing list
Gnustep-cvs@gna.org
https://mail.gna.org/listinfo/gnustep-cvs

Reply via email to