mirror of
https://github.com/dynup/kpatch
synced 2025-01-19 19:40:43 +00:00
improve static local variable correlation
Improve the static local variable correlation logic, for the case where a static local is used by multiple functions. For each usage of the variable, look for a corresponding usage in the base object. If we find at least one matching usage, consider it a twin.
This commit is contained in:
parent
ea0ad69bbf
commit
fb49e254cf
@ -932,6 +932,58 @@ void kpatch_rename_mangled_functions(struct kpatch_elf *base,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *kpatch_section_function_name(struct section *sec)
|
||||||
|
{
|
||||||
|
if (is_rela_section(sec))
|
||||||
|
sec = sec->base;
|
||||||
|
return sec->sym ? sec->sym->name : sec->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a static local variable symbol and a section which references it in
|
||||||
|
* the patched object, find a corresponding usage of a similarly named symbol
|
||||||
|
* in the base object.
|
||||||
|
*/
|
||||||
|
static struct symbol *kpatch_find_static_twin(struct section *sec,
|
||||||
|
struct symbol *sym)
|
||||||
|
{
|
||||||
|
struct rela *rela;
|
||||||
|
struct symbol *basesym;
|
||||||
|
|
||||||
|
if (!sec->twin)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure there are no other orphaned static variables with the
|
||||||
|
* same name in the function. This is possible if the
|
||||||
|
* variables are in different scopes or if one of them is part of an
|
||||||
|
* inlined function.
|
||||||
|
*/
|
||||||
|
list_for_each_entry(rela, &sec->relas, list) {
|
||||||
|
if (rela->sym == sym || rela->sym->twin)
|
||||||
|
continue;
|
||||||
|
if (!kpatch_mangled_strcmp(rela->sym->name, sym->name))
|
||||||
|
ERROR("found another static local variable matching %s in patched %s",
|
||||||
|
sym->name, kpatch_section_function_name(sec));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the base object's corresponding variable */
|
||||||
|
basesym = NULL;
|
||||||
|
list_for_each_entry(rela, &sec->twin->relas, list) {
|
||||||
|
if (rela->sym->twin)
|
||||||
|
continue;
|
||||||
|
if (kpatch_mangled_strcmp(rela->sym->name, sym->name))
|
||||||
|
continue;
|
||||||
|
if (basesym && basesym != rela->sym)
|
||||||
|
ERROR("found two static local variables matching %s in orig %s",
|
||||||
|
sym->name, kpatch_section_function_name(sec));
|
||||||
|
|
||||||
|
basesym = rela->sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
return basesym;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* gcc renames static local variables by appending a period and a number. For
|
* gcc renames static local variables by appending a period and a number. For
|
||||||
* example, __foo could be renamed to __foo.31452. Unfortunately this number
|
* example, __foo could be renamed to __foo.31452. Unfortunately this number
|
||||||
@ -941,7 +993,7 @@ void kpatch_rename_mangled_functions(struct kpatch_elf *base,
|
|||||||
void kpatch_correlate_static_local_variables(struct kpatch_elf *base,
|
void kpatch_correlate_static_local_variables(struct kpatch_elf *base,
|
||||||
struct kpatch_elf *patched)
|
struct kpatch_elf *patched)
|
||||||
{
|
{
|
||||||
struct symbol *sym, *basesym;
|
struct symbol *sym, *basesym, *tmpsym;
|
||||||
struct section *tmpsec, *sec;
|
struct section *tmpsec, *sec;
|
||||||
struct rela *rela;
|
struct rela *rela;
|
||||||
int bundled, basebundled;
|
int bundled, basebundled;
|
||||||
@ -958,12 +1010,16 @@ void kpatch_correlate_static_local_variables(struct kpatch_elf *base,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the first function which uses the static variable.
|
* For each function which uses the variable in the patched
|
||||||
|
* object, look for a corresponding use in the function's twin
|
||||||
|
* in the base object.
|
||||||
|
*
|
||||||
* It's possible for multiple functions to use the same static
|
* It's possible for multiple functions to use the same static
|
||||||
* variable if the containing function is inlined, but we only
|
* local variable if the variable is defined in an inlined
|
||||||
* need to find one of them to do the correlation.
|
* function.
|
||||||
*/
|
*/
|
||||||
sec = NULL;
|
sec = NULL;
|
||||||
|
basesym = NULL;
|
||||||
list_for_each_entry(tmpsec, &patched->sections, list) {
|
list_for_each_entry(tmpsec, &patched->sections, list) {
|
||||||
if (!is_rela_section(tmpsec) ||
|
if (!is_rela_section(tmpsec) ||
|
||||||
!is_text_section(tmpsec->base) ||
|
!is_text_section(tmpsec->base) ||
|
||||||
@ -972,45 +1028,29 @@ void kpatch_correlate_static_local_variables(struct kpatch_elf *base,
|
|||||||
list_for_each_entry(rela, &tmpsec->relas, list) {
|
list_for_each_entry(rela, &tmpsec->relas, list) {
|
||||||
if (rela->sym != sym)
|
if (rela->sym != sym)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
tmpsym = kpatch_find_static_twin(tmpsec, sym);
|
||||||
|
if (basesym && tmpsym && basesym != tmpsym)
|
||||||
|
ERROR("found two twins for static local variable %s: %s and %s",
|
||||||
|
sym->name, basesym->name,
|
||||||
|
tmpsym->name);
|
||||||
|
if (tmpsym && !basesym)
|
||||||
|
basesym = tmpsym;
|
||||||
|
|
||||||
sec = tmpsec;
|
sec = tmpsec;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done:
|
|
||||||
if (!sec)
|
if (!sec)
|
||||||
ERROR("static local variable %s not used", sym->name);
|
ERROR("static local variable %s not used", sym->name);
|
||||||
|
|
||||||
if (!sec->twin)
|
if (!basesym) {
|
||||||
|
log_normal("WARNING: unable to correlate static local variable %s used by %s, assuming variable is new\n",
|
||||||
|
sym->name,
|
||||||
|
kpatch_section_function_name(sec));
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
|
||||||
* Ensure there are no other orphaned static variables with the
|
|
||||||
* same name in the function. This is possible if the
|
|
||||||
* variables are in different scopes (using C braces).
|
|
||||||
*/
|
|
||||||
list_for_each_entry(rela, &sec->relas, list) {
|
|
||||||
if (rela->sym == sym || rela->sym->twin)
|
|
||||||
continue;
|
|
||||||
if (!kpatch_mangled_strcmp(rela->sym->name, sym->name))
|
|
||||||
ERROR("found another static local variable matching %s in patched %s",
|
|
||||||
sym->name, sec->name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find the base object's corresponding variable */
|
|
||||||
basesym = NULL;
|
|
||||||
list_for_each_entry(rela, &sec->twin->relas, list) {
|
|
||||||
if (rela->sym->twin)
|
|
||||||
continue;
|
|
||||||
if (kpatch_mangled_strcmp(rela->sym->name, sym->name))
|
|
||||||
continue;
|
|
||||||
if (basesym && basesym != rela->sym)
|
|
||||||
ERROR("found two static local variables matching %s in orig %s",
|
|
||||||
sym->name, sec->name);
|
|
||||||
|
|
||||||
basesym = rela->sym;
|
|
||||||
}
|
|
||||||
if (!basesym)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bundled = sym == sym->sec->sym;
|
bundled = sym == sym->sec->sym;
|
||||||
basebundled = basesym == basesym->sec->sym;
|
basebundled = basesym == basesym->sec->sym;
|
||||||
|
46
test/integration/gcc-static-local-var-5.patch
Normal file
46
test/integration/gcc-static-local-var-5.patch
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
Index: src/kernel/audit.c
|
||||||
|
===================================================================
|
||||||
|
--- src.orig/kernel/audit.c
|
||||||
|
+++ src/kernel/audit.c
|
||||||
|
@@ -211,6 +211,12 @@ void audit_panic(const char *message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+void kpatch_audit_foo(void)
|
||||||
|
+{
|
||||||
|
+ if (!jiffies)
|
||||||
|
+ printk("kpatch audit foo\n");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static inline int audit_rate_check(void)
|
||||||
|
{
|
||||||
|
static unsigned long last_check = 0;
|
||||||
|
@@ -221,6 +227,7 @@ static inline int audit_rate_check(void)
|
||||||
|
unsigned long elapsed;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
+ kpatch_audit_foo();
|
||||||
|
if (!audit_rate_limit) return 1;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&lock, flags);
|
||||||
|
@@ -240,6 +247,11 @@ static inline int audit_rate_check(void)
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
+noinline void kpatch_audit_check(void)
|
||||||
|
+{
|
||||||
|
+ audit_rate_check();
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* audit_log_lost - conditionally log lost audit message event
|
||||||
|
* @message: the message stating reason for lost audit message
|
||||||
|
@@ -286,6 +298,8 @@ static int audit_log_config_change(char
|
||||||
|
struct audit_buffer *ab;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
+ kpatch_audit_check();
|
||||||
|
+
|
||||||
|
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
|
||||||
|
if (unlikely(!ab))
|
||||||
|
return rc;
|
Loading…
Reference in New Issue
Block a user