History

tccrun: fix OOB read in elf_output_obj after tcc_relocate with -g

When -g is set, cleanup_sections() preserves all sections after relocate, including the SHT_RELX relocation sections. cleanup_symbols() drops all STB_LOCAL symbols from the symbol table, which renumbers the remaining globals. The kept relas still hold r_info indices from the pre-cleanup numbering, but nothing rewrites them. A later elf_output_obj() call ends up in sort_syms() / update_relocs(), which allocates old_to_new_syms[] sized for the post-cleanup (smaller) symtab and then indexes it with the stale (larger) sym_index values from those rela entries, reading past the end of the array. Fix: when do_debug is set, build an old->new index map while re-adding globals, then walk every retained SHT_RELX section linked to the symtab and rewrite its r_info entries. Locals (and the undef sym at 0) map to 0 by tcc_mallocz, so relocations against dropped locals now refer to SHN_UNDEF - the best we can do without preserving the locals themselves. Reproducible with libtcc_debug.c if the program contains enough static helpers / string literals for the symtab to actually shrink; observed under valgrind in jitc (https://github.com/cyanogilvie/jitc) when compiling re2c-generated state machines with -g.

Cyan Ogilvie committed 6 months ago

Commit 4768b111 parent b39da9f1 file · +37 −3Browse files

Unified · Split

tccrun.c+37−3
@@ -260,22 +260,56 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) · 259 unmodified lines
260}260}
261 261
262/* ------------------------------------------------------------- */262/* ------------------------------------------------------------- */
263/* remove all STB_LOCAL symbols */263/* remove all STB_LOCAL symbols. When do_debug is set, cleanup_sections()
264 keeps the relocation sections alive for a later elf_output_obj() call;
265 the r_info indices in those rela entries refer to the pre-cleanup
266 symbol numbering, so we must build an old->new map and rewrite them.
267 Without that, sort_syms() in elf_output_obj() allocates an
268 old_to_new_syms[] array sized for the post-cleanup (smaller) symtab
269 and indexes it with the stale (larger) sym_index values, reading off
270 the end of the array. */
264static void cleanup_symbols(TCCState *s1)271static void cleanup_symbols(TCCState *s1)
265{272{
266 Section *s = s1->symtab;273 Section *s = s1->symtab;
267 int sym_index, end_sym = s->data_offset / sizeof (ElfSym);274 int sym_index, end_sym = s->data_offset / sizeof (ElfSym);
275 int *old_to_new = s1->do_debug
276 ? tcc_mallocz(end_sym * sizeof(int))
277 : NULL;
268 /* reset symtab */278 /* reset symtab */
269 s->data_offset = s->link->data_offset = s->hash->data_offset = 0;279 s->data_offset = s->link->data_offset = s->hash->data_offset = 0;
270 init_symtab(s);280 init_symtab(s);
271 /* add global symbols again */281 /* add global symbols again, recording the new index of each */
272 for (sym_index = 1; sym_index < end_sym; ++sym_index) {282 for (sym_index = 1; sym_index < end_sym; ++sym_index) {
273 ElfW(Sym) *sym = &((ElfW(Sym) *)s->data)[sym_index];283 ElfW(Sym) *sym = &((ElfW(Sym) *)s->data)[sym_index];
274 const char *name = (char *)s->link->data + sym->st_name;284 const char *name = (char *)s->link->data + sym->st_name;
285 int new_idx;
275 if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)286 if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)
276 continue;287 continue;
277 //printf("sym %s\n", name);288 //printf("sym %s\n", name);
278 put_elf_sym(s, sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx, name);289 new_idx = put_elf_sym(s, sym->st_value, sym->st_size,
290 sym->st_info, sym->st_other, sym->st_shndx, name);
291 if (old_to_new)
292 old_to_new[sym_index] = new_idx;
293 }
294 if (old_to_new) {
295 int i;
296 for (i = 1; i < s1->nb_sections; i++) {
297 Section *sr = s1->sections[i];
298 ElfW_Rel *rel;
299 if (sr->sh_type != SHT_RELX || sr->link != s)
300 continue;
301 for_each_elem(sr, 0, rel, ElfW_Rel) {
302 int old = ELFW(R_SYM)(rel->r_info);
303 int type = ELFW(R_TYPE)(rel->r_info);
304 /* Locals (and the undef sym at 0) map to 0 by
305 tcc_mallocz; relocations against dropped locals now
306 refer to SHN_UNDEF, which is the best we can do
307 without preserving the locals themselves. */
308 int new_idx = (old > 0 && old < end_sym) ? old_to_new[old] : 0;
309 rel->r_info = ELFW(R_INFO)(new_idx, type);
310 }
311 }
312 tcc_free(old_to_new);
279 }313 }
280}314}
281 315

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t