2024-03-21 16:36:32 +00:00
|
|
|
// 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>
|
2024-03-21 16:36:34 +00:00
|
|
|
#include <linux/vmalloc.h>
|
2024-03-21 16:36:32 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-03-21 16:36:54 +00:00
|
|
|
bool codetag_trylock_module_list(struct codetag_type *cttype)
|
|
|
|
{
|
|
|
|
return down_read_trylock(&cttype->mod_lock) != 0;
|
|
|
|
}
|
|
|
|
|
2024-03-21 16:36:32 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *get_symbol(struct module *mod, const char *prefix, const char *name)
|
|
|
|
{
|
|
|
|
DECLARE_SEQ_BUF(sb, KSYM_NAME_LEN);
|
|
|
|
const char *buf;
|
2024-03-21 16:36:33 +00:00
|
|
|
void *ret;
|
2024-03-21 16:36:32 +00:00
|
|
|
|
|
|
|
seq_buf_printf(&sb, "%s%s", prefix, name);
|
|
|
|
if (seq_buf_has_overflowed(&sb))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
buf = seq_buf_str(&sb);
|
2024-03-21 16:36:33 +00:00
|
|
|
preempt_disable();
|
|
|
|
ret = mod ?
|
2024-03-21 16:36:32 +00:00
|
|
|
(void *)find_kallsyms_symbol_value(mod, buf) :
|
|
|
|
(void *)kallsyms_lookup_name(buf);
|
2024-03-21 16:36:33 +00:00
|
|
|
preempt_enable();
|
|
|
|
|
|
|
|
return ret;
|
2024-03-21 16:36:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct codetag_range get_section_range(struct module *mod,
|
|
|
|
const char *section)
|
|
|
|
{
|
|
|
|
return (struct codetag_range) {
|
2024-10-23 17:07:59 +00:00
|
|
|
get_symbol(mod, CODETAG_SECTION_START_PREFIX, section),
|
|
|
|
get_symbol(mod, CODETAG_SECTION_STOP_PREFIX, section),
|
2024-03-21 16:36:32 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-08-28 23:15:36 +00:00
|
|
|
static const char *get_mod_name(__maybe_unused struct module *mod)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_MODULES
|
|
|
|
if (mod)
|
|
|
|
return mod->name;
|
|
|
|
#endif
|
|
|
|
return "(built-in)";
|
|
|
|
}
|
|
|
|
|
2024-03-21 16:36:32 +00:00
|
|
|
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",
|
2024-08-28 23:15:36 +00:00
|
|
|
cttype->desc.section, get_mod_name(mod));
|
2024-03-21 16:36:32 +00:00
|
|
|
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);
|
2024-03-21 16:36:33 +00:00
|
|
|
if (err >= 0) {
|
2024-03-21 16:36:32 +00:00
|
|
|
cttype->count += range_size(cttype, &range);
|
2024-03-21 16:36:33 +00:00
|
|
|
if (cttype->desc.module_load)
|
|
|
|
cttype->desc.module_load(cttype, cmod);
|
|
|
|
}
|
2024-03-21 16:36:32 +00:00
|
|
|
up_write(&cttype->mod_lock);
|
|
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
kfree(cmod);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-08-28 23:15:36 +00:00
|
|
|
#ifdef CONFIG_MODULES
|
2024-10-23 17:07:56 +00:00
|
|
|
#define CODETAG_SECTION_PREFIX ".codetag."
|
|
|
|
|
|
|
|
/* Some codetag types need a separate module section */
|
|
|
|
bool codetag_needs_module_section(struct module *mod, const char *name,
|
|
|
|
unsigned long size)
|
|
|
|
{
|
|
|
|
const char *type_name;
|
|
|
|
struct codetag_type *cttype;
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
if (strncmp(name, CODETAG_SECTION_PREFIX, strlen(CODETAG_SECTION_PREFIX)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
type_name = name + strlen(CODETAG_SECTION_PREFIX);
|
|
|
|
mutex_lock(&codetag_lock);
|
|
|
|
list_for_each_entry(cttype, &codetag_types, link) {
|
|
|
|
if (strcmp(type_name, cttype->desc.section) == 0) {
|
|
|
|
if (!cttype->desc.needs_section_mem)
|
|
|
|
break;
|
|
|
|
|
|
|
|
down_write(&cttype->mod_lock);
|
|
|
|
ret = cttype->desc.needs_section_mem(mod, size);
|
|
|
|
up_write(&cttype->mod_lock);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mutex_unlock(&codetag_lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *codetag_alloc_module_section(struct module *mod, const char *name,
|
|
|
|
unsigned long size, unsigned int prepend,
|
|
|
|
unsigned long align)
|
|
|
|
{
|
|
|
|
const char *type_name = name + strlen(CODETAG_SECTION_PREFIX);
|
|
|
|
struct codetag_type *cttype;
|
|
|
|
void *ret = ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
mutex_lock(&codetag_lock);
|
|
|
|
list_for_each_entry(cttype, &codetag_types, link) {
|
|
|
|
if (strcmp(type_name, cttype->desc.section) == 0) {
|
|
|
|
if (WARN_ON(!cttype->desc.alloc_section_mem))
|
|
|
|
break;
|
|
|
|
|
|
|
|
down_write(&cttype->mod_lock);
|
|
|
|
ret = cttype->desc.alloc_section_mem(mod, size, prepend, align);
|
|
|
|
up_write(&cttype->mod_lock);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mutex_unlock(&codetag_lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void codetag_free_module_sections(struct module *mod)
|
|
|
|
{
|
|
|
|
struct codetag_type *cttype;
|
|
|
|
|
|
|
|
mutex_lock(&codetag_lock);
|
|
|
|
list_for_each_entry(cttype, &codetag_types, link) {
|
|
|
|
if (!cttype->desc.free_section_mem)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
down_write(&cttype->mod_lock);
|
|
|
|
cttype->desc.free_section_mem(mod, false);
|
|
|
|
up_write(&cttype->mod_lock);
|
|
|
|
}
|
|
|
|
mutex_unlock(&codetag_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void codetag_module_replaced(struct module *mod, struct module *new_mod)
|
|
|
|
{
|
|
|
|
struct codetag_type *cttype;
|
|
|
|
|
|
|
|
mutex_lock(&codetag_lock);
|
|
|
|
list_for_each_entry(cttype, &codetag_types, link) {
|
|
|
|
if (!cttype->desc.module_replaced)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
down_write(&cttype->mod_lock);
|
|
|
|
cttype->desc.module_replaced(mod, new_mod);
|
|
|
|
up_write(&cttype->mod_lock);
|
|
|
|
}
|
|
|
|
mutex_unlock(&codetag_lock);
|
|
|
|
}
|
|
|
|
|
2024-03-21 16:36:33 +00:00
|
|
|
void codetag_load_module(struct module *mod)
|
|
|
|
{
|
|
|
|
struct codetag_type *cttype;
|
|
|
|
|
|
|
|
if (!mod)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mutex_lock(&codetag_lock);
|
|
|
|
list_for_each_entry(cttype, &codetag_types, link)
|
|
|
|
codetag_module_init(cttype, mod);
|
|
|
|
mutex_unlock(&codetag_lock);
|
|
|
|
}
|
|
|
|
|
2024-10-23 17:07:56 +00:00
|
|
|
void codetag_unload_module(struct module *mod)
|
2024-03-21 16:36:33 +00:00
|
|
|
{
|
|
|
|
struct codetag_type *cttype;
|
|
|
|
|
|
|
|
if (!mod)
|
2024-10-23 17:07:56 +00:00
|
|
|
return;
|
2024-03-21 16:36:33 +00:00
|
|
|
|
2024-10-07 20:52:24 +00:00
|
|
|
/* await any module's kfree_rcu() operations to complete */
|
|
|
|
kvfree_rcu_barrier();
|
|
|
|
|
2024-03-21 16:36:33 +00:00
|
|
|
mutex_lock(&codetag_lock);
|
|
|
|
list_for_each_entry(cttype, &codetag_types, link) {
|
|
|
|
struct codetag_module *found = NULL;
|
|
|
|
struct codetag_module *cmod;
|
|
|
|
unsigned long mod_id, tmp;
|
|
|
|
|
|
|
|
down_write(&cttype->mod_lock);
|
|
|
|
idr_for_each_entry_ul(&cttype->mod_idr, cmod, tmp, mod_id) {
|
|
|
|
if (cmod->mod && cmod->mod == mod) {
|
|
|
|
found = cmod;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found) {
|
|
|
|
if (cttype->desc.module_unload)
|
2024-10-23 17:07:56 +00:00
|
|
|
cttype->desc.module_unload(cttype, cmod);
|
2024-03-21 16:36:33 +00:00
|
|
|
|
|
|
|
cttype->count -= range_size(cttype, &cmod->range);
|
|
|
|
idr_remove(&cttype->mod_idr, mod_id);
|
|
|
|
kfree(cmod);
|
|
|
|
}
|
|
|
|
up_write(&cttype->mod_lock);
|
2024-10-23 17:07:56 +00:00
|
|
|
if (found && cttype->desc.free_section_mem)
|
|
|
|
cttype->desc.free_section_mem(mod, true);
|
2024-03-21 16:36:33 +00:00
|
|
|
}
|
|
|
|
mutex_unlock(&codetag_lock);
|
|
|
|
}
|
2024-03-21 16:36:32 +00:00
|
|
|
#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;
|
|
|
|
}
|