source: trunk/base/src/darwintracelib1.0/proc.c @ 126475

Last change on this file since 126475 was 126475, checked in by cal@…, 6 years ago

base: tracelib: don't crash on posix_spawn with NULL envp, #45318

  • Property svn:eol-style set to native
File size: 11.0 KB
Line 
1/*
2 * Copyright (c) 2005 Apple Inc. All rights reserved.
3 * Copyright (c) 2005-2006 Paul Guyot <pguyot@kallisys.net>,
4 * All rights reserved.
5 * Copyright (c) 2006-2013 The MacPorts Project
6 *
7 * $Id: darwintrace.h 112642 2013-10-28 18:59:19Z cal@macports.org $
8 *
9 * @APPLE_BSD_LICENSE_HEADER_START@
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * 1.  Redistributions of source code must retain the above copyright
16 *     notice, this list of conditions and the following disclaimer.
17 * 2.  Redistributions in binary form must reproduce the above copyright
18 *     notice, this list of conditions and the following disclaimer in the
19 *     documentation and/or other materials provided with the distribution.
20 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
21 *     its contributors may be used to endorse or promote products derived
22 *     from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
25 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
28 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 * @APPLE_BSD_LICENSE_HEADER_END@
36 */
37
38#define DARWINTRACE_USE_PRIVATE_API 1
39#include "darwintrace.h"
40
41#include <ctype.h>
42#include <dlfcn.h>
43#include <errno.h>
44#include <fcntl.h>
45#include <stdlib.h>
46#include <string.h>
47#include <sys/param.h>
48#include <sys/syscall.h>
49#include <sys/types.h>
50#include <sys/uio.h>
51#include <unistd.h>
52
53#if defined(HAVE_SPAWN_H) && defined(HAVE_POSIX_SPAWN)
54#include <spawn.h>
55#endif
56
57static void store_env() __attribute__((constructor));
58
59/**
60 * Copy of the DYLD_INSERT_LIBRARIES environment variable to restore it in
61 * execve(2). DYLD_INSERT_LIBRARIES is needed to preload this library into any
62 * process' address space.
63 */
64static char *__env_dyld_insert_libraries;
65static char *__env_full_dyld_insert_libraries;
66
67/**
68 * Copy of the DYLD_FORCE_FLAT_NAMESPACE environment variable to restore it in
69 * execve(2). DYLD_FORCE_FLAT_NAMESPACE=1 is needed for the preload-based
70 * sandbox to work.
71 */
72static char *__env_dyld_force_flat_namespace;
73static char *__env_full_dyld_force_flat_namespace;
74
75/**
76 * Copy of the DARWINTRACE_LOG environment variable to restore it in execve(2).
77 * Contains the path to the unix socket used for communication with the
78 * MacPorts-side of the sandbox. Since this variable is also used from
79 * darwintrace.c, is can not be static.
80 */
81char *__env_darwintrace_log;
82static char *__env_full_darwintrace_log;
83
84/**
85 * Copy the environment variables, if they're defined. This is run as
86 * a constructor at startup.
87 */
88static void store_env() {
89#define COPYENV(name, variable, valuevar) do {\
90                char *val;\
91                if (NULL != (val = getenv(#name))) {\
92                        size_t lenName = strlen(#name);\
93                        size_t lenVal  = strlen(val);\
94                        if (NULL == (variable = malloc(lenName + 1 + lenVal + 1))) {\
95                                perror("darwintrace: malloc");\
96                                abort();\
97                        }\
98                        strcpy(variable, #name);\
99                        strcat(variable, "=");\
100                        strcat(variable, val);\
101                        valuevar = variable + lenName + 1;\
102                } else {\
103                        variable = NULL;\
104                        valuevar = NULL;\
105                }\
106        } while (0)
107
108        COPYENV(DYLD_INSERT_LIBRARIES, __env_full_dyld_insert_libraries, __env_dyld_insert_libraries);
109        COPYENV(DYLD_FORCE_FLAT_NAMESPACE, __env_full_dyld_force_flat_namespace, __env_dyld_force_flat_namespace);
110        COPYENV(DARWINTRACE_LOG, __env_full_darwintrace_log, __env_darwintrace_log);
111#undef COPYENV
112
113        char *debugpath = getenv("DARWINTRACE_DEBUG");
114        if (debugpath) {
115                __darwintrace_stderr = fopen(debugpath, "a+");
116        } else {
117                __darwintrace_stderr = stderr;
118        }
119}
120
121/**
122 * Return false if str doesn't begin with prefix, true otherwise.
123 */
124static inline bool __darwintrace_strbeginswith(const char *str, const char *prefix) {
125        char s;
126        char p;
127        do {
128                s = *str++;
129                p = *prefix++;
130        } while (p && (p == s));
131        return (p == '\0');
132}
133
134/**
135 * This function checks that envp contains the global variables we had when the
136 * library was loaded and modifies it if it doesn't. Returns a malloc(3)'d copy
137 * of envp where the appropriate values have been restored. The caller should
138 * pass the returned pointer to free(3) if necessary to avoid leaks.
139 */
140static inline char **restore_env(char *const envp[]) {
141        // we can re-use pre-allocated strings from store_env
142        char *dyld_insert_libraries_ptr     = __env_full_dyld_insert_libraries;
143        char *dyld_force_flat_namespace_ptr = __env_full_dyld_force_flat_namespace;
144        char *darwintrace_log_ptr           = __env_full_darwintrace_log;
145
146        char *const *enviter = envp;
147        size_t envlen = 0;
148        char **copy;
149        char **copyiter;
150
151        while (enviter != NULL && *enviter != NULL) {
152                envlen++;
153                enviter++;
154        }
155
156        // 4 is sufficient for the three variables we copy and the terminator
157        copy = malloc(sizeof(char *) * (envlen + 4));
158
159        enviter  = envp;
160        copyiter = copy;
161
162        while (enviter != NULL && *enviter != NULL) {
163                char *val = *enviter;
164                if (__darwintrace_strbeginswith(val, "DYLD_INSERT_LIBRARIES=")) {
165                        val = dyld_insert_libraries_ptr;
166                        dyld_insert_libraries_ptr = NULL;
167                } else if (__darwintrace_strbeginswith(val, "DYLD_FORCE_FLAT_NAMESPACE=")) {
168                        val = dyld_force_flat_namespace_ptr;
169                        dyld_force_flat_namespace_ptr = NULL;
170                } else if (__darwintrace_strbeginswith(val, "DARWINTRACE_LOG=")) {
171                        val = darwintrace_log_ptr;
172                        darwintrace_log_ptr = NULL;
173                }
174
175                if (val) {
176                        *copyiter++ = val;
177                }
178
179                enviter++;
180        }
181
182        if (dyld_insert_libraries_ptr) {
183                *copyiter++ = dyld_insert_libraries_ptr;
184        }
185        if (dyld_force_flat_namespace_ptr) {
186                *copyiter++ = dyld_force_flat_namespace_ptr;
187        }
188        if (darwintrace_log_ptr) {
189                *copyiter++ = darwintrace_log_ptr;
190        }
191
192        *copyiter = 0;
193
194        return copy;
195}
196
197/**
198 * Helper function that opens the file indicated by \a path, checks whether it
199 * is a script (i.e., contains a shebang line) and verifies the interpreter is
200 * within the sandbox bounds.
201 *
202 * \param[in] path The path of the file to be executed
203 * \return 0, if access should be granted, a non-zero error code to be stored
204 *         in \c errno otherwise
205 */
206static inline int check_interpreter(const char *restrict path) {
207#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
208#define close(x) syscall(SYS_close, (x))
209        int fd = open(path, O_RDONLY, 0);
210        if (fd <= 0) {
211                return errno;
212        }
213
214        char buffer[MAXPATHLEN + 1 + 2];
215        ssize_t bytes_read;
216
217        /* Read the file for the interpreter. Fortunately, on OS X:
218         *   The system guarantees to read the number of bytes requested if
219         *   the descriptor references a normal file that has that many
220         *   bytes left before the end-of-file, but in no other case.
221         * That _does_ save us another ugly loop to get things right. */
222        bytes_read = read(fd, buffer, sizeof(buffer) - 1);
223        buffer[bytes_read] = '\0';
224        const char *buffer_end = buffer + bytes_read;
225        if (bytes_read > 2 && buffer[0] == '#' && buffer[1] == '!') {
226                char *interp = buffer + 2;
227
228                /* skip past leading whitespace */
229                while (interp < buffer_end && isblank(*interp)) {
230                        ++interp;
231                }
232                /* found interpreter (or ran out of data); skip until next
233                 * whitespace, then terminate the string */
234                if (interp < buffer_end) {
235                        char *interp_end = interp;
236                        strsep(&interp_end, " \t");
237                }
238
239                /* check the iterpreter against the sandbox */
240                if (!__darwintrace_is_in_sandbox(interp, DT_REPORT | DT_ALLOWDIR | DT_FOLLOWSYMS)) {
241                        close(fd);
242                        return ENOENT;
243                }
244        }
245
246        close(fd);
247        return 0;
248#undef open
249#undef close
250}
251
252/**
253 * Wrapper for \c execve(2). Denies access and simulates the file does not
254 * exist, if it's outside the sandbox. Also checks for potential interpreters
255 * using \c check_interpreter.
256 */
257static int _dt_execve(const char *path, char *const argv[], char *const envp[]) {
258#define execve(x,y,z) syscall(SYS_execve, (x), (y), (z))
259        __darwintrace_setup();
260
261        int result = 0;
262
263        if (!__darwintrace_is_in_sandbox(path, DT_REPORT | DT_ALLOWDIR | DT_FOLLOWSYMS)) {
264                errno = ENOENT;
265                result = -1;
266        } else {
267                int interp_result = check_interpreter(path);
268                if (interp_result != 0) {
269                        errno = interp_result;
270                        result = -1;
271                } else {
272                        // Since \c execve(2) will likely not return, log before calling
273                        debug_printf("execve(%s) = ?\n", path);
274
275                        // Our variables won't survive exec, clean up
276                        __darwintrace_close();
277                        __darwintrace_pid = (pid_t) -1;
278
279                        // Call the original execve function, but restore environment
280                        char **newenv = restore_env(envp);
281                        result = execve(path, argv, newenv);
282                        free(newenv);
283                }
284        }
285
286        debug_printf("execve(%s) = %d\n", path, result);
287
288        return result;
289#undef execve
290}
291
292DARWINTRACE_INTERPOSE(_dt_execve, execve);
293
294#if defined(HAVE_SPAWN_H) && defined(HAVE_POSIX_SPAWN)
295/**
296 * Wrapper for \c posix_spawn(2). Denies access and simulates the file does not
297 * exist, if it's outside the sandbox. Also checks for potential interpreters
298 * using \c check_interpreter.
299 */
300static int _dt_posix_spawn(pid_t *restrict pid, const char *restrict path, const posix_spawn_file_actions_t *file_actions,
301                const posix_spawnattr_t *restrict attrp, char *const argv[restrict], char *const envp[restrict]) {
302        __darwintrace_setup();
303
304        int result = 0;
305
306        if (!__darwintrace_is_in_sandbox(path, DT_REPORT | DT_ALLOWDIR | DT_FOLLOWSYMS)) {
307                result = ENOENT;
308        } else {
309                int interp_result = check_interpreter(path);
310                if (interp_result != 0) {
311                        result = interp_result;
312                } else {
313                        short attrflags;
314                        if (   attrp != NULL
315                                && posix_spawnattr_getflags(attrp, &attrflags) == 0
316                                && (attrflags & POSIX_SPAWN_SETEXEC) > 0) {
317                                // Apple-specific extension: This call will not return, but
318                                // behave like execve(2). Since our variables won't survive
319                                // that, clean up. Also log the call, because we likely won't
320                                // be able to after the call.
321                                debug_printf("execve(%s) = ?\n", path);
322
323                                __darwintrace_close();
324                                __darwintrace_pid = (pid_t) - 1;
325                        }
326
327                        /* ATTN: the mac syscall you get from syscall(SYS_posix_spawn) corresponds
328                         * to __posix_spawn from /usr/lib/system/libsystem_kernel.dylib. We can not
329                         * override __posix_spawn directly, because it is called from posix_spawn
330                         * inside the same library (i.e., there is no dyld stub we can override).
331                         *
332                         * We cannot override posix_spawn and call __posix_spawn from it
333                         * either, because that will fail with an invalid argument. Thus,
334                         * we need to call the original posix_spawn from here. */
335                        // call the original posix_spawn function, but restore environment
336                        char **newenv = restore_env(envp);
337                        result = posix_spawn(pid, path, file_actions, attrp, argv, newenv);
338                        free(newenv);
339                }
340        }
341
342        debug_printf("posix_spawn(%s) = %d\n", path, result);
343
344        return result;
345}
346
347DARWINTRACE_INTERPOSE(_dt_posix_spawn, posix_spawn);
348#endif
Note: See TracBrowser for help on using the repository browser.