Diff
Modified: trunk/Source/_javascript_Core/API/JSScript.mm (245563 => 245564)
--- trunk/Source/_javascript_Core/API/JSScript.mm 2019-05-21 06:31:06 UTC (rev 245563)
+++ trunk/Source/_javascript_Core/API/JSScript.mm 2019-05-21 06:52:17 UTC (rev 245564)
@@ -38,8 +38,10 @@
#import "ParserError.h"
#import "Symbol.h"
#include <sys/stat.h>
+#include <wtf/FileMetadata.h>
#include <wtf/FileSystem.h>
#include <wtf/Scope.h>
+#include <wtf/spi/darwin/DataVaultSPI.h>
#if JSC_OBJC_API_ENABLED
@@ -60,9 +62,52 @@
return nil;
}
+static bool validateBytecodeCachePath(NSURL* cachePath, NSError** error)
+{
+ if (!cachePath)
+ return true;
+
+ URL cachePathURL([cachePath absoluteURL]);
+ if (!cachePathURL.isLocalFile()) {
+ createError([NSString stringWithFormat:@"Cache path `%@` is not a local file", static_cast<NSString *>(cachePathURL)], error);
+ return false;
+ }
+
+ String systemPath = cachePathURL.fileSystemPath();
+
+ if (auto metadata = FileSystem::fileMetadata(systemPath)) {
+ if (metadata->type != FileMetadata::Type::File) {
+ createError([NSString stringWithFormat:@"Cache path `%s` already exists and is not a file", systemPath.utf8().data()], error);
+ return false;
+ }
+ }
+
+ String directory = FileSystem::directoryName(systemPath);
+ if (directory.isNull()) {
+ createError([NSString stringWithFormat:@"Cache path `%s` does not contain in a valid directory", systemPath.utf8().data()], error);
+ return false;
+ }
+
+ if (!FileSystem::fileIsDirectory(directory, FileSystem::ShouldFollowSymbolicLinks::No)) {
+ createError([NSString stringWithFormat:@"Cache directory `%s` is not a directory or does not exist", directory.utf8().data()], error);
+ return false;
+ }
+
+#if USE(APPLE_INTERNAL_SDK)
+ if (rootless_check_datavault_flag(FileSystem::fileSystemRepresentation(directory).data(), nullptr)) {
+ createError([NSString stringWithFormat:@"Cache directory `%s` is not a data vault", directory.utf8().data()], error);
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+ (instancetype)scriptOfType:(JSScriptType)type withSource:(NSString *)source andSourceURL:(NSURL *)sourceURL andBytecodeCache:(NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError **)error
{
- UNUSED_PARAM(error);
+ if (!validateBytecodeCachePath(cachePath, error))
+ return nil;
+
JSScript *result = [[[JSScript alloc] init] autorelease];
result->m_virtualMachine = vm;
result->m_type = type;
@@ -75,7 +120,9 @@
+ (instancetype)scriptOfType:(JSScriptType)type memoryMappedFromASCIIFile:(NSURL *)filePath withSourceURL:(NSURL *)sourceURL andBytecodeCache:(NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError **)error
{
- UNUSED_PARAM(error);
+ if (!validateBytecodeCachePath(cachePath, error))
+ return nil;
+
URL filePathURL([filePath absoluteURL]);
if (!filePathURL.isLocalFile())
return createError([NSString stringWithFormat:@"File path %@ is not a local file", static_cast<NSString *>(filePathURL)], error);
Modified: trunk/Source/_javascript_Core/API/tests/testapi.mm (245563 => 245564)
--- trunk/Source/_javascript_Core/API/tests/testapi.mm 2019-05-21 06:31:06 UTC (rev 245563)
+++ trunk/Source/_javascript_Core/API/tests/testapi.mm 2019-05-21 06:52:17 UTC (rev 245564)
@@ -40,6 +40,7 @@
#import "JSWrapperMapTests.h"
#import "Regress141275.h"
#import "Regress141809.h"
+#import <wtf/spi/darwin/DataVaultSPI.h>
#if __has_include(<libproc.h>)
#define HAS_LIBPROC 1
@@ -2076,6 +2077,27 @@
return [tempDirectory URLByAppendingPathComponent:string];
}
+static NSURL* cacheFileInDataVault(NSString* name)
+{
+#if USE(APPLE_INTERNAL_SDK)
+ static NSURL* dataVaultURL;
+ static dispatch_once_t once;
+ dispatch_once(&once, ^{
+ char userDir[PATH_MAX];
+ RELEASE_ASSERT(confstr(_CS_DARWIN_USER_DIR, userDir, sizeof(userDir)));
+
+ NSString *userDirPath = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:userDir length:strlen(userDir)];
+ dataVaultURL = [NSURL fileURLWithPath:userDirPath isDirectory:YES];
+ dataVaultURL = [dataVaultURL URLByAppendingPathComponent:@"_javascript_Core" isDirectory:YES];
+ rootless_mkdir_datavault(dataVaultURL.path.UTF8String, 0700, "_javascript_Core");
+ });
+
+ return [dataVaultURL URLByAppendingPathComponent:name isDirectory:NO];
+#else
+ return tempFile(name);
+#endif
+}
+
static void testModuleBytecodeCache()
{
@autoreleasepool {
@@ -2087,9 +2109,9 @@
NSURL *barPath = tempFile(@"bar.js");
NSURL *bazPath = tempFile(@"baz.js");
- NSURL *fooCachePath = tempFile(@"foo.js.cache");
- NSURL *barCachePath = tempFile(@"bar.js.cache");
- NSURL *bazCachePath = tempFile(@"baz.js.cache");
+ NSURL *fooCachePath = cacheFileInDataVault(@"foo.js.cache");
+ NSURL *barCachePath = cacheFileInDataVault(@"bar.js.cache");
+ NSURL *bazCachePath = cacheFileInDataVault(@"baz.js.cache");
NSURL *fooFakePath = [NSURL fileURLWithPath:@"/foo.js"];
NSURL *barFakePath = [NSURL fileURLWithPath:@"/directory/bar.js"];
@@ -2110,7 +2132,8 @@
script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:bazPath withSourceURL:bazFakePath andBytecodeCache:bazCachePath inVirtualMachine:context.virtualMachine error:nil];
if (script) {
- if (![script cacheBytecodeWithError:nil])
+ NSError *error = nil;
+ if (![script cacheBytecodeWithError:&error])
CRASH();
[resolve callWithArguments:@[script]];
} else
@@ -2142,7 +2165,7 @@
{
@autoreleasepool {
NSString *fooSource = @"function foo() { return 42; }; function bar() { return 40; }; foo() + bar();";
- NSURL *fooCachePath = tempFile(@"foo.js.cache");
+ NSURL *fooCachePath = cacheFileInDataVault(@"foo.js.cache");
JSContext *context = [[JSContext alloc] init];
JSScript *script = [JSScript scriptOfType:kJSScriptTypeProgram withSource:fooSource andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:fooCachePath inVirtualMachine:context.virtualMachine error:nil];
RELEASE_ASSERT(script);
@@ -2166,7 +2189,7 @@
{
@autoreleasepool {
NSString *fooSource = @"this is a syntax error";
- NSURL *fooCachePath = tempFile(@"foo.js.cache");
+ NSURL *fooCachePath = cacheFileInDataVault(@"foo.js.cache");
JSContext *context = [[JSContext alloc] init];
JSScript *script = [JSScript scriptOfType:type withSource:fooSource andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:fooCachePath inVirtualMachine:context.virtualMachine error:nil];
RELEASE_ASSERT(script);
@@ -2180,7 +2203,8 @@
static void testBytecodeCacheWithSameCacheFileAndDifferentScript(bool forceDiskCache)
{
- NSURL *cachePath = tempFile(@"cachePath.cache");
+
+ NSURL *cachePath = cacheFileInDataVault(@"cachePath.cache");
NSURL *sourceURL = [NSURL URLWithString:@"my-path"];
@autoreleasepool {
@@ -2220,6 +2244,7 @@
NSFileManager* fileManager = [NSFileManager defaultManager];
BOOL removedAll = [fileManager removeItemAtURL:cachePath error:nil];
checkResult(@"Removed all temp files created", removedAll);
+
}
static void testProgramJSScriptException()
@@ -2245,7 +2270,7 @@
static void testCacheFileFailsWhenItsAlreadyCached()
{
- NSURL* cachePath = tempFile(@"foo.program.cache");
+ NSURL* cachePath = cacheFileInDataVault(@"foo.program.cache");
NSURL* sourceURL = [NSURL URLWithString:@"my-path"];
NSString *source = @"function foo() { return 42; } foo();";
@@ -2285,7 +2310,7 @@
NSMutableArray *scripts = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < 10000; ++i)
- [cachePaths addObject:tempFile([NSString stringWithFormat:@"cache-%d.cache", i])];
+ [cachePaths addObject:cacheFileInDataVault([NSString stringWithFormat:@"cache-%d.cache", i])];
JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
bool cachedAll = true;
@@ -2323,7 +2348,7 @@
static void testIsUsingBytecodeCacheAccessor()
{
- NSURL* cachePath = tempFile(@"foo.program.cache");
+ NSURL* cachePath = cacheFileInDataVault(@"foo.program.cache");
NSURL* sourceURL = [NSURL URLWithString:@"my-path"];
NSString *source = @"function foo() { return 1337; } foo();";
@@ -2356,6 +2381,44 @@
checkResult(@"Successfully removed cache file", removedAll);
}
+static void testBytecodeCacheValidation()
+{
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+
+ auto testInvalidCacheURL = [&](NSURL* cacheURL, NSString* expectedErrorMessage)
+ {
+ NSError* error;
+ JSScript *script = [JSScript scriptOfType:kJSScriptTypeProgram withSource:@"" andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:cacheURL inVirtualMachine:context.virtualMachine error:&error];
+ RELEASE_ASSERT(!script);
+ RELEASE_ASSERT(error);
+ NSString* testDesciption = [NSString stringWithFormat:@"Cache path validation for `%@` fails with message `%@`", cacheURL.absoluteString, expectedErrorMessage];
+ checkResult(testDesciption, [error.description containsString:expectedErrorMessage]);
+ };
+
+ testInvalidCacheURL([NSURL URLWithString:@""], @"Cache path `` is not a local file");
+ testInvalidCacheURL([NSURL URLWithString:@"file:///"], @"Cache path `/` already exists and is not a file");
+ testInvalidCacheURL([NSURL URLWithString:@"file:///a/b/c/d/e"], @"Cache directory `/a/b/c/d` is not a directory or does not exist");
+ testInvalidCacheURL([NSURL URLWithString:@"file:///private/tmp/file.cache"], @"Cache directory `/private/tmp` is not a data vault");
+ }
+
+#if USE(APPLE_INTERNAL_SDK)
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+
+ auto testValidCacheURL = [&](NSURL* cacheURL)
+ {
+ NSError* error;
+ JSScript *script = [JSScript scriptOfType:kJSScriptTypeProgram withSource:@"" andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:cacheURL inVirtualMachine:context.virtualMachine error:&error];
+ NSString* testDesciption = [NSString stringWithFormat:@"Cache path validation for `%@` passed", cacheURL.absoluteString];
+ checkResult(testDesciption, script && !error);
+ };
+
+ testValidCacheURL(cacheFileInDataVault(@"file.cache"));
+ }
+#endif
+}
+
@interface JSContextFileLoaderDelegate : JSContext <JSModuleLoaderDelegate>
+ (instancetype)newContext;
@@ -2628,6 +2691,7 @@
RUN(testCacheFileFailsWhenItsAlreadyCached());
RUN(testCanCacheManyFilesWithTheSameVM());
RUN(testIsUsingBytecodeCacheAccessor());
+ RUN(testBytecodeCacheValidation());
RUN(testLoaderRejectsNilScriptURL());
RUN(testLoaderRejectsFailedFetch());
Modified: trunk/Source/_javascript_Core/ChangeLog (245563 => 245564)
--- trunk/Source/_javascript_Core/ChangeLog 2019-05-21 06:31:06 UTC (rev 245563)
+++ trunk/Source/_javascript_Core/ChangeLog 2019-05-21 06:52:17 UTC (rev 245564)
@@ -1,5 +1,35 @@
2019-05-20 Tadeu Zagallo <[email protected]>
+ Only cache bytecode for API clients in data vaults
+ https://bugs.webkit.org/show_bug.cgi?id=197898
+ <rdar://problem/45945449>
+
+ Reviewed by Keith Miller.
+
+ Enforce that API clients only store cached bytecode in data vaults. This prevents
+ another process from compromising the current one by tampering with the bytecode.
+
+ * API/JSScript.mm:
+ (validateBytecodeCachePath):
+ (+[JSScript scriptOfType:withSource:andSourceURL:andBytecodeCache:inVirtualMachine:error:]):
+ (+[JSScript scriptOfType:memoryMappedFromASCIIFile:withSourceURL:andBytecodeCache:inVirtualMachine:error:]):
+ * API/tests/testapi.mm:
+ (cacheFileInDataVault):
+ (testModuleBytecodeCache):
+ (testProgramBytecodeCache):
+ (testBytecodeCacheWithSyntaxError):
+ (testBytecodeCacheWithSameCacheFileAndDifferentScript):
+ (testCacheFileFailsWhenItsAlreadyCached):
+ (testCanCacheManyFilesWithTheSameVM):
+ (testIsUsingBytecodeCacheAccessor):
+ (testBytecodeCacheValidation):
+ (testObjectiveCAPI):
+ * Configurations/ToolExecutable.xcconfig:
+ * _javascript_Core.xcodeproj/project.pbxproj:
+ * testapi.entitlements: Added.
+
+2019-05-20 Tadeu Zagallo <[email protected]>
+
Fix 32-bit btyecode cache crashes
https://bugs.webkit.org/show_bug.cgi?id=198035
<rdar://problem/49905560>
Modified: trunk/Source/_javascript_Core/Configurations/ToolExecutable.xcconfig (245563 => 245564)
--- trunk/Source/_javascript_Core/Configurations/ToolExecutable.xcconfig 2019-05-21 06:31:06 UTC (rev 245563)
+++ trunk/Source/_javascript_Core/Configurations/ToolExecutable.xcconfig 2019-05-21 06:52:17 UTC (rev 245564)
@@ -21,11 +21,27 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include? "../../../../Internal/Configurations/HaveInternalSDK.xcconfig"
#include "FeatureDefines.xcconfig"
#include "Version.xcconfig"
INSTALL_PATH = $(_javascript_CORE_FRAMEWORKS_DIR)/$(_javascript_CORE_RESOURCES_DIR);
PRODUCT_NAME = $(TARGET_NAME);
+
+USE_INTERNAL_SDK = $(USE_INTERNAL_SDK_$(CONFIGURATION));
+USE_INTERNAL_SDK_Production = YES;
+USE_INTERNAL_SDK_Debug = $(HAVE_INTERNAL_SDK);
+USE_INTERNAL_SDK_Release = $(HAVE_INTERNAL_SDK);
+
+CODE_SIGN_IDENTITY[sdk=macosx*] = $(CODE_SIGN_IDENTITY_$(USE_INTERNAL_SDK))
+
+CODE_SIGN_IDENTITY_ = $(CODE_SIGN_IDENTITY_NO);
+CODE_SIGN_IDENTITY_NO = -;
+CODE_SIGN_IDENTITY_YES = $(WK_ENGINEERING_CODE_SIGN_IDENTITY);
+
+CODE_SIGN_ENTITLEMENTS[sdk=macosx*] = $(CODE_SIGN_ENTITLEMENTS_macosx_$(TARGET_NAME));
+CODE_SIGN_ENTITLEMENTS_macosx_testapi = testapi.entitlements;
+
CODE_SIGN_ENTITLEMENTS[sdk=iphone*] = $(CODE_SIGN_ENTITLEMENTS_ios_$(TARGET_NAME));
CODE_SIGN_ENTITLEMENTS_ios_minidom = entitlements.plist;
CODE_SIGN_ENTITLEMENTS_ios_testair = entitlements.plist;
Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (245563 => 245564)
--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2019-05-21 06:31:06 UTC (rev 245563)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2019-05-21 06:52:17 UTC (rev 245564)
@@ -3212,6 +3212,7 @@
1442565F15EDE98D0066A49B /* JSWithScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSWithScope.cpp; sourceTree = "<group>"; };
1442566015EDE98D0066A49B /* JSWithScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSWithScope.h; sourceTree = "<group>"; };
144CA34F221F037900817789 /* CachedBytecode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedBytecode.h; sourceTree = "<group>"; };
+ 145348572291737F00B1C2EB /* testapi.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = testapi.entitlements; sourceTree = "<group>"; };
145722851437E140005FDE26 /* StrongInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StrongInlines.h; sourceTree = "<group>"; };
145C507F0D9DF63B0088F6B9 /* CallData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallData.h; sourceTree = "<group>"; };
146AAB2A0B66A84900E55F16 /* JSStringRefCF.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSStringRefCF.h; sourceTree = "<group>"; };
@@ -5177,6 +5178,7 @@
A7C225CC139981F100FF1662 /* KeywordLookupGenerator.py */,
79D7B0E221152FD300FE7C64 /* allow-jit-macOS.entitlements */,
79D7B0E121152FD200FE7C64 /* entitlements.plist */,
+ 145348572291737F00B1C2EB /* testapi.entitlements */,
1432EBD70A34CAD400717B9F /* API */,
9688CB120ED12B4E001D649F /* assembler */,
0FEC84B21BDACD5E0080FF74 /* b3 */,
Added: trunk/Source/_javascript_Core/testapi.entitlements (0 => 245564)
--- trunk/Source/_javascript_Core/testapi.entitlements (rev 0)
+++ trunk/Source/_javascript_Core/testapi.entitlements 2019-05-21 06:52:17 UTC (rev 245564)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.cs.allow-jit</key>
+ <true/>
+ <key>com.apple.rootless.storage._javascript_Core</key>
+ <true/>
+</dict>
+</plist>
Modified: trunk/Source/WTF/ChangeLog (245563 => 245564)
--- trunk/Source/WTF/ChangeLog 2019-05-21 06:31:06 UTC (rev 245563)
+++ trunk/Source/WTF/ChangeLog 2019-05-21 06:52:17 UTC (rev 245564)
@@ -1,3 +1,15 @@
+2019-05-20 Tadeu Zagallo <[email protected]>
+
+ Only cache bytecode for API clients in data vaults
+ https://bugs.webkit.org/show_bug.cgi?id=197898
+
+ Reviewed by Keith Miller.
+
+ Add SPI to check if a filesystem path is restricted as a data vault.
+
+ * WTF.xcodeproj/project.pbxproj:
+ * wtf/spi/darwin/DataVaultSPI.h: Added.
+
2019-05-20 Carlos Garcia Campos <[email protected]>
[GLIB] Repeating timer is not stopped when stop is called from the callback
Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (245563 => 245564)
--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2019-05-21 06:31:06 UTC (rev 245563)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2019-05-21 06:52:17 UTC (rev 245564)
@@ -288,6 +288,7 @@
1469419416EAAFF80024E146 /* SchedulePair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SchedulePair.h; sourceTree = "<group>"; };
1469419A16EAB10A0024E146 /* AutodrainedPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutodrainedPool.h; sourceTree = "<group>"; };
1469419B16EAB10A0024E146 /* AutodrainedPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AutodrainedPool.cpp; sourceTree = "<group>"; };
+ 14933E21228C22DF00F79E46 /* DataVaultSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataVaultSPI.h; sourceTree = "<group>"; };
149EF16216BBFE0D000A4331 /* TriState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TriState.h; sourceTree = "<group>"; };
14E785E71DFB330100209BD1 /* OrdinalNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OrdinalNumber.h; sourceTree = "<group>"; };
14F3B0F615E45E4600210069 /* SaturatedArithmetic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SaturatedArithmetic.h; sourceTree = "<group>"; };
@@ -1363,6 +1364,7 @@
CE73E02319DCB7AB00580D5C /* darwin */ = {
isa = PBXGroup;
children = (
+ 14933E21228C22DF00F79E46 /* DataVaultSPI.h */,
E431CC4A21187ADB000C8A07 /* DispatchSPI.h */,
93DDE9311CDC052D00FD3491 /* dyldSPI.h */,
A5098AFF1C169E0700087797 /* SandboxSPI.h */,
Added: trunk/Source/WTF/wtf/spi/darwin/DataVaultSPI.h (0 => 245564)
--- trunk/Source/WTF/wtf/spi/darwin/DataVaultSPI.h (rev 0)
+++ trunk/Source/WTF/wtf/spi/darwin/DataVaultSPI.h 2019-05-21 06:52:17 UTC (rev 245564)
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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. ``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
+ * 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 OS(DARWIN)
+
+#if USE(APPLE_INTERNAL_SDK)
+#import <rootless.h>
+#endif
+
+WTF_EXTERN_C_BEGIN
+
+int rootless_check_datavault_flag(const char *path, const char *storage_class);
+
+WTF_EXTERN_C_END
+
+#endif // OS(DARWIN)