Lines 83-90
static bool sb_check_exec(const char *filename, char *const argv[])
Link Here
|
83 |
({ \ |
83 |
({ \ |
84 |
Elf##n##_Ehdr *ehdr = (void *)elf; \ |
84 |
Elf##n##_Ehdr *ehdr = (void *)elf; \ |
85 |
Elf##n##_Phdr *phdr = (void *)(elf + ehdr->e_phoff); \ |
85 |
Elf##n##_Phdr *phdr = (void *)(elf + ehdr->e_phoff); \ |
86 |
Elf##n##_Addr vaddr, filesz, vsym = 0, vstr = 0, vhash = 0, vliblist = 0; \ |
86 |
Elf##n##_Addr vaddr, filesz, vsym = 0, vstr = 0, vhash = 0, vgnuhash = 0; \ |
87 |
Elf##n##_Off offset, symoff = 0, stroff = 0, hashoff = 0, liblistoff = 0; \ |
87 |
Elf##n##_Off offset, symoff = 0, stroff = 0, hashoff = 0, gnuhashoff = 0; \ |
88 |
Elf##n##_Dyn *dyn; \ |
88 |
Elf##n##_Dyn *dyn; \ |
89 |
Elf##n##_Sym *sym, *symend; \ |
89 |
Elf##n##_Sym *sym, *symend; \ |
90 |
uint##n##_t ent_size = 0, str_size = 0; \ |
90 |
uint##n##_t ent_size = 0, str_size = 0; \ |
Lines 107-113
static bool sb_check_exec(const char *filename, char *const argv[])
Link Here
|
107 |
case DT_STRTAB: vstr = dyn->d_un.d_val; break; \ |
107 |
case DT_STRTAB: vstr = dyn->d_un.d_val; break; \ |
108 |
case DT_STRSZ: str_size = dyn->d_un.d_val; break; \ |
108 |
case DT_STRSZ: str_size = dyn->d_un.d_val; break; \ |
109 |
case DT_HASH: vhash = dyn->d_un.d_val; break; \ |
109 |
case DT_HASH: vhash = dyn->d_un.d_val; break; \ |
110 |
case DT_GNU_LIBLIST: vliblist = dyn->d_un.d_val; break; \ |
110 |
case DT_GNU_HASH: vgnuhash = dyn->d_un.d_val; break; \ |
111 |
} \ |
111 |
} \ |
112 |
++dyn; \ |
112 |
++dyn; \ |
113 |
} \ |
113 |
} \ |
Lines 127-134
static bool sb_check_exec(const char *filename, char *const argv[])
Link Here
|
127 |
stroff = offset + (vstr - vaddr); \ |
127 |
stroff = offset + (vstr - vaddr); \ |
128 |
if (vhash >= vaddr && vhash < vaddr + filesz) \ |
128 |
if (vhash >= vaddr && vhash < vaddr + filesz) \ |
129 |
hashoff = offset + (vhash - vaddr); \ |
129 |
hashoff = offset + (vhash - vaddr); \ |
130 |
if (vliblist >= vaddr && vliblist < vaddr + filesz) \ |
130 |
if (vgnuhash >= vaddr && vgnuhash < vaddr + filesz) \ |
131 |
liblistoff = offset + (vliblist - vaddr); \ |
131 |
gnuhashoff = offset + (vgnuhash - vaddr); \ |
132 |
} \ |
132 |
} \ |
133 |
\ |
133 |
\ |
134 |
/* Finally walk the symbol table. This should generally be fast as \ |
134 |
/* Finally walk the symbol table. This should generally be fast as \ |
Lines 137-183
static bool sb_check_exec(const char *filename, char *const argv[])
Link Here
|
137 |
*/ \ |
137 |
*/ \ |
138 |
if (symoff && stroff) { \ |
138 |
if (symoff && stroff) { \ |
139 |
/* Nowhere is the # of symbols recorded, or the size of the symbol \ |
139 |
/* Nowhere is the # of symbols recorded, or the size of the symbol \ |
140 |
* table. Instead, we do what glibc does: use the sysv hash table \ |
140 |
* table. Instead, we do what glibc does: use the gnu or sysv hash \ |
141 |
* if it exists, else assume that the string table always directly \ |
141 |
* table if it exists, else assume that the string table always directly \ |
142 |
* follows the symbol table. This seems like a poor assumption to \ |
142 |
* follows the symbol table. This seems like a poor assumption to \ |
143 |
* make, but glibc has gotten by this long. See determine_info in \ |
143 |
* make, but glibc has gotten by this long. See determine_info in \ |
144 |
* glibc's elf/dl-addr.c. \ |
144 |
* glibc's elf/dl-addr.c. \ |
145 |
* \ |
145 |
* \ |
146 |
* Turns out prelink will violate that assumption. Fortunately it \ |
|
|
147 |
* will insert its liblist at the same location all the time -- it \ |
148 |
* replaces the string table with its liblist table. \ |
149 |
* \ |
150 |
* Long term, we should behave the same as glibc and walk the gnu \ |
151 |
* hash table first before falling back to the raw symbol table. \ |
152 |
* \ |
153 |
* We don't sanity check the ranges here as you aren't executing \ |
146 |
* We don't sanity check the ranges here as you aren't executing \ |
154 |
* corrupt programs in the sandbox. \ |
147 |
* corrupt programs in the sandbox. \ |
155 |
*/ \ |
148 |
*/ \ |
156 |
sym = (void *)(elf + symoff); \ |
149 |
sym = (void *)(elf + symoff); \ |
157 |
if (vhash) { \ |
150 |
if (vgnuhash) { \ |
158 |
/* Hash entries are always 32-bits. */ \ |
151 |
uint32_t *hash32 = (void *)(elf + gnuhashoff); \ |
159 |
uint32_t *hashes = (void *)(elf + hashoff); \ |
152 |
/* use glibc's elf/dl-lookup.c:_dl_setup_hash() as a reference */ \ |
160 |
symend = sym + hashes[1]; \ |
153 |
/* DT_GNU_HASH header: */ \ |
161 |
} else if (vliblist) \ |
154 |
uint32_t nbuckets = *hash32++; \ |
162 |
symend = (void *)(elf + liblistoff); \ |
155 |
uint32_t symbias = *hash32++; \ |
163 |
else \ |
156 |
uint32_t bitmask_nwords = *hash32++; \ |
164 |
symend = (void *)(elf + stroff); \ |
157 |
hash32++; /* gnu_shift */ \ |
165 |
\ |
158 |
hash32 += n / 32 * bitmask_nwords; /* gnu_bitmask */ \ |
166 |
while (sym < symend) { \ |
159 |
uint32_t *gnu_buckets = hash32; \ |
167 |
char *symname = (void *)(elf + stroff + sym->st_name); \ |
160 |
hash32 += nbuckets; \ |
168 |
if (ELF##n##_ST_VISIBILITY(sym->st_other) == STV_DEFAULT && \ |
161 |
uint32_t *gnu_chain_zero = hash32 - symbias; \ |
169 |
sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE && \ |
162 |
\ |
170 |
sym->st_name && \ |
163 |
uint32_t bucket; \ |
171 |
/* Minor optimization to avoid strcmp. */ \ |
164 |
\ |
172 |
symname[0] == '_' && symname[1] == '_') { \ |
165 |
for (bucket = 0; bucket < nbuckets; bucket++) { \ |
173 |
/* Blacklist internal C library symbols. */ \ |
166 |
uint32_t symndx = gnu_buckets[bucket]; \ |
174 |
for (i = 0; i < ARRAY_SIZE(libc_alloc_syms); ++i) \ |
167 |
if (symndx != 0) { \ |
175 |
if (!strcmp(symname, libc_alloc_syms[i])) { \ |
168 |
const uint32_t *hasharr = &gnu_chain_zero[symndx]; \ |
176 |
run_in_process = false; \ |
169 |
do { \ |
177 |
goto use_trace; \ |
170 |
Elf##n##_Sym * s = &sym[symndx]; \ |
178 |
} \ |
171 |
\ |
|
|
172 |
/* keep in sync with 'vhash' case */ \ |
173 |
char *symname = (void *)(elf + stroff + s->st_name); \ |
174 |
if (ELF##n##_ST_VISIBILITY(s->st_other) == STV_DEFAULT && \ |
175 |
s->st_shndx != SHN_UNDEF && s->st_shndx < SHN_LORESERVE && \ |
176 |
s->st_name && \ |
177 |
/* Minor optimization to avoid strcmp. */ \ |
178 |
symname[0] == '_' && symname[1] == '_') { \ |
179 |
/* Blacklist internal C library symbols. */ \ |
180 |
for (i = 0; i < ARRAY_SIZE(libc_alloc_syms); ++i) \ |
181 |
if (!strcmp(symname, libc_alloc_syms[i])) { \ |
182 |
run_in_process = false; \ |
183 |
goto use_trace; \ |
184 |
} \ |
185 |
} \ |
186 |
++symndx; \ |
187 |
} while ((*hasharr++ & 1u) == 0); \ |
188 |
} \ |
189 |
} \ |
190 |
} else { \ |
191 |
if (vhash) { \ |
192 |
/* Hash entries are always 32-bits. */ \ |
193 |
uint32_t *hashes = (void *)(elf + hashoff); \ |
194 |
symend = sym + hashes[1]; \ |
195 |
} else \ |
196 |
symend = (void *)(elf + stroff); \ |
197 |
\ |
198 |
while (sym < symend) { \ |
199 |
/* keep insync with 'vgnuhash' case */ \ |
200 |
char *symname = (void *)(elf + stroff + sym->st_name); \ |
201 |
if (ELF##n##_ST_VISIBILITY(sym->st_other) == STV_DEFAULT && \ |
202 |
sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE && \ |
203 |
sym->st_name && \ |
204 |
/* Minor optimization to avoid strcmp. */ \ |
205 |
symname[0] == '_' && symname[1] == '_') { \ |
206 |
/* Blacklist internal C library symbols. */ \ |
207 |
for (i = 0; i < ARRAY_SIZE(libc_alloc_syms); ++i) \ |
208 |
if (!strcmp(symname, libc_alloc_syms[i])) { \ |
209 |
run_in_process = false; \ |
210 |
goto use_trace; \ |
211 |
} \ |
212 |
} \ |
213 |
++sym; \ |
179 |
} \ |
214 |
} \ |
180 |
++sym; \ |
|
|
181 |
} \ |
215 |
} \ |
182 |
} \ |
216 |
} \ |
183 |
\ |
217 |
\ |
184 |
- |
|
|