| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "testing/libfuzzer/fuzzer_support_ios/fuzzer_support.h" |
| |
| #import <UIKit/UIKit.h> |
| |
| // Springboard/Frontboard will kill any iOS/MacCatalyst app that fails to check |
| // in after launch within a given time. Starting a UIApplication before invoking |
| // fuzzer prevents this from happening. |
| |
| // Since the executable isn't likely to be a real iOS UI, the delegate puts up a |
| // window displaying the app name. If a bunch of apps using MainHook are being |
| // run in a row, this provides an indication of which one is currently running. |
| |
| static int g_argc; |
| static char** g_argv; |
| |
| namespace { |
| extern "C" int LLVMFuzzerRunDriver(int* argc, |
| char*** argv, |
| int (*UserCb)(const uint8_t* Data, |
| size_t Size)); |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); |
| |
| void PopulateUIWindow(UIWindow* window) { |
| [window setBackgroundColor:[UIColor whiteColor]]; |
| [window makeKeyAndVisible]; |
| CGRect bounds = [[UIScreen mainScreen] bounds]; |
| // Add a label with the app name. |
| UILabel* label = [[UILabel alloc] initWithFrame:bounds]; |
| label.text = [[NSProcessInfo processInfo] processName]; |
| label.textAlignment = NSTextAlignmentCenter; |
| [window addSubview:label]; |
| |
| // An NSInternalInconsistencyException is thrown if the app doesn't have a |
| // root view controller. Set an empty one here. |
| [window setRootViewController:[[UIViewController alloc] init]]; |
| } |
| } // namespace |
| |
| @interface UIApplication (Testing) |
| - (void)_terminateWithStatus:(int)status; |
| @end |
| |
| // No-op scene delegate for libFuzzer. Note that this is created along with |
| // the application delegate, so they need to be separate objects (the same |
| // object can't be both the app and scene delegate, since new scene delegates |
| // are created for each scene). |
| @interface ChromeLibFuzzerSceneDelegate : NSObject <UIWindowSceneDelegate> { |
| UIWindow* _window; |
| } |
| - (void)runFuzzer; |
| @end |
| |
| @interface ChromeLibFuzzerDelegate : NSObject { |
| } |
| @end |
| |
| @implementation ChromeLibFuzzerSceneDelegate |
| |
| - (void)scene:(UIScene*)scene |
| willConnectToSession:(UISceneSession*)session |
| options:(UISceneConnectionOptions*)connectionOptions |
| API_AVAILABLE(ios(13), macCatalyst(13.0)) { |
| _window = |
| [[UIWindow alloc] initWithWindowScene:static_cast<UIWindowScene*>(scene)]; |
| PopulateUIWindow(_window); |
| static dispatch_once_t once; |
| // Delay 0.3 seconds to allow NSMenuBarScene to be created and thus app won't |
| // be killed by the watchdog tracking that. |
| dispatch_once(&once, ^{ |
| [self performSelector:@selector(runFuzzer) withObject:nil afterDelay:0.3]; |
| }); |
| } |
| |
| - (void)sceneDidDisconnect:(UIScene*)scene |
| API_AVAILABLE(ios(13), macCatalyst(13.0)) { |
| _window = nil; |
| } |
| |
| - (void)runFuzzer { |
| int exitStatus = |
| LLVMFuzzerRunDriver(&g_argc, &g_argv, &LLVMFuzzerTestOneInput); |
| |
| // If a test app is too fast, it will exit before Instruments has has a |
| // a chance to initialize and no test results will be seen. |
| [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; |
| |
| // Use the hidden selector to try and cleanly take down the app (otherwise |
| // things can think the app crashed even on a zero exit status). |
| UIApplication* application = [UIApplication sharedApplication]; |
| [application _terminateWithStatus:exitStatus]; |
| |
| exit(exitStatus); |
| } |
| |
| @end |
| |
| @implementation ChromeLibFuzzerDelegate |
| |
| - (BOOL)application:(UIApplication*)application |
| didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { |
| return YES; |
| } |
| |
| @end |
| |
| namespace ios_fuzzer { |
| void RunFuzzerFromIOSApp(int argc, char* argv[]) { |
| g_argc = argc; |
| g_argv = argv; |
| @autoreleasepool { |
| int exit_status = |
| UIApplicationMain(g_argc, g_argv, nil, @"ChromeLibFuzzerDelegate"); |
| exit(exit_status); |
| } |
| } |
| |
| } // namespace ios_fuzzer |