1 | // RUN: %clangxx_asan -g -Wno-deprecated-declarations %s -o %t |
2 | // RUN: %env_asan_opts=exitcode=42 %run %t | FileCheck %s |
3 | |
4 | // Android doesn't have spawn.h or posix_spawn. |
5 | // UNSUPPORTED: android |
6 | |
7 | // CHECK: got expected 42 exit code |
8 | |
9 | #include <stdlib.h> |
10 | #include <stdio.h> |
11 | |
12 | #ifdef _WIN32 |
13 | #include <windows.h> |
14 | |
15 | int spawn_child(char **argv) { |
16 | // Set an environment variable to tell the child process to interrupt |
17 | // itself. |
18 | if (!SetEnvironmentVariableW(L"CRASH_FOR_TEST" , L"1" )) { |
19 | printf("SetEnvironmentVariableW failed (0x%8lx).\n" , GetLastError()); |
20 | fflush(stdout); |
21 | exit(1); |
22 | } |
23 | |
24 | STARTUPINFOW si; |
25 | memset(&si, 0, sizeof(si)); |
26 | si.cb = sizeof(si); |
27 | |
28 | PROCESS_INFORMATION pi; |
29 | memset(&pi, 0, sizeof(pi)); |
30 | |
31 | if (!CreateProcessW(nullptr, // No module name (use command line) |
32 | GetCommandLineW(), // Command line |
33 | nullptr, // Process handle not inheritable |
34 | nullptr, // Thread handle not inheritable |
35 | TRUE, // Set handle inheritance to TRUE |
36 | 0, // No flags |
37 | nullptr, // Use parent's environment block |
38 | nullptr, // Use parent's starting directory |
39 | &si, &pi)) { |
40 | printf("CreateProcess failed (0x%08lx).\n" , GetLastError()); |
41 | fflush(stdout); |
42 | exit(1); |
43 | } |
44 | |
45 | WaitForSingleObject(pi.hProcess, INFINITE); |
46 | |
47 | DWORD exit_code; |
48 | if (!GetExitCodeProcess(pi.hProcess, &exit_code)) { |
49 | printf("GetExitCodeProcess failed (0x%08lx).\n" , GetLastError()); |
50 | fflush(stdout); |
51 | exit(1); |
52 | } |
53 | |
54 | CloseHandle(pi.hProcess); |
55 | CloseHandle(pi.hThread); |
56 | |
57 | return exit_code; |
58 | } |
59 | #else |
60 | #include <spawn.h> |
61 | #include <errno.h> |
62 | #include <sys/wait.h> |
63 | |
64 | #if defined(__APPLE__) |
65 | #include <TargetConditionals.h> |
66 | #endif |
67 | |
68 | #if defined(__APPLE__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) |
69 | #define USE_NSGETENVIRON 1 |
70 | #else |
71 | #define USE_NSGETENVIRON 0 |
72 | #endif |
73 | |
74 | #if !USE_NSGETENVIRON |
75 | extern char **environ; |
76 | #else |
77 | #include <crt_externs.h> // _NSGetEnviron |
78 | #endif |
79 | |
80 | int spawn_child(char **argv) { |
81 | setenv(name: "CRASH_FOR_TEST" , value: "1" , replace: 1); |
82 | |
83 | #if !USE_NSGETENVIRON |
84 | char **envp = environ; |
85 | #else |
86 | char **envp = *_NSGetEnviron(); |
87 | #endif |
88 | |
89 | pid_t pid; |
90 | int err = posix_spawn(pid: &pid, path: argv[0], file_actions: nullptr, attrp: nullptr, argv: argv, envp: envp); |
91 | if (err) { |
92 | printf(format: "posix_spawn failed: %d\n" , err); |
93 | fflush(stdout); |
94 | exit(status: 1); |
95 | } |
96 | |
97 | // Wait until the child exits. |
98 | int status; |
99 | pid_t wait_result_pid; |
100 | do { |
101 | wait_result_pid = waitpid(pid: pid, stat_loc: &status, options: 0); |
102 | } while (wait_result_pid == -1 && errno == EINTR); |
103 | |
104 | if (wait_result_pid != pid || !WIFEXITED(status)) { |
105 | printf(format: "error in waitpid\n" ); |
106 | fflush(stdout); |
107 | exit(status: 1); |
108 | } |
109 | |
110 | // Return the exit status. |
111 | return WEXITSTATUS(status); |
112 | } |
113 | #endif |
114 | |
115 | int main(int argc, char **argv) { |
116 | int r = 0; |
117 | if (getenv(name: "CRASH_FOR_TEST" )) { |
118 | // Generate an asan report to test ASAN_OPTIONS=exitcode=42 |
119 | int *p = new int; |
120 | delete p; |
121 | r = *p; |
122 | } else { |
123 | int exit_code = spawn_child(argv); |
124 | if (exit_code == 42) { |
125 | printf(format: "got expected 42 exit code\n" ); |
126 | fflush(stdout); |
127 | } |
128 | } |
129 | return r; |
130 | } |
131 | |