Skip to content

Commit

Permalink
lib: code tagging framework
Browse files Browse the repository at this point in the history
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/[email protected]
Co-developed-by: Kent Overstreet <[email protected]>
Signed-off-by: Kent Overstreet <[email protected]>
Signed-off-by: Suren Baghdasaryan <[email protected]>
Tested-by: Kees Cook <[email protected]>
Cc: Alexander Viro <[email protected]>
Cc: Alex Gaynor <[email protected]>
Cc: Alice Ryhl <[email protected]>
Cc: Andreas Hindborg <[email protected]>
Cc: Benno Lossin <[email protected]>
Cc: "Björn Roy Baron" <[email protected]>
Cc: Boqun Feng <[email protected]>
Cc: Christoph Lameter <[email protected]>
Cc: Dennis Zhou <[email protected]>
Cc: Gary Guo <[email protected]>
Cc: Miguel Ojeda <[email protected]>
Cc: Pasha Tatashin <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Tejun Heo <[email protected]>
Cc: Vlastimil Babka <[email protected]>
Cc: Wedson Almeida Filho <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
  • Loading branch information
surenbaghdasaryan authored and akpm00 committed Apr 26, 2024
1 parent 53ce720 commit 916cc51
Show file tree
Hide file tree
Showing 4 changed files with 292 additions and 0 deletions.
68 changes: 68 additions & 0 deletions include/linux/codetag.h
Original file line number Diff line number Diff line change
@@ -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 */
4 changes: 4 additions & 0 deletions lib/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
219 changes: 219 additions & 0 deletions lib/codetag.c
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 916cc51

Please sign in to comment.