From a9ca947d038caadf2f6e19c3b9245c1bd0bed32e Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Thu, 19 Aug 2021 13:05:45 +0000 Subject: [PATCH] ksyms: Use pserialize(9) for kernel access to ksyms. This makes it available in interrupt context, e.g. for printing messages with kernel symbol names for return addresses as drm wants to do. --- sys/arch/sparc64/sparc64/machdep.c | 10 +++- sys/kern/kern_ksyms.c | 88 ++++++++++++++++++++++-------- sys/kern/subr_csan.c | 4 ++ sys/kern/subr_msan.c | 6 ++ sys/sys/ksyms.h | 8 +++ 5 files changed, 90 insertions(+), 26 deletions(-) diff --git a/sys/arch/sparc64/sparc64/machdep.c b/sys/arch/sparc64/sparc64/machdep.c index 2b78d8bf4440..8a7997a1d1bd 100644 --- a/sys/arch/sparc64/sparc64/machdep.c +++ b/sys/arch/sparc64/sparc64/machdep.c @@ -102,6 +102,7 @@ __KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.300 2021/08/09 21:08:06 andvar Exp $") #include #include #include +#include #include @@ -836,17 +837,22 @@ get_symbol_and_offset(const char **mod, const char **sym, vaddr_t *offset, vaddr { static char symbuf[256]; unsigned long symaddr; + int s, error; #if NKSYMS || defined(DDB) || defined(MODULAR) + s = pserialize_read_enter(); if (ksyms_getname(mod, sym, pc, KSYMS_CLOSEST|KSYMS_PROC|KSYMS_ANY) == 0) { - if (ksyms_getval(*mod, *sym, &symaddr, - KSYMS_CLOSEST|KSYMS_PROC|KSYMS_ANY) != 0) + error = ksyms_getval(*mod, *sym, &symaddr, + KSYMS_CLOSEST|KSYMS_PROC|KSYMS_ANY); + pserialize_read_exit(s); + if (error) goto failed; *offset = (vaddr_t)(pc - symaddr); return; } + pserialize_read_exit(s); #endif failed: snprintf(symbuf, sizeof symbuf, "%llx", (unsigned long long)pc); diff --git a/sys/kern/kern_ksyms.c b/sys/kern/kern_ksyms.c index 3dd7b1591557..9555cd110b4a 100644 --- a/sys/kern/kern_ksyms.c +++ b/sys/kern/kern_ksyms.c @@ -94,6 +94,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_ksyms.c,v 1.98 2021/07/18 06:57:28 mlelstv Exp #include #include #include +#include #ifdef DDB #include @@ -118,6 +119,7 @@ static bool ksyms_initted; static bool ksyms_loaded; static kmutex_t ksyms_lock __cacheline_aligned; static kcondvar_t ksyms_cv; +static pserialize_t ksyms_psz __read_mostly; static struct ksyms_symtab kernel_symtab; static void ksyms_hdr_init(const void *); @@ -146,6 +148,7 @@ int ksyms_strsz; int ksyms_ctfsz; /* this is not currently used by savecore(8) */ TAILQ_HEAD(ksyms_symtab_queue, ksyms_symtab) ksyms_symtabs = TAILQ_HEAD_INITIALIZER(ksyms_symtabs); +static struct pslist_head ksyms_symtabs_psz = PSLIST_INITIALIZER; static int ksyms_verify(const void *symstart, const void *strstart) @@ -247,6 +250,7 @@ ksyms_init(void) if (!ksyms_initted) { mutex_init(&ksyms_lock, MUTEX_DEFAULT, IPL_NONE); cv_init(&ksyms_cv, "ksyms"); + ksyms_psz = pserialize_create(); ksyms_initted = true; } } @@ -449,9 +453,19 @@ addsymtab(const char *name, void *symstart, size_t symsize, /* * Publish the symtab. Do this at splhigh to ensure ddb never * witnesses an inconsistent state of the queue, unless memory - * is so corrupt that we crash in TAILQ_INSERT_TAIL. + * is so corrupt that we crash in PSLIST_WRITER_INSERT_AFTER or + * TAILQ_INSERT_TAIL. */ + PSLIST_ENTRY_INIT(tab, sd_pslist); s = splhigh(); + if (TAILQ_EMPTY(&ksyms_symtabs)) { + PSLIST_WRITER_INSERT_HEAD(&ksyms_symtabs_psz, tab, sd_pslist); + } else { + struct ksyms_symtab *last; + + last = TAILQ_LAST(&ksyms_symtabs, ksyms_symtab_queue); + PSLIST_WRITER_INSERT_AFTER(last, tab, sd_pslist); + } TAILQ_INSERT_TAIL(&ksyms_symtabs, tab, sd_queue); splx(s); @@ -587,7 +601,9 @@ ksyms_addsyms_explicit(void *ehdr, void *symstart, size_t symsize, * "val" is a pointer to the corresponding value, if call succeeded. * Returns 0 if success or ENOENT if no such entry. * - * Call with ksyms_lock, unless known that the symbol table can't change. + * If symp is nonnull, caller must hold ksyms_lock or module_lock, have + * ksyms_opencnt nonzero, be in a pserialize read section, be in ddb + * with all other CPUs quiescent. */ int ksyms_getval_unlocked(const char *mod, const char *sym, Elf_Sym **symp, @@ -595,51 +611,61 @@ ksyms_getval_unlocked(const char *mod, const char *sym, Elf_Sym **symp, { struct ksyms_symtab *st; Elf_Sym *es; + int s, error = ENOENT; #ifdef KSYMS_DEBUG if (ksyms_debug & FOLLOW_CALLS) printf("%s: mod %s sym %s valp %p\n", __func__, mod, sym, val); #endif - TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { + s = pserialize_read_enter(); + PSLIST_READER_FOREACH(st, &ksyms_symtabs_psz, struct ksyms_symtab, + sd_pslist) { if (mod != NULL && strcmp(st->sd_name, mod)) continue; if ((es = findsym(sym, st, type)) != NULL) { *val = es->st_value; if (symp) *symp = es; - return 0; + error = 0; + break; } } - return ENOENT; + pserialize_read_exit(s); + return error; } int ksyms_getval(const char *mod, const char *sym, unsigned long *val, int type) { - int rc; if (!ksyms_loaded) return ENOENT; - mutex_enter(&ksyms_lock); - rc = ksyms_getval_unlocked(mod, sym, NULL, val, type); - mutex_exit(&ksyms_lock); - return rc; + /* No locking needed -- we read the table pserialized. */ + return ksyms_getval_unlocked(mod, sym, NULL, val, type); } +/* + * ksyms_get_mod(mod) + * + * Return the symtab for the given module name. Caller must ensure + * that the module cannot be unloaded until after this returns. + */ struct ksyms_symtab * ksyms_get_mod(const char *mod) { struct ksyms_symtab *st; + int s; - mutex_enter(&ksyms_lock); - TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { + s = pserialize_read_enter(); + PSLIST_READER_FOREACH(st, &ksyms_symtabs_psz, struct ksyms_symtab, + sd_pslist) { if (mod != NULL && strcmp(st->sd_name, mod)) continue; break; } - mutex_exit(&ksyms_lock); + pserialize_read_exit(s); return st; } @@ -695,7 +721,9 @@ ksyms_mod_foreach(const char *mod, ksyms_callback_t callback, void *opaque) * Get "mod" and "symbol" associated with an address. * Returns 0 if success or ENOENT if no such entry. * - * Call with ksyms_lock, unless known that the symbol table can't change. + * Caller must hold ksyms_lock or module_lock, have ksyms_opencnt + * nonzero, be in a pserialize read section, or be in ddb with all + * other CPUs quiescent. */ int ksyms_getname(const char **mod, const char **sym, vaddr_t v, int f) @@ -710,7 +738,8 @@ ksyms_getname(const char **mod, const char **sym, vaddr_t v, int f) if (!ksyms_loaded) return ENOENT; - TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { + PSLIST_READER_FOREACH(st, &ksyms_symtabs_psz, struct ksyms_symtab, + sd_pslist) { if (v < st->sd_minsym || v > st->sd_maxsym) continue; sz = st->sd_symsize/sizeof(Elf_Sym); @@ -795,12 +824,21 @@ ksyms_modunload(const char *name) /* * Remove the symtab. Do this at splhigh to ensure ddb never * witnesses an inconsistent state of the queue, unless memory - * is so corrupt that we crash in TAILQ_REMOVE. + * is so corrupt that we crash in TAILQ_REMOVE or + * PSLIST_WRITER_REMOVE. */ s = splhigh(); TAILQ_REMOVE(&ksyms_symtabs, st, sd_queue); + PSLIST_WRITER_REMOVE(st, sd_pslist); splx(s); + /* + * And wait a grace period, in case there are any pserialized + * readers in flight. + */ + pserialize_perform(ksyms_psz); + PSLIST_ENTRY_DESTROY(st, sd_pslist); + /* Recompute the ksyms sizes now that we've removed st. */ ksyms_sizes_calc(); mutex_exit(&ksyms_lock); @@ -1142,7 +1180,7 @@ ksymsioctl(dev_t dev, u_long cmd, void *data, int fflag, struct lwp *l) unsigned long val; int error = 0; char *str = NULL; - int len; + int len, s; /* Read ksyms_maxlen only once while not holding the lock. */ len = ksyms_maxlen; @@ -1173,8 +1211,9 @@ ksymsioctl(dev_t dev, u_long cmd, void *data, int fflag, struct lwp *l) * Use the in-kernel symbol lookup code for fast * retreival of a symbol. */ - mutex_enter(&ksyms_lock); - TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { + s = pserialize_read_enter(); + PSLIST_READER_FOREACH(st, &ksyms_symtabs_psz, + struct ksyms_symtab, sd_pslist) { if ((sym = findsym(str, st, KSYMS_ANY)) == NULL) continue; #ifdef notdef @@ -1188,10 +1227,10 @@ ksymsioctl(dev_t dev, u_long cmd, void *data, int fflag, struct lwp *l) } if (sym != NULL) { memcpy(©, sym, sizeof(copy)); - mutex_exit(&ksyms_lock); + pserialize_read_exit(s); error = copyout(©, okg->kg_sym, sizeof(Elf_Sym)); } else { - mutex_exit(&ksyms_lock); + pserialize_read_exit(s); error = ENOENT; } kmem_free(str, len); @@ -1213,8 +1252,9 @@ ksymsioctl(dev_t dev, u_long cmd, void *data, int fflag, struct lwp *l) * Use the in-kernel symbol lookup code for fast * retreival of a symbol. */ - mutex_enter(&ksyms_lock); - TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { + s = pserialize_read_enter(); + PSLIST_READER_FOREACH(st, &ksyms_symtabs_psz, + struct ksyms_symtab, sd_pslist) { if ((sym = findsym(str, st, KSYMS_ANY)) == NULL) continue; #ifdef notdef @@ -1231,7 +1271,7 @@ ksymsioctl(dev_t dev, u_long cmd, void *data, int fflag, struct lwp *l) } else { error = ENOENT; } - mutex_exit(&ksyms_lock); + pserialize_read_exit(s); kmem_free(str, len); break; diff --git a/sys/kern/subr_csan.c b/sys/kern/subr_csan.c index 9f8af8815d12..d99c82bdcc72 100644 --- a/sys/kern/subr_csan.c +++ b/sys/kern/subr_csan.c @@ -40,6 +40,7 @@ __KERNEL_RCSID(0, "$NetBSD: subr_csan.c,v 1.10 2020/09/10 14:04:45 maxv Exp $"); #include #include #include +#include #ifdef KCSAN_PANIC #define REPORT panic @@ -94,7 +95,9 @@ static inline void kcsan_report(csan_cell_t *new, cpuid_t newcpu, csan_cell_t *old, cpuid_t oldcpu) { const char *newsym, *oldsym; + int s; + s = pserialize_read_enter(); if (ksyms_getname(NULL, &newsym, (vaddr_t)new->pc, KSYMS_PROC) != 0) { newsym = "Unknown"; } @@ -110,6 +113,7 @@ kcsan_report(csan_cell_t *new, cpuid_t newcpu, csan_cell_t *old, cpuid_t oldcpu) oldcpu, (old->atomic ? "Atomic " : ""), (old->write ? "Write" : "Read"), (void *)old->addr, old->size, (void *)old->pc, oldsym); + pserialize_read_exit(s); kcsan_md_unwind(); } diff --git a/sys/kern/subr_msan.c b/sys/kern/subr_msan.c index b815abcad53b..26c4449c25e6 100644 --- a/sys/kern/subr_msan.c +++ b/sys/kern/subr_msan.c @@ -152,6 +152,7 @@ kmsan_report_hook(const void *addr, size_t size, size_t off, const char *hook) uintptr_t ptr; char buf[128]; int type; + int s; if (__predict_false(panicstr != NULL || db_active || kmsan_reporting)) return; @@ -172,6 +173,7 @@ kmsan_report_hook(const void *addr, size_t size, size_t off, const char *hook) typename = kmsan_orig_name(type); if (kmsan_md_is_pc(ptr)) { + s = pserialize_read_enter(); if (ksyms_getname(&mod, &sym, (vaddr_t)ptr, KSYMS_PROC)) { REPORT("MSan: Uninitialized %s Memory In %s " "At Offset %zu/%zu, IP %p\n", typename, hook, off, @@ -181,6 +183,7 @@ kmsan_report_hook(const void *addr, size_t size, size_t off, const char *hook) "At Offset %zu/%zu, From %s()\n", typename, hook, off, size, sym); } + pserialize_read_exit(s); } else { var = (char *)ptr + 4; strlcpy(buf, var, sizeof(buf)); @@ -208,6 +211,7 @@ kmsan_report_inline(msan_orig_t orig, unsigned long pc) uintptr_t ptr; char buf[128]; int type; + int s; if (__predict_false(panicstr != NULL || db_active || kmsan_reporting)) return; @@ -225,6 +229,7 @@ kmsan_report_inline(msan_orig_t orig, unsigned long pc) typename = kmsan_orig_name(type); if (kmsan_md_is_pc(ptr)) { + s = pserialize_read_enter(); if (ksyms_getname(&mod, &sym, (vaddr_t)ptr, KSYMS_PROC)) { REPORT("MSan: Uninitialized %s Memory, " "Origin %x\n", typename, orig); @@ -232,6 +237,7 @@ kmsan_report_inline(msan_orig_t orig, unsigned long pc) REPORT("MSan: Uninitialized %s Memory " "From %s()\n", typename, sym); } + pserialize_read_exit(s); } else { var = (char *)ptr + 4; strlcpy(buf, var, sizeof(buf)); diff --git a/sys/sys/ksyms.h b/sys/sys/ksyms.h index 0be57f60c2db..4aece01bf0aa 100644 --- a/sys/sys/ksyms.h +++ b/sys/sys/ksyms.h @@ -39,6 +39,10 @@ #include #include +#ifdef _KERNEL +#include +#endif + struct ksyms_symtab { TAILQ_ENTRY(ksyms_symtab) sd_queue; /* All active tables */ const char *sd_name; /* Name of this table */ @@ -55,6 +59,10 @@ struct ksyms_symtab { int sd_ctfsize; /* Size in bytes of CTF contents */ uint32_t *sd_nmap; /* Name map for sorted symbols */ int sd_nmapsize; /* Total span of map */ + +#ifdef _KERNEL + struct pslist_entry sd_pslist; +#endif }; /*