xref: /aosp_15_r20/external/cronet/testing/libfuzzer/fuzzer_support_ios/fuzzer_support.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1// Copyright 2021 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "testing/libfuzzer/fuzzer_support_ios/fuzzer_support.h"
6
7#import <UIKit/UIKit.h>
8
9// Springboard/Frontboard will kill any iOS/MacCatalyst app that fails to check
10// in after launch within a given time. Starting a UIApplication before invoking
11// fuzzer prevents this from happening.
12
13// Since the executable isn't likely to be a real iOS UI, the delegate puts up a
14// window displaying the app name. If a bunch of apps using MainHook are being
15// run in a row, this provides an indication of which one is currently running.
16
17static int g_argc;
18static char** g_argv;
19
20namespace {
21extern "C" int LLVMFuzzerRunDriver(int* argc,
22                                   char*** argv,
23                                   int (*UserCb)(const uint8_t* Data,
24                                                 size_t Size));
25extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
26
27void PopulateUIWindow(UIWindow* window) {
28  [window setBackgroundColor:[UIColor whiteColor]];
29  [window makeKeyAndVisible];
30  CGRect bounds = [[UIScreen mainScreen] bounds];
31  // Add a label with the app name.
32  UILabel* label = [[UILabel alloc] initWithFrame:bounds];
33  label.text = [[NSProcessInfo processInfo] processName];
34  label.textAlignment = NSTextAlignmentCenter;
35  [window addSubview:label];
36
37  // An NSInternalInconsistencyException is thrown if the app doesn't have a
38  // root view controller. Set an empty one here.
39  [window setRootViewController:[[UIViewController alloc] init]];
40}
41}  // namespace
42
43@interface UIApplication (Testing)
44- (void)_terminateWithStatus:(int)status;
45@end
46
47// No-op scene delegate for libFuzzer. Note that this is created along with
48// the application delegate, so they need to be separate objects (the same
49// object can't be both the app and scene delegate, since new scene delegates
50// are created for each scene).
51@interface ChromeLibFuzzerSceneDelegate : NSObject <UIWindowSceneDelegate> {
52  UIWindow* _window;
53}
54- (void)runFuzzer;
55@end
56
57@interface ChromeLibFuzzerDelegate : NSObject {
58}
59@end
60
61@implementation ChromeLibFuzzerSceneDelegate
62
63- (void)scene:(UIScene*)scene
64    willConnectToSession:(UISceneSession*)session
65                 options:(UISceneConnectionOptions*)connectionOptions
66    API_AVAILABLE(ios(13), macCatalyst(13.0)) {
67  _window =
68      [[UIWindow alloc] initWithWindowScene:static_cast<UIWindowScene*>(scene)];
69  PopulateUIWindow(_window);
70  static dispatch_once_t once;
71  // Delay 0.3 seconds to allow NSMenuBarScene to be created and thus app won't
72  // be killed by the watchdog tracking that.
73  dispatch_once(&once, ^{
74    [self performSelector:@selector(runFuzzer) withObject:nil afterDelay:0.3];
75  });
76}
77
78- (void)sceneDidDisconnect:(UIScene*)scene
79    API_AVAILABLE(ios(13), macCatalyst(13.0)) {
80  _window = nil;
81}
82
83- (void)runFuzzer {
84  int exitStatus =
85      LLVMFuzzerRunDriver(&g_argc, &g_argv, &LLVMFuzzerTestOneInput);
86
87  // If a test app is too fast, it will exit before Instruments has has a
88  // a chance to initialize and no test results will be seen.
89  [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
90
91  // Use the hidden selector to try and cleanly take down the app (otherwise
92  // things can think the app crashed even on a zero exit status).
93  UIApplication* application = [UIApplication sharedApplication];
94  [application _terminateWithStatus:exitStatus];
95
96  exit(exitStatus);
97}
98
99@end
100
101@implementation ChromeLibFuzzerDelegate
102
103- (BOOL)application:(UIApplication*)application
104    didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
105  return YES;
106}
107
108@end
109
110namespace ios_fuzzer {
111void RunFuzzerFromIOSApp(int argc, char* argv[]) {
112  g_argc = argc;
113  g_argv = argv;
114  @autoreleasepool {
115    int exit_status =
116        UIApplicationMain(g_argc, g_argv, nil, @"ChromeLibFuzzerDelegate");
117    exit(exit_status);
118  }
119}
120
121}  // namespace ios_fuzzer
122