mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
![Josh Poimboeuf](/assets/img/avatar_default.png)
Currently we do not allow patch module to unload since there is no
method to determine if a task is still running in the patched code.
The consistency model gives us the way because when the unpatching
finishes we know that all tasks were marked as safe to call an original
function. Thus every new call to the function calls the original code
and at the same time no task can be somewhere in the patched code,
because it had to leave that code to be marked as safe.
We can safely let the patch module go after that.
Completion is used for synchronization between module removal and sysfs
infrastructure in a similar way to commit 942e443127
("module: Fix
mod->mkobj.kobj potentially freed too early").
Note that we still do not allow the removal for immediate model, that is
no consistency model. The module refcount may increase in this case if
somebody disables and enables the patch several times. This should not
cause any harm.
With this change a call to try_module_get() is moved to
__klp_enable_patch from klp_register_patch to make module reference
counting symmetric (module_put() is in a patch disable path) and to
allow to take a new reference to a disabled module when being enabled.
Finally, we need to be very careful about possible races between
klp_unregister_patch(), kobject_put() functions and operations
on the related sysfs files.
kobject_put(&patch->kobj) must be called without klp_mutex. Otherwise,
it might be blocked by enabled_store() that needs the mutex as well.
In addition, enabled_store() must check if the patch was not
unregisted in the meantime.
There is no need to do the same for other kobject_put() callsites
at the moment. Their sysfs operations neither take the lock nor
they access any data that might be freed in the meantime.
There was an attempt to use kobjects the right way and prevent these
races by design. But it made the patch definition more complicated
and opened another can of worms. See
https://lkml.kernel.org/r/1464018848-4303-1-git-send-email-pmladek@suse.com
[Thanks to Petr Mladek for improving the commit message.]
Signed-off-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Acked-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
177 lines
5.6 KiB
C
177 lines
5.6 KiB
C
/*
|
|
* livepatch.h - Kernel Live Patching Core
|
|
*
|
|
* Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
|
|
* Copyright (C) 2014 SUSE
|
|
*
|
|
* 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/>.
|
|
*/
|
|
|
|
#ifndef _LINUX_LIVEPATCH_H_
|
|
#define _LINUX_LIVEPATCH_H_
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/ftrace.h>
|
|
#include <linux/completion.h>
|
|
|
|
#if IS_ENABLED(CONFIG_LIVEPATCH)
|
|
|
|
#include <asm/livepatch.h>
|
|
|
|
/* task patch states */
|
|
#define KLP_UNDEFINED -1
|
|
#define KLP_UNPATCHED 0
|
|
#define KLP_PATCHED 1
|
|
|
|
/**
|
|
* struct klp_func - function structure for live patching
|
|
* @old_name: name of the function to be patched
|
|
* @new_func: pointer to the patched function code
|
|
* @old_sympos: a hint indicating which symbol position the old function
|
|
* can be found (optional)
|
|
* @immediate: patch the func immediately, bypassing safety mechanisms
|
|
* @old_addr: the address of the function being patched
|
|
* @kobj: kobject for sysfs resources
|
|
* @stack_node: list node for klp_ops func_stack list
|
|
* @old_size: size of the old function
|
|
* @new_size: size of the new function
|
|
* @patched: the func has been added to the klp_ops list
|
|
* @transition: the func is currently being applied or reverted
|
|
*
|
|
* The patched and transition variables define the func's patching state. When
|
|
* patching, a func is always in one of the following states:
|
|
*
|
|
* patched=0 transition=0: unpatched
|
|
* patched=0 transition=1: unpatched, temporary starting state
|
|
* patched=1 transition=1: patched, may be visible to some tasks
|
|
* patched=1 transition=0: patched, visible to all tasks
|
|
*
|
|
* And when unpatching, it goes in the reverse order:
|
|
*
|
|
* patched=1 transition=0: patched, visible to all tasks
|
|
* patched=1 transition=1: patched, may be visible to some tasks
|
|
* patched=0 transition=1: unpatched, temporary ending state
|
|
* patched=0 transition=0: unpatched
|
|
*/
|
|
struct klp_func {
|
|
/* external */
|
|
const char *old_name;
|
|
void *new_func;
|
|
/*
|
|
* The old_sympos field is optional and can be used to resolve
|
|
* duplicate symbol names in livepatch objects. If this field is zero,
|
|
* it is expected the symbol is unique, otherwise patching fails. If
|
|
* this value is greater than zero then that occurrence of the symbol
|
|
* in kallsyms for the given object is used.
|
|
*/
|
|
unsigned long old_sympos;
|
|
bool immediate;
|
|
|
|
/* internal */
|
|
unsigned long old_addr;
|
|
struct kobject kobj;
|
|
struct list_head stack_node;
|
|
unsigned long old_size, new_size;
|
|
bool patched;
|
|
bool transition;
|
|
};
|
|
|
|
/**
|
|
* struct klp_object - kernel object structure for live patching
|
|
* @name: module name (or NULL for vmlinux)
|
|
* @funcs: function entries for functions to be patched in the object
|
|
* @kobj: kobject for sysfs resources
|
|
* @mod: kernel module associated with the patched object
|
|
* (NULL for vmlinux)
|
|
* @patched: the object's funcs have been added to the klp_ops list
|
|
*/
|
|
struct klp_object {
|
|
/* external */
|
|
const char *name;
|
|
struct klp_func *funcs;
|
|
|
|
/* internal */
|
|
struct kobject kobj;
|
|
struct module *mod;
|
|
bool patched;
|
|
};
|
|
|
|
/**
|
|
* struct klp_patch - patch structure for live patching
|
|
* @mod: reference to the live patch module
|
|
* @objs: object entries for kernel objects to be patched
|
|
* @immediate: patch all funcs immediately, bypassing safety mechanisms
|
|
* @list: list node for global list of registered patches
|
|
* @kobj: kobject for sysfs resources
|
|
* @enabled: the patch is enabled (but operation may be incomplete)
|
|
* @finish: for waiting till it is safe to remove the patch module
|
|
*/
|
|
struct klp_patch {
|
|
/* external */
|
|
struct module *mod;
|
|
struct klp_object *objs;
|
|
bool immediate;
|
|
|
|
/* internal */
|
|
struct list_head list;
|
|
struct kobject kobj;
|
|
bool enabled;
|
|
struct completion finish;
|
|
};
|
|
|
|
#define klp_for_each_object(patch, obj) \
|
|
for (obj = patch->objs; obj->funcs || obj->name; obj++)
|
|
|
|
#define klp_for_each_func(obj, func) \
|
|
for (func = obj->funcs; \
|
|
func->old_name || func->new_func || func->old_sympos; \
|
|
func++)
|
|
|
|
int klp_register_patch(struct klp_patch *);
|
|
int klp_unregister_patch(struct klp_patch *);
|
|
int klp_enable_patch(struct klp_patch *);
|
|
int klp_disable_patch(struct klp_patch *);
|
|
|
|
void arch_klp_init_object_loaded(struct klp_patch *patch,
|
|
struct klp_object *obj);
|
|
|
|
/* Called from the module loader during module coming/going states */
|
|
int klp_module_coming(struct module *mod);
|
|
void klp_module_going(struct module *mod);
|
|
|
|
void klp_copy_process(struct task_struct *child);
|
|
void klp_update_patch_state(struct task_struct *task);
|
|
|
|
static inline bool klp_patch_pending(struct task_struct *task)
|
|
{
|
|
return test_tsk_thread_flag(task, TIF_PATCH_PENDING);
|
|
}
|
|
|
|
static inline bool klp_have_reliable_stack(void)
|
|
{
|
|
return IS_ENABLED(CONFIG_STACKTRACE) &&
|
|
IS_ENABLED(CONFIG_HAVE_RELIABLE_STACKTRACE);
|
|
}
|
|
|
|
#else /* !CONFIG_LIVEPATCH */
|
|
|
|
static inline int klp_module_coming(struct module *mod) { return 0; }
|
|
static inline void klp_module_going(struct module *mod) {}
|
|
static inline bool klp_patch_pending(struct task_struct *task) { return false; }
|
|
static inline void klp_update_patch_state(struct task_struct *task) {}
|
|
static inline void klp_copy_process(struct task_struct *child) {}
|
|
|
|
#endif /* CONFIG_LIVEPATCH */
|
|
|
|
#endif /* _LINUX_LIVEPATCH_H_ */
|