• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
export_fd_env()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 
import_fd_env(int verify)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  */
linker_import_fd_env(void)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 
filefd_opened(int fd,enum filefd_type fd_type)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 
filefd_closed(int fd)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 
filefd_CLOEXEC_enabled(int fd)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 
filefd_CLOEXEC_disabled(int fd)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 
filefd_disable_mapping()407 __hidden void filefd_disable_mapping()
408 {
409     ALOGV("%s:() {", __func__);
410 
411     filefd_enabled = 0;
412 
413     ALOGV("%s: }", __func__);
414 }
415 
416 
WRAP(close)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 
WRAP(read)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  */
WRAP(execve)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