forked from mirrors/linux
		
	lib: code tagging framework
Add basic infrastructure to support code tagging which stores tag common information consisting of the module name, function, file name and line number. Provide functions to register a new code tag type and navigate between code tags. Link: https://lkml.kernel.org/r/20240321163705.3067592-11-surenb@google.com Co-developed-by: Kent Overstreet <kent.overstreet@linux.dev> Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev> Signed-off-by: Suren Baghdasaryan <surenb@google.com> Tested-by: Kees Cook <keescook@chromium.org> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Alex Gaynor <alex.gaynor@gmail.com> Cc: Alice Ryhl <aliceryhl@google.com> Cc: Andreas Hindborg <a.hindborg@samsung.com> Cc: Benno Lossin <benno.lossin@proton.me> Cc: "Björn Roy Baron" <bjorn3_gh@protonmail.com> Cc: Boqun Feng <boqun.feng@gmail.com> Cc: Christoph Lameter <cl@linux.com> Cc: Dennis Zhou <dennis@kernel.org> Cc: Gary Guo <gary@garyguo.net> Cc: Miguel Ojeda <ojeda@kernel.org> Cc: Pasha Tatashin <pasha.tatashin@soleen.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Tejun Heo <tj@kernel.org> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Wedson Almeida Filho <wedsonaf@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									53ce720359
								
							
						
					
					
						commit
						916cc5167c
					
				
					 4 changed files with 292 additions and 0 deletions
				
			
		
							
								
								
									
										68
									
								
								include/linux/codetag.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								include/linux/codetag.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,68 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0 */
 | 
			
		||||
/*
 | 
			
		||||
 * code tagging framework
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _LINUX_CODETAG_H
 | 
			
		||||
#define _LINUX_CODETAG_H
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
 | 
			
		||||
struct codetag_iterator;
 | 
			
		||||
struct codetag_type;
 | 
			
		||||
struct codetag_module;
 | 
			
		||||
struct seq_buf;
 | 
			
		||||
struct module;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * An instance of this structure is created in a special ELF section at every
 | 
			
		||||
 * code location being tagged.  At runtime, the special section is treated as
 | 
			
		||||
 * an array of these.
 | 
			
		||||
 */
 | 
			
		||||
struct codetag {
 | 
			
		||||
	unsigned int flags; /* used in later patches */
 | 
			
		||||
	unsigned int lineno;
 | 
			
		||||
	const char *modname;
 | 
			
		||||
	const char *function;
 | 
			
		||||
	const char *filename;
 | 
			
		||||
} __aligned(8);
 | 
			
		||||
 | 
			
		||||
union codetag_ref {
 | 
			
		||||
	struct codetag *ct;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct codetag_type_desc {
 | 
			
		||||
	const char *section;
 | 
			
		||||
	size_t tag_size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct codetag_iterator {
 | 
			
		||||
	struct codetag_type *cttype;
 | 
			
		||||
	struct codetag_module *cmod;
 | 
			
		||||
	unsigned long mod_id;
 | 
			
		||||
	struct codetag *ct;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef MODULE
 | 
			
		||||
#define CT_MODULE_NAME KBUILD_MODNAME
 | 
			
		||||
#else
 | 
			
		||||
#define CT_MODULE_NAME NULL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define CODE_TAG_INIT {					\
 | 
			
		||||
	.modname	= CT_MODULE_NAME,		\
 | 
			
		||||
	.function	= __func__,			\
 | 
			
		||||
	.filename	= __FILE__,			\
 | 
			
		||||
	.lineno		= __LINE__,			\
 | 
			
		||||
	.flags		= 0,				\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void codetag_lock_module_list(struct codetag_type *cttype, bool lock);
 | 
			
		||||
struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype);
 | 
			
		||||
struct codetag *codetag_next_ct(struct codetag_iterator *iter);
 | 
			
		||||
 | 
			
		||||
void codetag_to_text(struct seq_buf *out, struct codetag *ct);
 | 
			
		||||
 | 
			
		||||
struct codetag_type *
 | 
			
		||||
codetag_register_type(const struct codetag_type_desc *desc);
 | 
			
		||||
 | 
			
		||||
#endif /* _LINUX_CODETAG_H */
 | 
			
		||||
| 
						 | 
				
			
			@ -968,6 +968,10 @@ config DEBUG_STACKOVERFLOW
 | 
			
		|||
 | 
			
		||||
	  If in doubt, say "N".
 | 
			
		||||
 | 
			
		||||
config CODE_TAGGING
 | 
			
		||||
	bool
 | 
			
		||||
	select KALLSYMS
 | 
			
		||||
 | 
			
		||||
source "lib/Kconfig.kasan"
 | 
			
		||||
source "lib/Kconfig.kfence"
 | 
			
		||||
source "lib/Kconfig.kmsan"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -233,6 +233,7 @@ obj-$(CONFIG_OF_RECONFIG_NOTIFIER_ERROR_INJECT) += \
 | 
			
		|||
	of-reconfig-notifier-error-inject.o
 | 
			
		||||
obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_CODE_TAGGING) += codetag.o
 | 
			
		||||
lib-$(CONFIG_GENERIC_BUG) += bug.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										219
									
								
								lib/codetag.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								lib/codetag.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,219 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
#include <linux/codetag.h>
 | 
			
		||||
#include <linux/idr.h>
 | 
			
		||||
#include <linux/kallsyms.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/seq_buf.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
 | 
			
		||||
struct codetag_type {
 | 
			
		||||
	struct list_head link;
 | 
			
		||||
	unsigned int count;
 | 
			
		||||
	struct idr mod_idr;
 | 
			
		||||
	struct rw_semaphore mod_lock; /* protects mod_idr */
 | 
			
		||||
	struct codetag_type_desc desc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct codetag_range {
 | 
			
		||||
	struct codetag *start;
 | 
			
		||||
	struct codetag *stop;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct codetag_module {
 | 
			
		||||
	struct module *mod;
 | 
			
		||||
	struct codetag_range range;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static DEFINE_MUTEX(codetag_lock);
 | 
			
		||||
static LIST_HEAD(codetag_types);
 | 
			
		||||
 | 
			
		||||
void codetag_lock_module_list(struct codetag_type *cttype, bool lock)
 | 
			
		||||
{
 | 
			
		||||
	if (lock)
 | 
			
		||||
		down_read(&cttype->mod_lock);
 | 
			
		||||
	else
 | 
			
		||||
		up_read(&cttype->mod_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype)
 | 
			
		||||
{
 | 
			
		||||
	struct codetag_iterator iter = {
 | 
			
		||||
		.cttype = cttype,
 | 
			
		||||
		.cmod = NULL,
 | 
			
		||||
		.mod_id = 0,
 | 
			
		||||
		.ct = NULL,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	return iter;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct codetag *get_first_module_ct(struct codetag_module *cmod)
 | 
			
		||||
{
 | 
			
		||||
	return cmod->range.start < cmod->range.stop ? cmod->range.start : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline
 | 
			
		||||
struct codetag *get_next_module_ct(struct codetag_iterator *iter)
 | 
			
		||||
{
 | 
			
		||||
	struct codetag *res = (struct codetag *)
 | 
			
		||||
			((char *)iter->ct + iter->cttype->desc.tag_size);
 | 
			
		||||
 | 
			
		||||
	return res < iter->cmod->range.stop ? res : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct codetag *codetag_next_ct(struct codetag_iterator *iter)
 | 
			
		||||
{
 | 
			
		||||
	struct codetag_type *cttype = iter->cttype;
 | 
			
		||||
	struct codetag_module *cmod;
 | 
			
		||||
	struct codetag *ct;
 | 
			
		||||
 | 
			
		||||
	lockdep_assert_held(&cttype->mod_lock);
 | 
			
		||||
 | 
			
		||||
	if (unlikely(idr_is_empty(&cttype->mod_idr)))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	ct = NULL;
 | 
			
		||||
	while (true) {
 | 
			
		||||
		cmod = idr_find(&cttype->mod_idr, iter->mod_id);
 | 
			
		||||
 | 
			
		||||
		/* If module was removed move to the next one */
 | 
			
		||||
		if (!cmod)
 | 
			
		||||
			cmod = idr_get_next_ul(&cttype->mod_idr,
 | 
			
		||||
					       &iter->mod_id);
 | 
			
		||||
 | 
			
		||||
		/* Exit if no more modules */
 | 
			
		||||
		if (!cmod)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (cmod != iter->cmod) {
 | 
			
		||||
			iter->cmod = cmod;
 | 
			
		||||
			ct = get_first_module_ct(cmod);
 | 
			
		||||
		} else
 | 
			
		||||
			ct = get_next_module_ct(iter);
 | 
			
		||||
 | 
			
		||||
		if (ct)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		iter->mod_id++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	iter->ct = ct;
 | 
			
		||||
	return ct;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void codetag_to_text(struct seq_buf *out, struct codetag *ct)
 | 
			
		||||
{
 | 
			
		||||
	if (ct->modname)
 | 
			
		||||
		seq_buf_printf(out, "%s:%u [%s] func:%s",
 | 
			
		||||
			       ct->filename, ct->lineno,
 | 
			
		||||
			       ct->modname, ct->function);
 | 
			
		||||
	else
 | 
			
		||||
		seq_buf_printf(out, "%s:%u func:%s",
 | 
			
		||||
			       ct->filename, ct->lineno, ct->function);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline size_t range_size(const struct codetag_type *cttype,
 | 
			
		||||
				const struct codetag_range *range)
 | 
			
		||||
{
 | 
			
		||||
	return ((char *)range->stop - (char *)range->start) /
 | 
			
		||||
			cttype->desc.tag_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MODULES
 | 
			
		||||
static void *get_symbol(struct module *mod, const char *prefix, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	DECLARE_SEQ_BUF(sb, KSYM_NAME_LEN);
 | 
			
		||||
	const char *buf;
 | 
			
		||||
 | 
			
		||||
	seq_buf_printf(&sb, "%s%s", prefix, name);
 | 
			
		||||
	if (seq_buf_has_overflowed(&sb))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	buf = seq_buf_str(&sb);
 | 
			
		||||
	return mod ?
 | 
			
		||||
		(void *)find_kallsyms_symbol_value(mod, buf) :
 | 
			
		||||
		(void *)kallsyms_lookup_name(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct codetag_range get_section_range(struct module *mod,
 | 
			
		||||
					      const char *section)
 | 
			
		||||
{
 | 
			
		||||
	return (struct codetag_range) {
 | 
			
		||||
		get_symbol(mod, "__start_", section),
 | 
			
		||||
		get_symbol(mod, "__stop_", section),
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int codetag_module_init(struct codetag_type *cttype, struct module *mod)
 | 
			
		||||
{
 | 
			
		||||
	struct codetag_range range;
 | 
			
		||||
	struct codetag_module *cmod;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	range = get_section_range(mod, cttype->desc.section);
 | 
			
		||||
	if (!range.start || !range.stop) {
 | 
			
		||||
		pr_warn("Failed to load code tags of type %s from the module %s\n",
 | 
			
		||||
			cttype->desc.section,
 | 
			
		||||
			mod ? mod->name : "(built-in)");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Ignore empty ranges */
 | 
			
		||||
	if (range.start == range.stop)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(range.start > range.stop);
 | 
			
		||||
 | 
			
		||||
	cmod = kmalloc(sizeof(*cmod), GFP_KERNEL);
 | 
			
		||||
	if (unlikely(!cmod))
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	cmod->mod = mod;
 | 
			
		||||
	cmod->range = range;
 | 
			
		||||
 | 
			
		||||
	down_write(&cttype->mod_lock);
 | 
			
		||||
	err = idr_alloc(&cttype->mod_idr, cmod, 0, 0, GFP_KERNEL);
 | 
			
		||||
	if (err >= 0)
 | 
			
		||||
		cttype->count += range_size(cttype, &range);
 | 
			
		||||
	up_write(&cttype->mod_lock);
 | 
			
		||||
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		kfree(cmod);
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else /* CONFIG_MODULES */
 | 
			
		||||
static int codetag_module_init(struct codetag_type *cttype, struct module *mod) { return 0; }
 | 
			
		||||
#endif /* CONFIG_MODULES */
 | 
			
		||||
 | 
			
		||||
struct codetag_type *
 | 
			
		||||
codetag_register_type(const struct codetag_type_desc *desc)
 | 
			
		||||
{
 | 
			
		||||
	struct codetag_type *cttype;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(desc->tag_size <= 0);
 | 
			
		||||
 | 
			
		||||
	cttype = kzalloc(sizeof(*cttype), GFP_KERNEL);
 | 
			
		||||
	if (unlikely(!cttype))
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	cttype->desc = *desc;
 | 
			
		||||
	idr_init(&cttype->mod_idr);
 | 
			
		||||
	init_rwsem(&cttype->mod_lock);
 | 
			
		||||
 | 
			
		||||
	err = codetag_module_init(cttype, NULL);
 | 
			
		||||
	if (unlikely(err)) {
 | 
			
		||||
		kfree(cttype);
 | 
			
		||||
		return ERR_PTR(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&codetag_lock);
 | 
			
		||||
	list_add_tail(&cttype->link, &codetag_types);
 | 
			
		||||
	mutex_unlock(&codetag_lock);
 | 
			
		||||
 | 
			
		||||
	return cttype;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in a new issue