Skip to content

Commit

Permalink
Generate serialization for NSError
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=264462
rdar://118155518

Reviewed by Brady Eidson.

* Source/WebKit/DerivedSources-input.xcfilelist:
* Source/WebKit/DerivedSources.make:
* Source/WebKit/Shared/Cocoa/ArgumentCodersCocoa.h:
* Source/WebKit/Shared/Cocoa/ArgumentCodersCocoa.mm:
(IPC::typeFromObject):
* Source/WebKit/Shared/Cocoa/CoreIPCError.h: Copied from Source/WebKit/Shared/Cocoa/CoreIPCNSCFObject.h.
(WebKit::CoreIPCError::CoreIPCError):
(WebKit::CoreIPCError::domain const):
(WebKit::CoreIPCError::code const):
(WebKit::CoreIPCError::userInfo const):
(WebKit::CoreIPCError::underlyingError const):
* Source/WebKit/Shared/Cocoa/CoreIPCError.mm: Added.
(WebKit::CoreIPCError::hasValidUserInfo):
(WebKit::CoreIPCError::toID const):
(WebKit::CoreIPCError::isSafeToEncodeUserInfo):
(WebKit::CoreIPCError::CoreIPCError):
* Source/WebKit/Shared/Cocoa/CoreIPCError.serialization.in: Added.
* Source/WebKit/Shared/Cocoa/CoreIPCNSCFObject.h:
* Source/WebKit/Shared/Cocoa/CoreIPCNSCFObject.mm:
(WebKit::valueFromID):
* Source/WebKit/Shared/mac/WebCoreArgumentCodersMac.mm:
(IPC::ArgumentCoder<WebCore::ResourceError>::encodePlatformData):
(IPC::ArgumentCoder<WebCore::ResourceError>::decodePlatformData):
(IPC::isSafeToEncodeUserInfo): Deleted.
(IPC::encodeNSError): Deleted.
(IPC::decodeNSError): Deleted.
* Source/WebKit/SourcesCocoa.txt:
* Source/WebKit/WebKit.xcodeproj/project.pbxproj:
* Tools/TestWebKitAPI/Tests/IPC/IPCSerialization.mm:
(createCertificate):
(createPrivateKey):
(TEST):

Canonical link: https://commits.webkit.org/270706@main
  • Loading branch information
sheeparegreat authored and beidson committed Nov 14, 2023
1 parent e36c143 commit 748284b
Show file tree
Hide file tree
Showing 13 changed files with 517 additions and 131 deletions.
1 change: 1 addition & 0 deletions Source/WebKit/DerivedSources-input.xcfilelist
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ $(PROJECT_DIR)/Shared/Cocoa/CoreIPCColor.serialization.in
$(PROJECT_DIR)/Shared/Cocoa/CoreIPCData.serialization.in
$(PROJECT_DIR)/Shared/Cocoa/CoreIPCDate.serialization.in
$(PROJECT_DIR)/Shared/Cocoa/CoreIPCDictionary.serialization.in
$(PROJECT_DIR)/Shared/Cocoa/CoreIPCError.serialization.in
$(PROJECT_DIR)/Shared/Cocoa/CoreIPCFont.serialization.in
$(PROJECT_DIR)/Shared/Cocoa/CoreIPCNSCFObject.serialization.in
$(PROJECT_DIR)/Shared/Cocoa/CoreIPCSecureCoding.serialization.in
Expand Down
1 change: 1 addition & 0 deletions Source/WebKit/DerivedSources.make
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ SERIALIZATION_DESCRIPTION_FILES = \
Shared/Cocoa/CoreIPCData.serialization.in \
Shared/Cocoa/CoreIPCDate.serialization.in \
Shared/Cocoa/CoreIPCDictionary.serialization.in \
Shared/Cocoa/CoreIPCError.serialization.in \
Shared/Cocoa/CoreIPCFont.serialization.in \
Shared/Cocoa/CoreIPCNSCFObject.serialization.in \
Shared/Cocoa/CoreIPCSecureCoding.serialization.in \
Expand Down
1 change: 1 addition & 0 deletions Source/WebKit/Shared/Cocoa/ArgumentCodersCocoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ enum class NSType : uint8_t {
Color,
Data,
Date,
Error,
Dictionary,
Font,
Number,
Expand Down
2 changes: 2 additions & 0 deletions Source/WebKit/Shared/Cocoa/ArgumentCodersCocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ NSType typeFromObject(id object)
return NSType::Data;
if ([object isKindOfClass:[NSDate class]])
return NSType::Date;
if ([object isKindOfClass:[NSError class]])
return NSType::Error;
if ([object isKindOfClass:[NSDictionary class]])
return NSType::Dictionary;
if ([object isKindOfClass:[CocoaFont class]])
Expand Down
87 changes: 87 additions & 0 deletions Source/WebKit/Shared/Cocoa/CoreIPCError.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

#if PLATFORM(COCOA)

#import <CoreFoundation/CoreFoundation.h>
#import <wtf/text/WTFString.h>

OBJC_CLASS NSError;

namespace WebKit {

class CoreIPCError {
WTF_MAKE_FAST_ALLOCATED;
public:
static bool hasValidUserInfo(const RetainPtr<CFDictionaryRef>&);

CoreIPCError(CoreIPCError&&) = default;
CoreIPCError& operator=(CoreIPCError&&) = default;

CoreIPCError(NSError *);
CoreIPCError(String&& domain, int64_t code, RetainPtr<CFDictionaryRef>&& userInfo, std::unique_ptr<CoreIPCError>&& underlyingError)
: m_domain(WTFMove(domain))
, m_code(WTFMove(code))
, m_userInfo(WTFMove(userInfo))
, m_underlyingError(WTFMove(underlyingError))
{
}

RetainPtr<id> toID() const;

String domain() const
{
return m_domain;
}

int64_t code() const
{
return m_code;
}

RetainPtr<CFDictionaryRef> userInfo() const
{
return m_userInfo;
}

const std::unique_ptr<CoreIPCError>& underlyingError() const
{
return m_underlyingError;
}

private:
bool isSafeToEncodeUserInfo(id value);

String m_domain;
int64_t m_code;
RetainPtr<CFDictionaryRef> m_userInfo;
std::unique_ptr<CoreIPCError> m_underlyingError;
};

}

#endif // PLATFORM(COCOA)
184 changes: 184 additions & 0 deletions Source/WebKit/Shared/Cocoa/CoreIPCError.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright (C) 2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#import "config.h"
#import "CoreIPCError.h"

#import <wtf/cocoa/TypeCastsCocoa.h>

namespace WebKit {

bool CoreIPCError::hasValidUserInfo(const RetainPtr<CFDictionaryRef>& userInfo)
{
NSDictionary * info = bridge_cast(userInfo.get());

if (id object = [info objectForKey:@"NSErrorClientCertificateChainKey"]) {
if (![object isKindOfClass:[NSArray class]])
return false;
for (id certificate in object) {
if ((CFGetTypeID((__bridge CFTypeRef)certificate) != SecCertificateGetTypeID()))
return false;
}
}

if (id peerCertificateChain = [info objectForKey:@"NSErrorPeerCertificateChainKey"]) {
for (id object in peerCertificateChain) {
if (CFGetTypeID((__bridge CFTypeRef)object) != SecCertificateGetTypeID())
return false;
}
}

if (SecTrustRef peerTrust = (__bridge SecTrustRef)[info objectForKey:NSURLErrorFailingURLPeerTrustErrorKey]) {
if (CFGetTypeID((__bridge CFTypeRef)peerTrust) != SecTrustGetTypeID())
return false;
}

if (id underlyingError = [info objectForKey:NSUnderlyingErrorKey]) {
if (![underlyingError isKindOfClass:[NSError class]])
return false;
}

return true;
}

RetainPtr<id> CoreIPCError::toID() const
{
if (m_underlyingError) {
auto underlyingNSError = m_underlyingError->toID();
if (!underlyingNSError)
return nil;

auto mutableUserInfo = adoptCF(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(m_userInfo.get()) + 1, m_userInfo.get()));
CFDictionarySetValue(mutableUserInfo.get(), (__bridge CFStringRef)NSUnderlyingErrorKey, (__bridge CFTypeRef)underlyingNSError.get());
return adoptNS([[NSError alloc] initWithDomain:m_domain code:m_code userInfo:(__bridge NSDictionary *)mutableUserInfo.get()]);
}
return adoptNS([[NSError alloc] initWithDomain:m_domain code:m_code userInfo:(__bridge NSDictionary *)m_userInfo.get()]);
}

bool CoreIPCError::isSafeToEncodeUserInfo(id value)
{
if ([value isKindOfClass:NSString.class] || [value isKindOfClass:NSURL.class] || [value isKindOfClass:NSNumber.class])
return true;

if (auto array = dynamic_objc_cast<NSArray>(value)) {
for (id object in array) {
if (!isSafeToEncodeUserInfo(object))
return false;
}
return true;
}

if (auto dictionary = dynamic_objc_cast<NSDictionary>(value)) {
for (id innerValue in dictionary.objectEnumerator) {
if (!isSafeToEncodeUserInfo(innerValue))
return false;
}
return true;
}

return false;
}

CoreIPCError::CoreIPCError(NSError *nsError)
: m_domain([nsError domain])
, m_code([nsError code])
{
NSDictionary *userInfo = [nsError userInfo];

RetainPtr<CFMutableDictionaryRef> filteredUserInfo = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, userInfo.count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));

[userInfo enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL*) {
if ([key isEqualToString:@"NSErrorClientCertificateChainKey"]) {
if (![value isKindOfClass:[NSArray class]])
return;
}
if (isSafeToEncodeUserInfo(value))
CFDictionarySetValue(filteredUserInfo.get(), (__bridge CFTypeRef)key, (__bridge CFTypeRef)value);
}];

if (NSArray *clientIdentityAndCertificates = [userInfo objectForKey:@"NSErrorClientCertificateChainKey"]) {
if ([clientIdentityAndCertificates isKindOfClass:[NSArray class]]) {
// Turn SecIdentity members into SecCertificate to strip out private key information.
id clientCertificates = [NSMutableArray arrayWithCapacity:clientIdentityAndCertificates.count];
for (id object in clientIdentityAndCertificates) {
// Only SecIdentity or SecCertificate types are expected in clientIdentityAndCertificates
if (CFGetTypeID((__bridge CFTypeRef)object) != SecIdentityGetTypeID() && CFGetTypeID((__bridge CFTypeRef)object) != SecCertificateGetTypeID())
continue;
if (CFGetTypeID((__bridge CFTypeRef)object) != SecIdentityGetTypeID()) {
[clientCertificates addObject:object];
continue;
}
SecCertificateRef certificate = nil;
OSStatus status = SecIdentityCopyCertificate((SecIdentityRef)object, &certificate);
RetainPtr<SecCertificateRef> retainCertificate = adoptCF(certificate);
// The SecIdentity member is the key information of this attribute. Without it, we should nil
// the attribute.
if (status != errSecSuccess) {
LOG_ERROR("Failed to encode nsError.userInfo[NSErrorClientCertificateChainKey]: %d", status);
clientCertificates = nil;
break;
}
[clientCertificates addObject:(__bridge id)certificate];
}
CFDictionarySetValue(filteredUserInfo.get(), CFSTR("NSErrorClientCertificateChainKey"), (__bridge CFTypeRef)clientCertificates);
} else
LOG_ERROR("NSErrorClientCertificateChainKey's value is not an NSArray");

}

id peerCertificateChain = [userInfo objectForKey:@"NSErrorPeerCertificateChainKey"];
if (!peerCertificateChain) {
if (id candidatePeerTrust = [userInfo objectForKey:NSURLErrorFailingURLPeerTrustErrorKey]) {
if (CFGetTypeID((__bridge CFTypeRef)candidatePeerTrust) == SecTrustGetTypeID())
peerCertificateChain = (__bridge NSArray *)adoptCF(SecTrustCopyCertificateChain((__bridge SecTrustRef)candidatePeerTrust)).autorelease();
}
}

if (peerCertificateChain && [peerCertificateChain isKindOfClass:[NSArray class]]) {
bool hasExpectedTypes = true;
for (id object in peerCertificateChain) {
if (CFGetTypeID((__bridge CFTypeRef)object) != SecCertificateGetTypeID()) {
hasExpectedTypes = false;
break;
}
}
if (hasExpectedTypes)
CFDictionarySetValue(filteredUserInfo.get(), CFSTR("NSErrorPeerCertificateChainKey"), (__bridge CFTypeRef)peerCertificateChain);
}

if (SecTrustRef peerTrust = (__bridge SecTrustRef)[userInfo objectForKey:NSURLErrorFailingURLPeerTrustErrorKey]) {
if (CFGetTypeID((__bridge CFTypeRef)peerTrust) == SecTrustGetTypeID())
CFDictionarySetValue(filteredUserInfo.get(), (__bridge CFStringRef)NSURLErrorFailingURLPeerTrustErrorKey, peerTrust);
}

m_userInfo = static_cast<CFDictionaryRef>(filteredUserInfo.get());

if (id underlyingError = [userInfo objectForKey:NSUnderlyingErrorKey]) {
if ([underlyingError isKindOfClass:[NSError class]])
m_underlyingError = makeUnique<CoreIPCError>(underlyingError);
}
}

} // namespace WebKit
34 changes: 34 additions & 0 deletions Source/WebKit/Shared/Cocoa/CoreIPCError.serialization.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (C) 2023 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#if PLATFORM(COCOA)

webkit_platform_headers: "CoreIPCError.h" "ArgumentCodersCF.h"

[WebKitPlatform, AdditionalEncoder=StreamConnectionEncoder] class WebKit::CoreIPCError {
String domain();
int64_t code();
[Validator='WebKit::CoreIPCError::hasValidUserInfo(*userInfo)'] RetainPtr<CFDictionaryRef> userInfo();
std::unique_ptr<WebKit::CoreIPCError> underlyingError();
}

#endif // PLATFORM(COCOA)
2 changes: 2 additions & 0 deletions Source/WebKit/Shared/Cocoa/CoreIPCNSCFObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "CoreIPCData.h"
#include "CoreIPCDate.h"
#include "CoreIPCDictionary.h"
#include "CoreIPCError.h"
#include "CoreIPCFont.h"
#include "CoreIPCNumber.h"
#include "CoreIPCSecureCoding.h"
Expand All @@ -53,6 +54,7 @@ class CoreIPCNSCFObject {
CoreIPCData,
CoreIPCDate,
CoreIPCDictionary,
CoreIPCError,
CoreIPCFont,
CoreIPCNumber,
CoreIPCSecureCoding,
Expand Down
2 changes: 2 additions & 0 deletions Source/WebKit/Shared/Cocoa/CoreIPCNSCFObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
return CoreIPCDate(bridge_cast((NSDate *)object));
case IPC::NSType::Dictionary:
return CoreIPCDictionary((NSDictionary *)object);
case IPC::NSType::Error:
return CoreIPCError((NSError *)object);
case IPC::NSType::Font:
return CoreIPCFont((WebCore::CocoaFont *)object);
case IPC::NSType::Number:
Expand Down
Loading

0 comments on commit 748284b

Please sign in to comment.