git: 95c20faf11a1 - main - kernel linker: do not read debug symbol tables for non-debug symbols

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Wed, 08 Dec 2021 21:59:28 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=95c20faf11a1af6924f97ec4aafc32d899fea8b0

commit 95c20faf11a1af6924f97ec4aafc32d899fea8b0
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2021-11-07 08:37:48 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2021-12-08 21:32:29 +0000

    kernel linker: do not read debug symbol tables for non-debug symbols
    
    In particular, this prevents resolving locals from other files.
    To access debug symbol tables, add LINKER_LOOKUP_DEBUG_SYMBOL and
    LINKER_DEBUG_SYMBOL_VALUES kobj methods, which are allowed to use
    any types of present symbols in all tables.
    
    PR:     207898
    Reviewed by:    emaste, markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D32878
---
 sys/kern/kern_linker.c  |  2 +-
 sys/kern/link_elf.c     | 79 +++++++++++++++++++++++++++++++++++++++----------
 sys/kern/link_elf_obj.c | 61 +++++++++++++++++++++++++++++++-------
 sys/kern/linker_if.m    | 12 ++++++++
 4 files changed, 126 insertions(+), 28 deletions(-)

diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
index b34131604fef..2e4c95f16c8f 100644
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -912,7 +912,7 @@ linker_debug_lookup(const char *symstr, c_linker_sym_t *sym)
 	linker_file_t lf;
 
 	TAILQ_FOREACH(lf, &linker_files, link) {
-		if (LINKER_LOOKUP_SYMBOL(lf, symstr, sym) == 0)
+		if (LINKER_LOOKUP_DEBUG_SYMBOL(lf, symstr, sym) == 0)
 			return (0);
 	}
 	return (ENOENT);
diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c
index 075238b91d95..dc8002be0e89 100644
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -144,8 +144,12 @@ static int	link_elf_load_file(linker_class_t, const char *,
 		    linker_file_t *);
 static int	link_elf_lookup_symbol(linker_file_t, const char *,
 		    c_linker_sym_t *);
+static int	link_elf_lookup_debug_symbol(linker_file_t, const char *,
+		    c_linker_sym_t *);
 static int	link_elf_symbol_values(linker_file_t, c_linker_sym_t,
 		    linker_symval_t *);
+static int	link_elf_debug_symbol_values(linker_file_t, c_linker_sym_t,
+		    linker_symval_t*);
 static int	link_elf_search_symbol(linker_file_t, caddr_t,
 		    c_linker_sym_t *, long *);
 
@@ -164,7 +168,9 @@ static int	elf_lookup(linker_file_t, Elf_Size, int, Elf_Addr *);
 
 static kobj_method_t link_elf_methods[] = {
 	KOBJMETHOD(linker_lookup_symbol,	link_elf_lookup_symbol),
+	KOBJMETHOD(linker_lookup_debug_symbol,	link_elf_lookup_debug_symbol),
 	KOBJMETHOD(linker_symbol_values,	link_elf_symbol_values),
+	KOBJMETHOD(linker_debug_symbol_values,	link_elf_debug_symbol_values),
 	KOBJMETHOD(linker_search_symbol,	link_elf_search_symbol),
 	KOBJMETHOD(linker_unload,		link_elf_unload_file),
 	KOBJMETHOD(linker_load_file,		link_elf_load_file),
@@ -1490,14 +1496,14 @@ elf_hash(const char *name)
 }
 
 static int
-link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
+link_elf_lookup_symbol1(linker_file_t lf, const char *name, c_linker_sym_t *sym,
+    bool see_local)
 {
 	elf_file_t ef = (elf_file_t) lf;
 	unsigned long symnum;
 	const Elf_Sym* symp;
 	const char *strp;
 	unsigned long hash;
-	int i;
 
 	/* If we don't have a hash, bail. */
 	if (ef->buckets == NULL || ef->nbuckets == 0) {
@@ -1528,8 +1534,11 @@ link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
 			    (symp->st_value != 0 &&
 			    (ELF_ST_TYPE(symp->st_info) == STT_FUNC ||
 			    ELF_ST_TYPE(symp->st_info) == STT_GNU_IFUNC))) {
-				*sym = (c_linker_sym_t) symp;
-				return (0);
+				if (see_local ||
+				    ELF_ST_BIND(symp->st_info) != STB_LOCAL) {
+					*sym = (c_linker_sym_t) symp;
+					return (0);
+				}
 			}
 			return (ENOENT);
 		}
@@ -1537,11 +1546,27 @@ link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
 		symnum = ef->chains[symnum];
 	}
 
-	/* If we have not found it, look at the full table (if loaded) */
-	if (ef->symtab == ef->ddbsymtab)
-		return (ENOENT);
+	return (ENOENT);
+}
+
+static int
+link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
+{
+	return (link_elf_lookup_symbol1(lf, name, sym, false));
+}
+
+static int
+link_elf_lookup_debug_symbol(linker_file_t lf, const char *name,
+    c_linker_sym_t *sym)
+{
+	elf_file_t ef = (elf_file_t)lf;
+	const Elf_Sym* symp;
+	const char *strp;
+	int i;
+
+	if (link_elf_lookup_symbol1(lf, name, sym, true) == 0)
+		return (0);
 
-	/* Exhaustive search */
 	for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
 		strp = ef->ddbstrtab + symp->st_name;
 		if (strcmp(name, strp) == 0) {
@@ -1560,8 +1585,8 @@ link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
 }
 
 static int
-link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
-    linker_symval_t *symval)
+link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym,
+    linker_symval_t *symval, bool see_local)
 {
 	elf_file_t ef;
 	const Elf_Sym *es;
@@ -1569,7 +1594,9 @@ link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
 
 	ef = (elf_file_t)lf;
 	es = (const Elf_Sym *)sym;
-	if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) {
+	if (es >= ef->symtab && es < ef->symtab + ef->nchains) {
+		if (!see_local && ELF_ST_BIND(es->st_info) == STB_LOCAL)
+			return (ENOENT);
 		symval->name = ef->strtab + es->st_name;
 		val = (caddr_t)ef->address + es->st_value;
 		if (ELF_ST_TYPE(es->st_info) == STT_GNU_IFUNC)
@@ -1578,8 +1605,29 @@ link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
 		symval->size = es->st_size;
 		return (0);
 	}
+	return (ENOENT);
+}
+
+static int
+link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
+    linker_symval_t *symval)
+{
+	return (link_elf_symbol_values1(lf, sym, symval, false));
+}
+
+static int
+link_elf_debug_symbol_values(linker_file_t lf, c_linker_sym_t sym,
+    linker_symval_t *symval)
+{
+	elf_file_t ef = (elf_file_t)lf;
+	const Elf_Sym *es = (const Elf_Sym *)sym;
+	caddr_t val;
+
+	if (link_elf_symbol_values1(lf, sym, symval, true) == 0)
+		return (0);
 	if (ef->symtab == ef->ddbsymtab)
 		return (ENOENT);
+
 	if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) {
 		symval->name = ef->ddbstrtab + es->st_name;
 		val = (caddr_t)ef->address + es->st_value;
@@ -1597,7 +1645,7 @@ link_elf_search_symbol(linker_file_t lf, caddr_t value,
     c_linker_sym_t *sym, long *diffp)
 {
 	elf_file_t ef = (elf_file_t)lf;
-	u_long off = (uintptr_t) (void *)value;
+	u_long off = (uintptr_t)(void *)value;
 	u_long diff = off;
 	u_long st_value;
 	const Elf_Sym *es;
@@ -1719,11 +1767,10 @@ link_elf_each_function_nameval(linker_file_t file,
 		if (symp->st_value != 0 &&
 		    (ELF_ST_TYPE(symp->st_info) == STT_FUNC ||
 		    ELF_ST_TYPE(symp->st_info) == STT_GNU_IFUNC)) {
-			error = link_elf_symbol_values(file,
+			error = link_elf_debug_symbol_values(file,
 			    (c_linker_sym_t) symp, &symval);
-			if (error != 0)
-				return (error);
-			error = callback(file, i, &symval, opaque);
+			if (error == 0)
+				error = callback(file, i, &symval, opaque);
 			if (error != 0)
 				return (error);
 		}
diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c
index fbefd0f8d7ca..ec2319ffad47 100644
--- a/sys/kern/link_elf_obj.c
+++ b/sys/kern/link_elf_obj.c
@@ -131,8 +131,12 @@ static int	link_elf_link_preload_finish(linker_file_t);
 static int	link_elf_load_file(linker_class_t, const char *, linker_file_t *);
 static int	link_elf_lookup_symbol(linker_file_t, const char *,
 		    c_linker_sym_t *);
+static int	link_elf_lookup_debug_symbol(linker_file_t, const char *,
+		    c_linker_sym_t *);
 static int	link_elf_symbol_values(linker_file_t, c_linker_sym_t,
 		    linker_symval_t *);
+static int	link_elf_debug_symbol_values(linker_file_t, c_linker_sym_t,
+		    linker_symval_t *);
 static int	link_elf_search_symbol(linker_file_t, caddr_t value,
 		    c_linker_sym_t *sym, long *diffp);
 
@@ -153,7 +157,9 @@ static int	elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps,
 
 static kobj_method_t link_elf_methods[] = {
 	KOBJMETHOD(linker_lookup_symbol,	link_elf_lookup_symbol),
+	KOBJMETHOD(linker_lookup_debug_symbol,	link_elf_lookup_debug_symbol),
 	KOBJMETHOD(linker_symbol_values,	link_elf_symbol_values),
+	KOBJMETHOD(linker_debug_symbol_values,	link_elf_debug_symbol_values),
 	KOBJMETHOD(linker_search_symbol,	link_elf_search_symbol),
 	KOBJMETHOD(linker_unload,		link_elf_unload_file),
 	KOBJMETHOD(linker_load_file,		link_elf_load_file),
@@ -1424,9 +1430,10 @@ relocate_file(elf_file_t ef)
 }
 
 static int
-link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
+link_elf_lookup_symbol1(linker_file_t lf, const char *name, c_linker_sym_t *sym,
+    bool see_local)
 {
-	elf_file_t ef = (elf_file_t) lf;
+	elf_file_t ef = (elf_file_t)lf;
 	const Elf_Sym *symp;
 	const char *strp;
 	int i;
@@ -1434,16 +1441,33 @@ link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
 	for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
 		strp = ef->ddbstrtab + symp->st_name;
 		if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) {
-			*sym = (c_linker_sym_t) symp;
-			return 0;
+			if (see_local ||
+			    ELF_ST_BIND(symp->st_info) == STB_GLOBAL) {
+				*sym = (c_linker_sym_t) symp;
+				return (0);
+			}
+			return (ENOENT);
 		}
 	}
 	return (ENOENT);
 }
 
 static int
-link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
-    linker_symval_t *symval)
+link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
+{
+	return (link_elf_lookup_symbol1(lf, name, sym, false));
+}
+
+static int
+link_elf_lookup_debug_symbol(linker_file_t lf, const char *name,
+    c_linker_sym_t *sym)
+{
+	return (link_elf_lookup_symbol1(lf, name, sym, true));
+}
+
+static int
+link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym,
+    linker_symval_t *symval, bool see_local)
 {
 	elf_file_t ef;
 	const Elf_Sym *es;
@@ -1453,6 +1477,8 @@ link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
 	es = (const Elf_Sym*) sym;
 	val = (caddr_t)es->st_value;
 	if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) {
+		if (!see_local && ELF_ST_BIND(es->st_info) == STB_LOCAL)
+			return (ENOENT);
 		symval->name = ef->ddbstrtab + es->st_name;
 		val = (caddr_t)es->st_value;
 		if (ELF_ST_TYPE(es->st_info) == STT_GNU_IFUNC)
@@ -1464,6 +1490,20 @@ link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
 	return (ENOENT);
 }
 
+static int
+link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
+    linker_symval_t *symval)
+{
+	return (link_elf_symbol_values1(lf, sym, symval, false));
+}
+
+static int
+link_elf_debug_symbol_values(linker_file_t lf, c_linker_sym_t sym,
+    linker_symval_t *symval)
+{
+	return (link_elf_symbol_values1(lf, sym, symval, true));
+}
+
 static int
 link_elf_search_symbol(linker_file_t lf, caddr_t value,
     c_linker_sym_t *sym, long *diffp)
@@ -1566,12 +1606,11 @@ link_elf_each_function_nameval(linker_file_t file,
 		if (symp->st_value != 0 &&
 		    (ELF_ST_TYPE(symp->st_info) == STT_FUNC ||
 		    ELF_ST_TYPE(symp->st_info) == STT_GNU_IFUNC)) {
-			error = link_elf_symbol_values(file,
+			error = link_elf_debug_symbol_values(file,
 			    (c_linker_sym_t)symp, &symval);
-			if (error)
-				return (error);
-			error = callback(file, i, &symval, opaque);
-			if (error)
+			if (error == 0)
+				error = callback(file, i, &symval, opaque);
+			if (error != 0)
 				return (error);
 		}
 	}
diff --git a/sys/kern/linker_if.m b/sys/kern/linker_if.m
index a583a0369ebc..524f786ccac8 100644
--- a/sys/kern/linker_if.m
+++ b/sys/kern/linker_if.m
@@ -40,12 +40,24 @@ METHOD int lookup_symbol {
     c_linker_sym_t*	symp;
 };
 
+METHOD int lookup_debug_symbol {
+    linker_file_t	file;
+    const char*		name;
+    c_linker_sym_t*	symp;
+};
+
 METHOD int symbol_values {
     linker_file_t	file;
     c_linker_sym_t	sym;
     linker_symval_t*	valp;
 };
 
+METHOD int debug_symbol_values {
+    linker_file_t	file;
+    c_linker_sym_t	sym;
+    linker_symval_t*	valp;
+};
+
 METHOD int search_symbol {
     linker_file_t	file;
     caddr_t		value;