mirror of
https://github.com/dynup/kpatch
synced 2025-01-25 22:32:43 +00:00
Merge pull request #380 from jpoimboe/shadow-variables
add support for shadow variables
This commit is contained in:
commit
3343eed007
@ -20,4 +20,4 @@ clean:
|
||||
|
||||
# kbuild rules
|
||||
obj-m := kpatch.o
|
||||
kpatch-y := core.o
|
||||
kpatch-y := core.o shadow.o
|
||||
|
@ -91,4 +91,8 @@ extern struct kobject *kpatch_patches_kobj;
|
||||
extern int kpatch_register(struct kpatch_module *kpmod, bool replace);
|
||||
extern int kpatch_unregister(struct kpatch_module *kpmod);
|
||||
|
||||
extern void *kpatch_shadow_alloc(void *obj, char *var, size_t size, gfp_t gfp);
|
||||
extern void kpatch_shadow_free(void *obj, char *var);
|
||||
extern void *kpatch_shadow_get(void *obj, char *var);
|
||||
|
||||
#endif /* _KPATCH_H_ */
|
||||
|
139
kmod/core/shadow.c
Normal file
139
kmod/core/shadow.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
* Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* kpatch shadow variables
|
||||
*
|
||||
* These functions can be used to add new "shadow" fields to existing data
|
||||
* structures. For example, to allocate a "newpid" variable associated with an
|
||||
* instance of task_struct, and assign it a value of 1000:
|
||||
*
|
||||
* struct task_struct *tsk = current;
|
||||
* int *newpid;
|
||||
* newpid = kpatch_shadow_alloc(tsk, "newpid", sizeof(int), GFP_KERNEL);
|
||||
* if (newpid)
|
||||
* *newpid = 1000;
|
||||
*
|
||||
* To retrieve a pointer to the variable:
|
||||
*
|
||||
* struct task_struct *tsk = current;
|
||||
* int *newpid;
|
||||
* newpid = kpatch_shadow_get(tsk, "newpid");
|
||||
* if (newpid)
|
||||
* printk("task newpid = %d\n", *newpid); // prints "task newpid = 1000"
|
||||
*
|
||||
* To free it:
|
||||
*
|
||||
* kpatch_shadow_free(tsk, "newpid");
|
||||
*/
|
||||
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/slab.h>
|
||||
#include "kpatch.h"
|
||||
|
||||
static DEFINE_HASHTABLE(kpatch_shadow_hash, 12);
|
||||
static DEFINE_SPINLOCK(kpatch_shadow_lock);
|
||||
|
||||
struct kpatch_shadow {
|
||||
struct hlist_node node;
|
||||
struct rcu_head rcu_head;
|
||||
void *obj;
|
||||
char *var;
|
||||
void *data;
|
||||
};
|
||||
|
||||
void *kpatch_shadow_alloc(void *obj, char *var, size_t size, gfp_t gfp)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct kpatch_shadow *shadow;
|
||||
|
||||
shadow = kmalloc(sizeof(*shadow), gfp);
|
||||
if (!shadow)
|
||||
return NULL;
|
||||
|
||||
shadow->obj = obj;
|
||||
|
||||
shadow->var = kstrdup(var, gfp);
|
||||
if (!shadow->var)
|
||||
return NULL;
|
||||
|
||||
shadow->data = kmalloc(size, gfp);
|
||||
if (!shadow->data) {
|
||||
kfree(shadow->var);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&kpatch_shadow_lock, flags);
|
||||
hash_add_rcu(kpatch_shadow_hash, &shadow->node, (unsigned long)obj);
|
||||
spin_unlock_irqrestore(&kpatch_shadow_lock, flags);
|
||||
|
||||
return shadow->data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kpatch_shadow_alloc);
|
||||
|
||||
static void kpatch_shadow_rcu_free(struct rcu_head *head)
|
||||
{
|
||||
struct kpatch_shadow *shadow;
|
||||
|
||||
shadow = container_of(head, struct kpatch_shadow, rcu_head);
|
||||
|
||||
kfree(shadow->data);
|
||||
kfree(shadow->var);
|
||||
kfree(shadow);
|
||||
}
|
||||
|
||||
void kpatch_shadow_free(void *obj, char *var)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct kpatch_shadow *shadow;
|
||||
|
||||
spin_lock_irqsave(&kpatch_shadow_lock, flags);
|
||||
|
||||
hash_for_each_possible(kpatch_shadow_hash, shadow, node,
|
||||
(unsigned long)obj) {
|
||||
if (shadow->obj == obj && !strcmp(shadow->var, var)) {
|
||||
hash_del_rcu(&shadow->node);
|
||||
spin_unlock_irqrestore(&kpatch_shadow_lock, flags);
|
||||
call_rcu(&shadow->rcu_head, kpatch_shadow_rcu_free);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&kpatch_shadow_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kpatch_shadow_free);
|
||||
|
||||
void *kpatch_shadow_get(void *obj, char *var)
|
||||
{
|
||||
struct kpatch_shadow *shadow;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
hash_for_each_possible_rcu(kpatch_shadow_hash, shadow, node,
|
||||
(unsigned long)obj) {
|
||||
if (shadow->obj == obj && !strcmp(shadow->var, var)) {
|
||||
rcu_read_unlock();
|
||||
return shadow->data;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kpatch_shadow_get);
|
@ -2094,6 +2094,13 @@ void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
||||
|
||||
}
|
||||
|
||||
static int kpatch_is_core_module_symbol(char *name)
|
||||
{
|
||||
return (!strcmp(name, "kpatch_shadow_alloc") ||
|
||||
!strcmp(name, "kpatch_shadow_free") ||
|
||||
!strcmp(name, "kpatch_shadow_get"));
|
||||
}
|
||||
|
||||
void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
||||
struct lookup_table *table, char *hint,
|
||||
char *objname)
|
||||
@ -2143,6 +2150,14 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
||||
list_for_each_entry_safe(rela, safe, &sec->relas, list) {
|
||||
if (rela->sym->sec)
|
||||
continue;
|
||||
/*
|
||||
* Allow references to core module symbols to remain as
|
||||
* normal relas, since the core module may not be
|
||||
* compiled into the kernel, and they should be
|
||||
* exported anyway.
|
||||
*/
|
||||
if (kpatch_is_core_module_symbol(rela->sym->name))
|
||||
continue;
|
||||
|
||||
exported = 0;
|
||||
|
||||
|
@ -401,8 +401,14 @@ echo "Building patched kernel"
|
||||
patch -N -p1 < "$APPLIEDPATCHFILE" >> "$LOGFILE" 2>&1 || die
|
||||
mkdir -p "$TEMPDIR/orig" "$TEMPDIR/patched"
|
||||
export TEMPDIR
|
||||
CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " make "-j$CPUS" $TARGETS "O=$OBJDIR" >> "$LOGFILE" 2>&1 || die
|
||||
# TODO: remove custom LDFLAGS and ugly "undefined reference" grep when core
|
||||
# module gets moved to the kernel tree
|
||||
CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " \
|
||||
LDFLAGS_vmlinux="--warn-unresolved-symbols" \
|
||||
LDFLAGS_MODULE="--warn-unresolved-symbols" \
|
||||
make "-j$CPUS" $TARGETS "O=$OBJDIR" >> "$LOGFILE" 2>&1 || die
|
||||
[[ "${PIPESTATUS[0]}" -eq 0 ]] || die
|
||||
grep -q "undefined reference" "$LOGFILE" | grep -qv kpatch_shadow && die
|
||||
|
||||
if [[ ! -e "$TEMPDIR/changed_objs" ]]; then
|
||||
die "no changed objects found"
|
||||
|
3
test/integration/shadow-newpid-LOADED.test
Executable file
3
test/integration/shadow-newpid-LOADED.test
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
grep -q newpid: /proc/$$/status
|
72
test/integration/shadow-newpid.patch
Normal file
72
test/integration/shadow-newpid.patch
Normal file
@ -0,0 +1,72 @@
|
||||
Index: src/kernel/fork.c
|
||||
===================================================================
|
||||
--- src.orig/kernel/fork.c
|
||||
+++ src/kernel/fork.c
|
||||
@@ -1572,6 +1572,7 @@ struct task_struct *fork_idle(int cpu)
|
||||
* It copies the process, and if successful kick-starts
|
||||
* it and waits for it to finish using the VM if required.
|
||||
*/
|
||||
+#include "kpatch.h"
|
||||
long do_fork(unsigned long clone_flags,
|
||||
unsigned long stack_start,
|
||||
unsigned long stack_size,
|
||||
@@ -1622,6 +1623,13 @@ long do_fork(unsigned long clone_flags,
|
||||
if (!IS_ERR(p)) {
|
||||
struct completion vfork;
|
||||
struct pid *pid;
|
||||
+ int *newpid;
|
||||
+ static int ctr = 0;
|
||||
+
|
||||
+ newpid = kpatch_shadow_alloc(p, "newpid", sizeof(*newpid),
|
||||
+ GFP_KERNEL);
|
||||
+ if (newpid)
|
||||
+ *newpid = ctr++;
|
||||
|
||||
trace_sched_process_fork(current, p);
|
||||
|
||||
Index: src/fs/proc/array.c
|
||||
===================================================================
|
||||
--- src.orig/fs/proc/array.c
|
||||
+++ src/fs/proc/array.c
|
||||
@@ -337,13 +337,20 @@ static inline void task_seccomp(struct s
|
||||
#endif
|
||||
}
|
||||
|
||||
+#include "kpatch.h"
|
||||
static inline void task_context_switch_counts(struct seq_file *m,
|
||||
struct task_struct *p)
|
||||
{
|
||||
+ int *newpid;
|
||||
+
|
||||
seq_printf(m, "voluntary_ctxt_switches:\t%lu\n"
|
||||
"nonvoluntary_ctxt_switches:\t%lu\n",
|
||||
p->nvcsw,
|
||||
p->nivcsw);
|
||||
+
|
||||
+ newpid = kpatch_shadow_get(p, "newpid");
|
||||
+ if (newpid)
|
||||
+ seq_printf(m, "newpid:\t%d\n", *newpid);
|
||||
}
|
||||
|
||||
static void task_cpus_allowed(struct seq_file *m, struct task_struct *task)
|
||||
Index: src/kernel/exit.c
|
||||
===================================================================
|
||||
--- src.orig/kernel/exit.c
|
||||
+++ src/kernel/exit.c
|
||||
@@ -694,6 +694,7 @@ static void check_stack_usage(void)
|
||||
static inline void check_stack_usage(void) {}
|
||||
#endif
|
||||
|
||||
+#include "kpatch.h"
|
||||
void do_exit(long code)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
@@ -790,6 +791,8 @@ void do_exit(long code)
|
||||
exit_task_work(tsk);
|
||||
exit_thread();
|
||||
|
||||
+ kpatch_shadow_free(tsk, "newpid");
|
||||
+
|
||||
/*
|
||||
* Flush inherited counters to the parent - before the parent
|
||||
* gets woken up by child-exit notifications.
|
Loading…
Reference in New Issue
Block a user