Commit d8792a57 authored by Charlie Jenkins's avatar Charlie Jenkins Committed by Palmer Dabbelt

riscv: Safely remove entries from relocation list

Use the safe versions of list and hlist iteration to safely remove
entries from the module relocation lists. To allow mutliple threads to
load modules concurrently, move relocation list pointers onto the stack
rather than using global variables.

Fixes: 8fd6c514 ("riscv: Add remaining module relocations")
Reported-by: default avatarRon Economos <re@w6rz.net>
Closes: https://lore.kernel.org/linux-riscv/444de86a-7e7c-4de7-5d1d-c1c40eefa4ba@w6rz.netSigned-off-by: default avatarCharlie Jenkins <charlie@rivosinc.com>
Tested-by: default avatarBjörn Töpel <bjorn@rivosinc.com>
Link: https://lore.kernel.org/r/20231127-module_linking_freeing-v4-1-a2ca1d7027d0@rivosinc.comSigned-off-by: default avatarPalmer Dabbelt <palmer@rivosinc.com>
parent b85ea95d
...@@ -40,15 +40,6 @@ struct relocation_handlers { ...@@ -40,15 +40,6 @@ struct relocation_handlers {
long buffer); long buffer);
}; };
unsigned int initialize_relocation_hashtable(unsigned int num_relocations);
void process_accumulated_relocations(struct module *me);
int add_relocation_to_accumulate(struct module *me, int type, void *location,
unsigned int hashtable_bits, Elf_Addr v);
struct hlist_head *relocation_hashtable;
struct list_head used_buckets_list;
/* /*
* The auipc+jalr instruction pair can reach any PC-relative offset * The auipc+jalr instruction pair can reach any PC-relative offset
* in the range [-2^31 - 2^11, 2^31 - 2^11) * in the range [-2^31 - 2^11, 2^31 - 2^11)
...@@ -604,7 +595,10 @@ static const struct relocation_handlers reloc_handlers[] = { ...@@ -604,7 +595,10 @@ static const struct relocation_handlers reloc_handlers[] = {
/* 192-255 nonstandard ABI extensions */ /* 192-255 nonstandard ABI extensions */
}; };
void process_accumulated_relocations(struct module *me) static void
process_accumulated_relocations(struct module *me,
struct hlist_head **relocation_hashtable,
struct list_head *used_buckets_list)
{ {
/* /*
* Only ADD/SUB/SET/ULEB128 should end up here. * Only ADD/SUB/SET/ULEB128 should end up here.
...@@ -624,18 +618,25 @@ void process_accumulated_relocations(struct module *me) ...@@ -624,18 +618,25 @@ void process_accumulated_relocations(struct module *me)
* - Each relocation entry for a location address * - Each relocation entry for a location address
*/ */
struct used_bucket *bucket_iter; struct used_bucket *bucket_iter;
struct used_bucket *bucket_iter_tmp;
struct relocation_head *rel_head_iter; struct relocation_head *rel_head_iter;
struct hlist_node *rel_head_iter_tmp;
struct relocation_entry *rel_entry_iter; struct relocation_entry *rel_entry_iter;
struct relocation_entry *rel_entry_iter_tmp;
int curr_type; int curr_type;
void *location; void *location;
long buffer; long buffer;
list_for_each_entry(bucket_iter, &used_buckets_list, head) { list_for_each_entry_safe(bucket_iter, bucket_iter_tmp,
hlist_for_each_entry(rel_head_iter, bucket_iter->bucket, node) { used_buckets_list, head) {
hlist_for_each_entry_safe(rel_head_iter, rel_head_iter_tmp,
bucket_iter->bucket, node) {
buffer = 0; buffer = 0;
location = rel_head_iter->location; location = rel_head_iter->location;
list_for_each_entry(rel_entry_iter, list_for_each_entry_safe(rel_entry_iter,
rel_head_iter->rel_entry, head) { rel_entry_iter_tmp,
rel_head_iter->rel_entry,
head) {
curr_type = rel_entry_iter->type; curr_type = rel_entry_iter->type;
reloc_handlers[curr_type].reloc_handler( reloc_handlers[curr_type].reloc_handler(
me, &buffer, rel_entry_iter->value); me, &buffer, rel_entry_iter->value);
...@@ -648,11 +649,14 @@ void process_accumulated_relocations(struct module *me) ...@@ -648,11 +649,14 @@ void process_accumulated_relocations(struct module *me)
kfree(bucket_iter); kfree(bucket_iter);
} }
kfree(relocation_hashtable); kfree(*relocation_hashtable);
} }
int add_relocation_to_accumulate(struct module *me, int type, void *location, static int add_relocation_to_accumulate(struct module *me, int type,
unsigned int hashtable_bits, Elf_Addr v) void *location,
unsigned int hashtable_bits, Elf_Addr v,
struct hlist_head *relocation_hashtable,
struct list_head *used_buckets_list)
{ {
struct relocation_entry *entry; struct relocation_entry *entry;
struct relocation_head *rel_head; struct relocation_head *rel_head;
...@@ -661,6 +665,10 @@ int add_relocation_to_accumulate(struct module *me, int type, void *location, ...@@ -661,6 +665,10 @@ int add_relocation_to_accumulate(struct module *me, int type, void *location,
unsigned long hash; unsigned long hash;
entry = kmalloc(sizeof(*entry), GFP_KERNEL); entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
INIT_LIST_HEAD(&entry->head); INIT_LIST_HEAD(&entry->head);
entry->type = type; entry->type = type;
entry->value = v; entry->value = v;
...@@ -669,7 +677,10 @@ int add_relocation_to_accumulate(struct module *me, int type, void *location, ...@@ -669,7 +677,10 @@ int add_relocation_to_accumulate(struct module *me, int type, void *location,
current_head = &relocation_hashtable[hash]; current_head = &relocation_hashtable[hash];
/* Find matching location (if any) */ /*
* Search for the relocation_head for the relocations that happen at the
* provided location
*/
bool found = false; bool found = false;
struct relocation_head *rel_head_iter; struct relocation_head *rel_head_iter;
...@@ -681,19 +692,45 @@ int add_relocation_to_accumulate(struct module *me, int type, void *location, ...@@ -681,19 +692,45 @@ int add_relocation_to_accumulate(struct module *me, int type, void *location,
} }
} }
/*
* If there has not yet been any relocations at the provided location,
* create a relocation_head for that location and populate it with this
* relocation_entry.
*/
if (!found) { if (!found) {
rel_head = kmalloc(sizeof(*rel_head), GFP_KERNEL); rel_head = kmalloc(sizeof(*rel_head), GFP_KERNEL);
if (!rel_head) {
kfree(entry);
return -ENOMEM;
}
rel_head->rel_entry = rel_head->rel_entry =
kmalloc(sizeof(struct list_head), GFP_KERNEL); kmalloc(sizeof(struct list_head), GFP_KERNEL);
if (!rel_head->rel_entry) {
kfree(entry);
kfree(rel_head);
return -ENOMEM;
}
INIT_LIST_HEAD(rel_head->rel_entry); INIT_LIST_HEAD(rel_head->rel_entry);
rel_head->location = location; rel_head->location = location;
INIT_HLIST_NODE(&rel_head->node); INIT_HLIST_NODE(&rel_head->node);
if (!current_head->first) { if (!current_head->first) {
bucket = bucket =
kmalloc(sizeof(struct used_bucket), GFP_KERNEL); kmalloc(sizeof(struct used_bucket), GFP_KERNEL);
if (!bucket) {
kfree(entry);
kfree(rel_head);
kfree(rel_head->rel_entry);
return -ENOMEM;
}
INIT_LIST_HEAD(&bucket->head); INIT_LIST_HEAD(&bucket->head);
bucket->bucket = current_head; bucket->bucket = current_head;
list_add(&bucket->head, &used_buckets_list); list_add(&bucket->head, used_buckets_list);
} }
hlist_add_head(&rel_head->node, current_head); hlist_add_head(&rel_head->node, current_head);
} }
...@@ -704,7 +741,9 @@ int add_relocation_to_accumulate(struct module *me, int type, void *location, ...@@ -704,7 +741,9 @@ int add_relocation_to_accumulate(struct module *me, int type, void *location,
return 0; return 0;
} }
unsigned int initialize_relocation_hashtable(unsigned int num_relocations) static unsigned int
initialize_relocation_hashtable(unsigned int num_relocations,
struct hlist_head **relocation_hashtable)
{ {
/* Can safely assume that bits is not greater than sizeof(long) */ /* Can safely assume that bits is not greater than sizeof(long) */
unsigned long hashtable_size = roundup_pow_of_two(num_relocations); unsigned long hashtable_size = roundup_pow_of_two(num_relocations);
...@@ -720,12 +759,13 @@ unsigned int initialize_relocation_hashtable(unsigned int num_relocations) ...@@ -720,12 +759,13 @@ unsigned int initialize_relocation_hashtable(unsigned int num_relocations)
hashtable_size <<= should_double_size; hashtable_size <<= should_double_size;
relocation_hashtable = kmalloc_array(hashtable_size, *relocation_hashtable = kmalloc_array(hashtable_size,
sizeof(*relocation_hashtable), sizeof(*relocation_hashtable),
GFP_KERNEL); GFP_KERNEL);
__hash_init(relocation_hashtable, hashtable_size); if (!*relocation_hashtable)
return -ENOMEM;
INIT_LIST_HEAD(&used_buckets_list); __hash_init(*relocation_hashtable, hashtable_size);
return hashtable_bits; return hashtable_bits;
} }
...@@ -742,7 +782,17 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, ...@@ -742,7 +782,17 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
Elf_Addr v; Elf_Addr v;
int res; int res;
unsigned int num_relocations = sechdrs[relsec].sh_size / sizeof(*rel); unsigned int num_relocations = sechdrs[relsec].sh_size / sizeof(*rel);
unsigned int hashtable_bits = initialize_relocation_hashtable(num_relocations); struct hlist_head *relocation_hashtable;
struct list_head used_buckets_list;
unsigned int hashtable_bits;
hashtable_bits = initialize_relocation_hashtable(num_relocations,
&relocation_hashtable);
if (hashtable_bits < 0)
return hashtable_bits;
INIT_LIST_HEAD(&used_buckets_list);
pr_debug("Applying relocate section %u to %u\n", relsec, pr_debug("Applying relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info); sechdrs[relsec].sh_info);
...@@ -823,14 +873,18 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, ...@@ -823,14 +873,18 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
} }
if (reloc_handlers[type].accumulate_handler) if (reloc_handlers[type].accumulate_handler)
res = add_relocation_to_accumulate(me, type, location, hashtable_bits, v); res = add_relocation_to_accumulate(me, type, location,
hashtable_bits, v,
relocation_hashtable,
&used_buckets_list);
else else
res = handler(me, location, v); res = handler(me, location, v);
if (res) if (res)
return res; return res;
} }
process_accumulated_relocations(me); process_accumulated_relocations(me, &relocation_hashtable,
&used_buckets_list);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment