forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
1 parent
53ce720
commit 916cc51
Showing
4 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |