1 // RUN: %clangxx_asan -g %stdcxx11 -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 
spawn_child(char ** argv)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 
spawn_child(char ** argv)80 int spawn_child(char **argv) {
81   setenv("CRASH_FOR_TEST", "1", 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, argv[0], nullptr, nullptr, argv, envp);
91   if (err) {
92     printf("posix_spawn failed: %d\n", err);
93     fflush(stdout);
94     exit(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, &status, 0);
102   } while (wait_result_pid == -1 && errno == EINTR);
103 
104   if (wait_result_pid != pid || !WIFEXITED(status)) {
105     printf("error in waitpid\n");
106     fflush(stdout);
107     exit(1);
108   }
109 
110   // Return the exit status.
111   return WEXITSTATUS(status);
112 }
113 #endif
114 
main(int argc,char ** argv)115 int main(int argc, char **argv) {
116   int r = 0;
117   if (getenv("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("got expected 42 exit code\n");
126       fflush(stdout);
127     }
128   }
129   return r;
130 }
131