Changeset 270559 in webkit


Ignore:
Timestamp:
Dec 8, 2020 3:45:23 PM (3 years ago)
Author:
Chris Dumez
Message:

Potential crash under [WKRemoteObjectEncoder encodeObject:forKey:] when the object graph contains a cycle
https://bugs.webkit.org/show_bug.cgi?id=219620
Source/WebKit:

<rdar://71551776>

Reviewed by Geoffrey Garen.

Update WKRemoteObjectEncoder to detect cycles when encoding objects. When a cycle is detected, we
first attempt to encode a default-initialized object of the same type instead. If that fails, we
raise a NSInvalidArgumentException.

Based on crashes in the wild, we have evidence that such cycles are occuring and I suspect this is
caused by Norton Safe Web extension somehow.

  • Shared/API/Cocoa/WKRemoteObjectCoder.mm:

(-[WKRemoteObjectEncoder init]):
(encodeObject):

Tools:

Reviewed by Geoffrey Garen.

Add API test coverage.

  • TestWebKitAPI/Tests/WebKitCocoa/RemoteObjectRegistry.h:
  • TestWebKitAPI/Tests/WebKitCocoa/RemoteObjectRegistry.mm:

(TEST):

  • TestWebKitAPI/Tests/WebKitCocoa/RemoteObjectRegistryPlugIn.mm:

(-[RemoteObjectRegistryPlugIn takeDictionary:completionHandler:]):

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r270557 r270559  
     12020-12-08  Chris Dumez  <cdumez@apple.com>
     2
     3        Potential crash under [WKRemoteObjectEncoder encodeObject:forKey:] when the object graph contains a cycle
     4        https://bugs.webkit.org/show_bug.cgi?id=219620
     5        <rdar://71551776>
     6
     7        Reviewed by Geoffrey Garen.
     8
     9        Update WKRemoteObjectEncoder to detect cycles when encoding objects. When a cycle is detected, we
     10        first attempt to encode a default-initialized object of the same type instead. If that fails, we
     11        raise a NSInvalidArgumentException.
     12
     13        Based on crashes in the wild, we have evidence that such cycles are occuring and I suspect this is
     14        caused by Norton Safe Web extension somehow.
     15
     16        * Shared/API/Cocoa/WKRemoteObjectCoder.mm:
     17        (-[WKRemoteObjectEncoder init]):
     18        (encodeObject):
     19
    1202020-12-08  Simon Fraser  <simon.fraser@apple.com>
    221
  • trunk/Source/WebKit/Shared/API/Cocoa/WKRemoteObjectCoder.mm

    r263016 r270559  
    3232#import "APINumber.h"
    3333#import "APIString.h"
     34#import "Logging.h"
    3435#import "NSInvocationSPI.h"
    3536#import "_WKRemoteObjectInterfaceInternal.h"
    3637#import <objc/runtime.h>
    3738#import <wtf/RetainPtr.h>
     39#import <wtf/Scope.h>
    3840#import <wtf/SetForScope.h>
    3941#import <wtf/text/CString.h>
     
    6264
    6365    API::Dictionary* _currentDictionary;
     66    RetainPtr<NSMutableSet> _objectsBeingEncoded; // Used to detect cycles.
    6467}
    6568
     
    7174    _rootDictionary = API::Dictionary::create();
    7275    _currentDictionary = _rootDictionary.get();
     76    _objectsBeingEncoded = adoptNS([[NSMutableSet alloc] init]);
    7377
    7478    return self;
     
    305309    if (!objectClass)
    306310        [NSException raise:NSInvalidArgumentException format:@"-classForCoder returned nil for %@", object];
     311
     312    if ([encoder->_objectsBeingEncoded containsObject:object]) {
     313        RELEASE_LOG_FAULT(IPC, "WKRemoteObjectCode::encodeObject: Object of type '%{private}s' contains a cycle", class_getName(object_getClass(object)));
     314        @try {
     315            // Try to encode a newly initialized object instead.
     316            id newObject = [[[[object class] alloc] init] autorelease];
     317            object = newObject;
     318        } @catch (NSException *e) {
     319            [NSException raise:NSInvalidArgumentException format:@"Object of type '%s' contains a cycle", class_getName(object_getClass(object))];
     320        }
     321    }
     322
     323    [encoder->_objectsBeingEncoded addObject:object];
     324    auto exitScope = makeScopeExit([encoder, object] {
     325        [encoder->_objectsBeingEncoded removeObject:object];
     326    });
    307327
    308328    encoder->_currentDictionary->set(classNameKey, API::String::create(class_getName(objectClass)));
  • trunk/Tools/ChangeLog

    r270558 r270559  
     12020-12-08  Chris Dumez  <cdumez@apple.com>
     2
     3        Potential crash under [WKRemoteObjectEncoder encodeObject:forKey:] when the object graph contains a cycle
     4        https://bugs.webkit.org/show_bug.cgi?id=219620
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        Add API test coverage.
     9
     10        * TestWebKitAPI/Tests/WebKitCocoa/RemoteObjectRegistry.h:
     11        * TestWebKitAPI/Tests/WebKitCocoa/RemoteObjectRegistry.mm:
     12        (TEST):
     13        * TestWebKitAPI/Tests/WebKitCocoa/RemoteObjectRegistryPlugIn.mm:
     14        (-[RemoteObjectRegistryPlugIn takeDictionary:completionHandler:]):
     15
    1162020-12-08  Jonathan Bedard  <jbedard@apple.com>
    217
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RemoteObjectRegistry.h

    r258095 r270559  
    4949- (void)takeUnsignedLong:(unsigned long)value completionHandler:(void (^)(unsigned long value))completionHandler;
    5050- (void)takeLong:(long)value completionHandler:(void (^)(long value))completionHandler;
     51- (void)takeDictionary:(NSDictionary *)value completionHandler:(void (^)(NSDictionary *value))completionHandler;
    5152- (void)doNotCallCompletionHandler:(void (^)())completionHandler;
    5253- (void)sendRequest:(NSURLRequest *)request response:(NSURLResponse *)response challenge:(NSURLAuthenticationChallenge *)challenge error:(NSError *)error completionHandler:(void (^)(NSURLRequest *, NSURLResponse *, NSURLAuthenticationChallenge *, NSError *))completionHandler;
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RemoteObjectRegistry.mm

    r258095 r270559  
    127127        isDone = false;
    128128
    129         class DoneWhenDestroyed : public RefCounted<DoneWhenDestroyed> {
    130         public:
    131             DoneWhenDestroyed(bool& isDone)
    132                 : isDone(isDone) { }
    133             ~DoneWhenDestroyed() { isDone = true; }
    134         private:
    135             bool& isDone;
    136         };
    137 
    138         {
    139             RefPtr<DoneWhenDestroyed> doneWhenDestroyed = adoptRef(*new DoneWhenDestroyed(isDone));
    140             [object doNotCallCompletionHandler:[doneWhenDestroyed]() {
    141             }];
    142         }
    143 
    144         TestWebKitAPI::Util::run(&isDone);
    145 
    146129        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://webkit.org/"]];
    147130        NSHTTPURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://webkit.org/"] statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"testFieldName" : @"testFieldValue" }] autorelease];
     
    159142        }];
    160143        TestWebKitAPI::Util::run(&isDone);
     144
     145        isDone = false;
     146
     147        bool exceptionThrown = false;
     148        NSMutableDictionary *child = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"foo", @"name", [NSNumber numberWithInt:1], @"value", nil];
     149        NSMutableDictionary *dictionaryWithCycle = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"root", @"name", child, @"child", nil];
     150        [child setValue:dictionaryWithCycle forKey:@"parent"]; // Creates a cycle.
     151        @try {
     152            [object takeDictionary:dictionaryWithCycle completionHandler:^(NSDictionary* value) {
     153                NSString *name = [value objectForKey:@"name"];
     154                EXPECT_WK_STREQ(@"root", name);
     155                NSDictionary *child = [value objectForKey:@"child"];
     156                EXPECT_TRUE(!!child);
     157                NSString* childName = [child objectForKey:@"name"];
     158                EXPECT_WK_STREQ(@"foo", childName);
     159                NSNumber *childValue = [child objectForKey:@"value"];
     160                EXPECT_EQ(1, [childValue integerValue]);
     161                // We should have encoded parent as an empty NSDictionary.
     162                NSDictionary *childParent = [child objectForKey:@"parent"];
     163                EXPECT_TRUE(!!childParent);
     164                EXPECT_EQ(0U, [childParent count]);
     165                isDone = true;
     166            }];
     167            TestWebKitAPI::Util::run(&isDone);
     168            isDone = false;
     169        } @catch (NSException *e) {
     170            exceptionThrown = true;
     171        }
     172        EXPECT_FALSE(exceptionThrown);
     173
     174        class DoneWhenDestroyed : public RefCounted<DoneWhenDestroyed> {
     175        public:
     176            DoneWhenDestroyed(bool& isDone)
     177                : isDone(isDone) { }
     178            ~DoneWhenDestroyed() { isDone = true; }
     179        private:
     180            bool& isDone;
     181        };
     182
     183        {
     184            RefPtr<DoneWhenDestroyed> doneWhenDestroyed = adoptRef(*new DoneWhenDestroyed(isDone));
     185            [object doNotCallCompletionHandler:[doneWhenDestroyed]() {
     186            }];
     187        }
     188
     189        TestWebKitAPI::Util::run(&isDone);
    161190    }
    162191}
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RemoteObjectRegistryPlugIn.mm

    r258095 r270559  
    105105}
    106106
     107- (void)takeDictionary:(NSDictionary *)value completionHandler:(void (^)(NSDictionary *value))completionHandler
     108{
     109    completionHandler(value);
     110}
     111
    107112- (void)doNotCallCompletionHandler:(void (^)())completionHandler
    108113{
Note: See TracChangeset for help on using the changeset viewer.