1/*
2 * Copyright 2012, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <portability.h>
18#include <unistd.h>
19#include <stdarg.h>
20#include <stdlib.h>
21#include <stdio.h>
22#include <errno.h>
23#include <errno_portable.h>
24#include <filefd_portable.h>
25#include <signal_portable.h>
26#include <sys/atomics.h>
27
28#define PORTABLE_TAG "filefd_portable"
29#include <log_portable.h>
30
31/*
32 * Maintaining a list of special file descriptors in lib-portable:
33 * ---------------------------------------------------------------
34 *
35 * These are file descriptors which were opened with system calls
36 * which make it possible to read kernel data structures via the
37 * read system call. See man pages for:
38 *      signalfd(2)
39 *      eventfd(2)
40 *      timerfd_create(2)
41 *
42 * The files conditioned with signalfd(2) need to have their reads
43 * intercepted to correct signal numbers. This is done using this table
44 * of mapped files.
45 *
46 * The signalfd(2) semantics are maintained across execve(2) by exporting
47 * and importing environment variables for file descriptors that are not
48 * marked as close-on-execute. For example testing import code with:
49 * Eg:
50 *      export ANDROID_PORTABLE_MAPPED_FILE_DESCRIPTORS=10,17
51 *      export ANDROID_PORTABLE_MAPPED_FILE_TYPES=2,1
52 *
53 *      Where
54 *          filefd_mapped_file[10] = SIGNAL_FD_TYPE:2
55 *          filefd_FD_CLOEXEC_file[10] = 0;
56 *      and
57 *          filefd_mapped_file[17] = EVENT_FD_TYPE:1
58 *          filefd_FD_CLOEXEC_file[17] = 0;
59 *
60 * A table of CLOEXEC_files is maintained via call-backs
61 * in open_portable() and fcntl_portable() which indicates
62 * the files with close-on-execute semantics.
63 *
64 * The signalfd(2) fork(2) and thread semantics are not
65 * affected by the mapping of signalfd() file descriptor reads.
66 *
67 * This algorithm requires that threads have the same sharing
68 * attributes for file descriptors and memory and will be disabled
69 * by a call from clone() if the environment is unsuitable for it's use.
70 */
71
72static char *fd_env_name = "ANDROID_PORTABLE_MAPPED_FILE_DESCRIPTORS";
73static char *type_env_name = "ANDROID_PORTABLE_MAPPED_FILE_TYPES";
74static enum filefd_type filefd_mapped_file[__FD_SETSIZE];
75static int  filefd_FD_CLOEXEC_file[__FD_SETSIZE];
76
77static volatile int filefd_mapped_files = 0;
78static volatile int filefd_enabled = 1;
79
80/*
81 * Assuming sizeof(int)==4, and __FD_SETSIZE < 10000 each token will
82 * occupy a maximum of 5 characters (4 digits + delimiter:','). The tokens
83 * are the numbers above, a file descriptor (0..9999), and the filefd_type's
84 * which are a single digit.
85 *
86 * The arrays used to manipulate the environment variables are allocated using
87 * malloc to avoid overrunning the stack.
88 */
89#if __FD_SETSIZE >= 10000
90#error MAX_ENV_SIZE must be increased
91#endif
92
93#define MAX_ENV_SIZE (__FD_SETSIZE * 5)
94
95static int export_fd_env()
96{
97    const int max_env_size = MAX_ENV_SIZE;
98    int type_env_bytes_remaining = max_env_size;
99    char *type_env_allocated = NULL, *type_env;
100    int fd_env_bytes_remaining = max_env_size;
101    char *fd_env_allocated = NULL, *fd_env;
102    int exported_file_descriptors = 0;
103    enum filefd_type fd_type;
104    int overwrite = 1;
105    int fd_count = 0;
106    int saved_errno;
107    int fd_cloexec;
108    int len;
109    int rv1;
110    int rv2;
111    int rv;
112    int fd;
113
114    ALOGV("%s:() {", __func__);
115
116    saved_errno = *REAL(__errno)();
117
118    type_env_allocated = malloc(max_env_size);
119    fd_env_allocated = malloc(max_env_size);
120    if (type_env_allocated == NULL || fd_env_allocated == NULL) {
121        ALOGE("%s: type_env_allocated:%p, fd_env_allocated:%p; FIXME!", __func__,
122                   type_env_allocated,    fd_env_allocated);
123
124        rv = -1;
125        goto done;
126    } else {
127        ALOGV("%s: type_env_allocated:%p, fd_env_allocated:%p;", __func__,
128                   type_env_allocated,    fd_env_allocated);
129    }
130
131    type_env = type_env_allocated;
132    fd_env = fd_env_allocated;
133
134    for (fd = 0; fd < __FD_SETSIZE; fd++) {
135        fd_type = filefd_mapped_file[fd];
136        if (fd_type != UNUSED_FD_TYPE) {
137            ++fd_count;
138            ALOGV("%s: fd_type = %d = filefd_mapped_file[fd:%d]; ++fdcount:%d;", __func__,
139                       fd_type,                          fd,       fd_count);
140
141            fd_cloexec = filefd_FD_CLOEXEC_file[fd];
142            ALOGV("%s: fd_cloexec = %d = filefd_FD_CLOEXEC_file[fd:%d];", __func__,
143                       fd_cloexec,                              fd);
144
145            if (fd_cloexec == 0) {
146                rv = snprintf(fd_env, fd_env_bytes_remaining, "%d,", fd);
147                ASSERT(rv > 0);
148                fd_env += rv;
149                fd_env_bytes_remaining -= rv;
150                rv = snprintf(type_env, type_env_bytes_remaining, "%d,", filefd_mapped_file[fd]);
151                ASSERT(rv > 0);
152                type_env += rv;
153                type_env_bytes_remaining -= rv;
154                exported_file_descriptors++;
155            }
156
157            /*
158             * There is a chance of inconsistent results here if
159             * another thread is updating the array while it was
160             * being copied, but this code is only run during exec
161             * so the state of the file descriptors that the child
162             * sees will be inconsistent anyway.
163             */
164            if (fd_count == filefd_mapped_files)
165                break;
166        }
167    }
168    if (fd_count != filefd_mapped_files) {
169        ALOGE("%s: fd_count:%d != filefd_mapped_files:%d; [Likely Race; add futex?]", __func__,
170                   fd_count,      filefd_mapped_files);
171
172    }
173    if (exported_file_descriptors == 0) {
174        rv1 = unsetenv(fd_env_name);
175        rv2 = unsetenv(type_env_name);
176        if (rv1 != 0 || rv2 != 0) {
177            ALOGV("%s: Note: unsetenv() failed!", __func__);
178        }
179        rv = 0;
180    } else {
181        if (fd_env > fd_env_allocated) {
182            fd_env--;                           /* backup fd_env to last ',' */
183        }
184        *fd_env = '\0';
185
186        if (type_env > type_env_allocated) {
187            type_env--;                         /* backup type_env to last ',' */
188        }
189        *type_env = '\0';
190
191        rv = setenv(fd_env_name, fd_env_allocated, overwrite);
192        if (rv != 0) {
193            ALOGE("%s: rv:%d = setenv(fd_env_name:'%s', fd_env_allocated:'%s' ...);", __func__,
194                       rv,            fd_env_name,      fd_env_allocated);
195        } else {
196            ALOGV("%s: rv:%d = setenv(fd_env_name:'%s', fd_env_allocated:'%s' ...);", __func__,
197                       rv,            fd_env_name,      fd_env_allocated);
198        }
199        if (rv != 0) goto done;
200
201        rv = setenv(type_env_name, type_env_allocated, overwrite);
202
203        if (rv != 0) {
204            ALOGE("%s: rv:%d = setenv(type_env_name:'%s', type_env_allocated:'%s' ...);",
205            __func__,  rv,            type_env_name,      type_env_allocated);
206        } else {
207            ALOGV("%s: rv:%d = setenv(type_env_name:'%s', type_env_allocated:'%s' ...);",
208            __func__,  rv,            type_env_name,      type_env_allocated);
209        }
210    }
211
212done:
213    if (type_env_allocated)
214        free(type_env_allocated);
215
216    if (fd_env_allocated)
217        free(fd_env_allocated);
218
219    *REAL(__errno)() = saved_errno;
220
221    ALOGV("%s: return(rv:%d); }", __func__, rv);
222    return rv;
223}
224
225
226static int import_fd_env(int verify)
227{
228    char *type_env_allocated = NULL;
229    char *fd_env_allocated = NULL;
230    char *type_token_saved_ptr;
231    char *fd_token_saved_ptr;
232    enum filefd_type fd_type;
233    char *type_env, *fd_env;
234    int saved_errno;
235    char *type_token;
236    char *fd_token;
237    int rv = 0;
238    int fd;
239
240    ALOGV("%s:(verify:%d) {", __func__, verify);
241
242    saved_errno = *REAL(__errno)();
243
244    /*
245     * get file descriptor environment pointer and make a
246     * a copy of the string.
247     */
248    fd_env = getenv(fd_env_name);
249    if (fd_env == NULL) {
250        ALOGV("%s: fd_env = NULL = getenv('%s');", __func__,
251                                   fd_env_name);
252        goto done;
253    } else {
254        ALOGV("%s: fd_env = '%s' = getenv('%s');", __func__,
255                   fd_env,         fd_env_name);
256
257        fd_env_allocated = malloc(strlen(fd_env)+1);
258        if (fd_env_allocated == NULL) {
259            ALOGE("%s: fd_env_allocated = NULL; malloc failed", __func__);
260            goto done;
261        }
262        strcpy(fd_env_allocated, fd_env);
263    }
264
265    /*
266     * get file descriptor environment pointer and make a copy of
267     * the string to our stack.
268     */
269    type_env = getenv(type_env_name);
270    if (type_env == NULL) {
271        ALOGV("%s: type_env = NULL = getenv(type_env_name:'%s');", __func__,
272                                            type_env_name);
273        goto done;
274    } else {
275        ALOGV("%s: type_env = '%s' = getenv(type_env_name:'%s');", __func__,
276                   type_env,                type_env_name);
277
278        type_env_allocated = malloc(strlen(type_env)+1);
279        if (type_env_allocated == NULL) {
280            ALOGE("%s: type_env_allocated = NULL; malloc failed", __func__);
281            goto done;
282        }
283        strcpy(type_env_allocated, type_env);
284    }
285
286    /*
287     * Setup strtok_r(), use it to parse the env tokens, and
288     * initialise the filefd_mapped_file array.
289     */
290    fd_token = strtok_r(fd_env_allocated, ",", &fd_token_saved_ptr);
291    type_token = strtok_r(type_env_allocated, ",", &type_token_saved_ptr);
292    while (fd_token && type_token) {
293        fd = atoi(fd_token);
294        ASSERT(fd >= 0 );
295        ASSERT(fd < __FD_SETSIZE);
296
297        fd_type = (enum filefd_type) atoi(type_token);
298        ASSERT(fd_type > UNUSED_FD_TYPE);
299        ASSERT(fd_type < MAX_FD_TYPE);
300
301        if (fd >= 0 && fd < __FD_SETSIZE) {
302            if (fd_type > UNUSED_FD_TYPE && fd_type < MAX_FD_TYPE) {
303                if (verify) {
304                    ASSERT(filefd_mapped_file[fd] == fd_type);
305                    ALOGV("%s: filefd_mapped_file[fd:%d] == fd_type:%d;", __func__,
306                                                  fd,       fd_type);
307                } else {
308                    ASSERT(filefd_mapped_file[fd] == UNUSED_FD_TYPE);
309
310                    __atomic_inc(&filefd_mapped_files);
311                    ALOGV("%s: ++filefd_mapped_files:%d;", __func__,
312                                 filefd_mapped_files);
313
314                    filefd_mapped_file[fd] = fd_type;
315                    ALOGV("%s: filefd_mapped_file[fd:%d] = fd_type:%d;", __func__,
316                                                  fd,      fd_type);
317                }
318            }
319        }
320
321        fd_token = strtok_r(NULL, ",", &fd_token_saved_ptr);
322        type_token = strtok_r(NULL, ",", &type_token_saved_ptr);
323    }
324
325done:
326    if (type_env_allocated)
327        free(type_env_allocated);
328    if (fd_env_allocated)
329        free(fd_env_allocated);
330
331    *REAL(__errno)() = saved_errno;
332
333    ALOGV("%s: return(rv:%d); }", __func__, rv);
334    return rv;
335}
336
337
338/*
339 * This function will get run by the linker when the library is loaded.
340 */
341static void __attribute__ ((constructor)) linker_import_fd_env(void)
342{
343    int rv;
344    int verify_consistancy = 0;
345
346    ALOGV(" ");
347    ALOGV("%s() {", __func__);
348
349    rv = import_fd_env(verify_consistancy);     /* File type table not verified. */
350
351    ALOGV("%s: }", __func__);
352}
353
354
355__hidden void filefd_opened(int fd, enum filefd_type fd_type)
356{
357    ALOGV("%s(fd:%d) {", __func__, fd);
358
359    if (fd >= 0 && fd < __FD_SETSIZE) {
360        if (filefd_mapped_file[fd] == UNUSED_FD_TYPE) {
361            __atomic_inc(&filefd_mapped_files);
362            filefd_mapped_file[fd] = fd_type;
363        }
364        ASSERT(filefd_mapped_file[fd] == fd_type);
365    }
366
367    ALOGV("%s: }", __func__);
368}
369
370__hidden void filefd_closed(int fd)
371{
372    ALOGV("%s(fd:%d) {", __func__, fd);
373
374    if (fd >= 0 && fd < __FD_SETSIZE) {
375        if (filefd_mapped_file[fd] != UNUSED_FD_TYPE) {
376            filefd_mapped_file[fd] = UNUSED_FD_TYPE;
377            filefd_FD_CLOEXEC_file[fd] = 0;
378            __atomic_dec(&filefd_mapped_files);
379        }
380    }
381    ALOGV("%s: }", __func__);
382}
383
384
385__hidden void filefd_CLOEXEC_enabled(int fd)
386{
387    ALOGV("%s:(fd:%d) {", __func__, fd);
388
389    if (fd >= 0 && fd < __FD_SETSIZE) {
390        filefd_FD_CLOEXEC_file[fd] = 1;
391    }
392
393    ALOGV("%s: }", __func__);
394}
395
396__hidden void filefd_CLOEXEC_disabled(int fd)
397{
398    ALOGV("%s:(fd:%d) {", __func__, fd);
399
400    if (fd >= 0 && fd < __FD_SETSIZE) {
401        filefd_FD_CLOEXEC_file[fd] = 0;
402    }
403
404    ALOGV("%s: }", __func__);
405}
406
407
408__hidden void filefd_disable_mapping()
409{
410    ALOGV("%s:() {", __func__);
411
412    filefd_enabled = 0;
413
414    ALOGV("%s: }", __func__);
415}
416
417
418int WRAP(close)(int fd)
419{
420    int rv;
421
422    ALOGV(" ");
423    ALOGV("%s(fd:%d) {", __func__, fd);
424
425    rv = REAL(close)(fd);
426    filefd_closed(fd);
427
428    ALOGV("%s: return(rv:%d); }", __func__, rv);
429    return rv;
430}
431
432
433int WRAP(read)(int fd, void *buf, size_t count)
434{
435    int rv;
436    enum filefd_type fd_type;
437
438    ALOGV(" ");
439    ALOGV("%s(fd:%d, buf:0x%p, count:%d) {", __func__,
440              fd,    buf,      count);
441
442    fd_type = filefd_mapped_file[fd];
443    ALOGV("%s:fd_type:%d", __func__,
444              fd_type);
445
446    switch (fd_type) {
447    /* Reads on these descriptors are portable; no need to be mapped. */
448    case UNUSED_FD_TYPE:
449    case EVENT_FD_TYPE:
450    case INOTIFY_FD_TYPE:
451    case TIMER_FD_TYPE:
452        rv = REAL(read)(fd, buf, count);
453        break;
454
455    /* The read() of a signalfd() file descriptor needs to be mapped. */
456    case SIGNAL_FD_TYPE:
457        if (filefd_enabled) {
458            rv = read_signalfd_mapper(fd, buf, count);
459        } else {
460            rv = REAL(read)(fd, buf, count);
461        }
462        break;
463
464    default:
465        ALOGE("Unknown fd_type:%d!", fd_type);
466        rv = REAL(read)(fd, buf, count);
467        break;
468    }
469
470    ALOGV("%s: return(rv:%d); }", __func__, rv);
471    return rv;
472}
473
474
475/*
476 * Export PORTABLE environment variables before execve().
477 * Tries a second time if it detects an extremely unlikely
478 * race condition.
479 */
480int WRAP(execve)(const char *filename, char *const argv[],  char *const envp[])
481{
482    int rv;
483    int mapped_files = filefd_mapped_files;
484    int verify_consistancy = 1;
485
486    ALOGV(" ");
487    ALOGV("%s(filename:%p, argv:%p, envp:%p) {", __func__,
488              filename,    argv,    envp);
489
490    export_fd_env();
491
492    if (mapped_files != filefd_mapped_files) {
493        export_fd_env();
494    }
495    import_fd_env(verify_consistancy);          /* File type table consistancy verified. */
496
497    rv = REAL(execve)(filename, argv, envp);
498
499    ALOGV("%s: return(rv:%d); }", __func__, rv);
500    return rv;
501}
502
503