Home | History | Annotate | Download | only in arch-mips
      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 
     72 static char *fd_env_name = "ANDROID_PORTABLE_MAPPED_FILE_DESCRIPTORS";
     73 static char *type_env_name = "ANDROID_PORTABLE_MAPPED_FILE_TYPES";
     74 static enum filefd_type filefd_mapped_file[__FD_SETSIZE];
     75 static int  filefd_FD_CLOEXEC_file[__FD_SETSIZE];
     76 
     77 static volatile int filefd_mapped_files = 0;
     78 static 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 
     95 static 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 
    212 done:
    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 
    226 static 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 
    325 done:
    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  */
    341 static 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 
    418 int 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 
    433 int 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  */
    480 int 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