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