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