Merge pull request #380 from jpoimboe/shadow-variables

add support for shadow variables
This commit is contained in:
Seth Jennings 2014-09-08 14:17:37 -05:00
commit 3343eed007
7 changed files with 241 additions and 2 deletions

View File

@ -20,4 +20,4 @@ clean:
# kbuild rules
obj-m := kpatch.o
kpatch-y := core.o
kpatch-y := core.o shadow.o

View File

@ -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
View 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);

View File

@ -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;

View File

@ -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"

View File

@ -0,0 +1,3 @@
#!/bin/bash
grep -q newpid: /proc/$$/status

View 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.