]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/platform-bionic.git/blob - linker/linker_environ.cpp
More dynamic linker cleanup.
[android-sdk/platform-bionic.git] / linker / linker_environ.cpp
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *  * Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
29 #include "linker_environ.h"
31 #include <linux/auxvec.h>
32 #include <stddef.h>
33 #include <stdlib.h>
34 #include <unistd.h>
36 static char** _envp;
37 static bool _AT_SECURE_value = true;
39 bool get_AT_SECURE() {
40   return _AT_SECURE_value;
41 }
43 /* Returns 1 if 'str' points to a valid environment variable definition.
44  * For now, we check that:
45  *  - It is smaller than MAX_ENV_LEN (to detect non-zero terminated strings)
46  *  - It contains at least one equal sign that is not the first character
47  */
48 static int _is_valid_definition(const char* str) {
49   int pos = 0;
50   int first_equal_pos = -1;
52   // According to its sources, the kernel uses 32*PAGE_SIZE by default
53   // as the maximum size for an env. variable definition.
54   const int MAX_ENV_LEN = 32*4096;
56   if (str == NULL) {
57     return 0;
58   }
60   // Parse the string, looking for the first '=' there, and its size.
61   while (pos < MAX_ENV_LEN) {
62     if (str[pos] == '\0') {
63       break;
64     }
65     if (str[pos] == '=' && first_equal_pos < 0) {
66       first_equal_pos = pos;
67     }
68     pos++;
69   }
71   if (pos >= MAX_ENV_LEN) {
72     return 0; // Too large.
73   }
75   if (first_equal_pos < 1) {
76     return 0; // No equals sign, or it's the first character.
77   }
79   return 1;
80 }
82 static void __init_AT_SECURE(unsigned* auxv) {
83   // Check auxv for AT_SECURE first to see if program is setuid, setgid,
84   // has file caps, or caused a SELinux/AppArmor domain transition.
85   for (unsigned* v = auxv; v[0]; v += 2) {
86     if (v[0] == AT_SECURE) {
87       // Kernel told us whether to enable secure mode.
88       _AT_SECURE_value = v[1];
89       return;
90     }
91   }
93   // We don't support ancient kernels.
94   const char* msg = "FATAL: kernel did not supply AT_SECURE\n";
95   write(2, msg, strlen(msg));
96   exit(EXIT_FAILURE);
97 }
99 static void __remove_unsafe_environment_variables() {
100   // None of these should be allowed in setuid programs.
101   static const char* const UNSAFE_VARIABLE_NAMES[] = {
102       "GCONV_PATH",
103       "GETCONF_DIR",
104       "HOSTALIASES",
105       "LD_AOUT_LIBRARY_PATH",
106       "LD_AOUT_PRELOAD",
107       "LD_AUDIT",
108       "LD_DEBUG",
109       "LD_DEBUG_OUTPUT",
110       "LD_DYNAMIC_WEAK",
111       "LD_LIBRARY_PATH",
112       "LD_ORIGIN_PATH",
113       "LD_PRELOAD",
114       "LD_PROFILE",
115       "LD_SHOW_AUXV",
116       "LD_USE_LOAD_BIAS",
117       "LOCALDOMAIN",
118       "LOCPATH",
119       "MALLOC_CHECK_",
120       "MALLOC_TRACE",
121       "NIS_PATH",
122       "NLSPATH",
123       "RESOLV_HOST_CONF",
124       "RES_OPTIONS",
125       "TMPDIR",
126       "TZDIR",
127       NULL
128   };
129   for (size_t i = 0; UNSAFE_VARIABLE_NAMES[i] != NULL; ++i) {
130     linker_env_unset(UNSAFE_VARIABLE_NAMES[i]);
131   }
134 static void __remove_invalid_environment_variables() {
135   char** src  = _envp;
136   char** dst = _envp;
137   for (; src[0] != NULL; ++src) {
138     if (!_is_valid_definition(src[0])) {
139       continue;
140     }
141     dst[0] = src[0];
142     ++dst;
143   }
144   dst[0] = NULL;
147 unsigned* linker_env_init(unsigned* environment_and_aux_vectors) {
148   // Store environment pointer - can't be NULL.
149   _envp = reinterpret_cast<char**>(environment_and_aux_vectors);
151   // Skip over all environment variable definitions.
152   // The end of the environment block is marked by two NULL pointers.
153   unsigned* aux_vectors = environment_and_aux_vectors;
154   while (aux_vectors[0] != 0) {
155     ++aux_vectors;
156   }
157   ++aux_vectors;
159   __remove_invalid_environment_variables();
160   __init_AT_SECURE(aux_vectors);
162   // Sanitize environment if we're loading a setuid program.
163   if (get_AT_SECURE()) {
164     __remove_unsafe_environment_variables();
165   }
167   return aux_vectors;
170 /* Check if the environment variable definition at 'envstr'
171  * starts with '<name>=', and if so return the address of the
172  * first character after the equal sign. Otherwise return NULL.
173  */
174 static char* env_match(char* envstr, const char* name) {
175   size_t i = 0;
177   while (envstr[i] == name[i] && name[i] != '\0') {
178     ++i;
179   }
181   if (name[i] == '\0' && envstr[i] == '=') {
182     return envstr + i + 1;
183   }
185   return NULL;
188 const char* linker_env_get(const char* name) {
189   if (name == NULL || name[0] == '\0') {
190     return NULL;
191   }
193   for (char** p = _envp; p[0] != NULL; ++p) {
194     char* val = env_match(p[0], name);
195     if (val != NULL) {
196       if (val[0] == '\0') {
197         return NULL; // Return NULL for empty strings.
198       }
199       return val;
200     }
201   }
202   return NULL;
205 void linker_env_unset(const char* name) {
206   char** readp = _envp;
207   char** writep = readp;
209   if (name == NULL || name[0] == '\0') {
210     return;
211   }
213   for ( ; readp[0] != NULL; readp++ ) {
214     if (env_match(readp[0], name)) {
215       continue;
216     }
217     writep[0] = readp[0];
218     writep++;
219   }
220   /* end list with a NULL */
221   writep[0] = NULL;