Skip to content

Commit e2c0ae2

Browse files
committed
ftrace: bpf: Fix IPMODIFY + DIRECT in modify_ftrace_direct()
ftrace_hash_ipmodify_enable() checks IPMODIFY and DIRECT ftrace_ops on the same kernel function. When needed, ftrace_hash_ipmodify_enable() calls ops->ops_func() to prepare the direct ftrace (BPF trampoline) to share the same function as the IPMODIFY ftrace (livepatch). ftrace_hash_ipmodify_enable() is called in register_ftrace_direct() path, but not called in modify_ftrace_direct() path. As a result, the following operations will break livepatch: 1. Load livepatch to a kernel function; 2. Attach fentry program to the kernel function; 3. Attach fexit program to the kernel function. After 3, the kernel function being used will not be the livepatched version, but the original version. Fix this by adding ftrace_hash_ipmodify_enable() to modify_ftrace_direct() and adjust some logic around the call. Signed-off-by: Song Liu <[email protected]>
1 parent 3d6387a commit e2c0ae2

File tree

2 files changed

+38
-14
lines changed

2 files changed

+38
-14
lines changed

kernel/bpf/trampoline.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,13 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr)
221221

222222
if (tr->func.ftrace_managed) {
223223
ftrace_set_filter_ip(tr->fops, (unsigned long)ip, 0, 1);
224+
/*
225+
* Clearing fops->trampoline and fops->NULL is
226+
* needed by the "goto again" case in
227+
* bpf_trampoline_update().
228+
*/
229+
tr->fops->trampoline = 0;
230+
tr->fops->func = NULL;
224231
ret = register_ftrace_direct(tr->fops, (long)new_addr);
225232
} else {
226233
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, NULL, new_addr);
@@ -479,11 +486,6 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
479486
* BPF_TRAMP_F_SHARE_IPMODIFY is set, we can generate the
480487
* trampoline again, and retry register.
481488
*/
482-
/* reset fops->func and fops->trampoline for re-register */
483-
tr->fops->func = NULL;
484-
tr->fops->trampoline = 0;
485-
486-
/* free im memory and reallocate later */
487489
bpf_tramp_image_free(im);
488490
goto again;
489491
}

kernel/trace/ftrace.c

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,7 +1971,8 @@ static void ftrace_hash_rec_enable_modify(struct ftrace_ops *ops)
19711971
*/
19721972
static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,
19731973
struct ftrace_hash *old_hash,
1974-
struct ftrace_hash *new_hash)
1974+
struct ftrace_hash *new_hash,
1975+
bool update_target)
19751976
{
19761977
struct ftrace_page *pg;
19771978
struct dyn_ftrace *rec, *end = NULL;
@@ -2006,10 +2007,13 @@ static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,
20062007
if (rec->flags & FTRACE_FL_DISABLED)
20072008
continue;
20082009

2009-
/* We need to update only differences of filter_hash */
2010+
/*
2011+
* Unless we are updating the target of a direct function,
2012+
* we only need to update differences of filter_hash
2013+
*/
20102014
in_old = !!ftrace_lookup_ip(old_hash, rec->ip);
20112015
in_new = !!ftrace_lookup_ip(new_hash, rec->ip);
2012-
if (in_old == in_new)
2016+
if (!update_target && (in_old == in_new))
20132017
continue;
20142018

20152019
if (in_new) {
@@ -2020,7 +2024,16 @@ static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,
20202024
if (is_ipmodify)
20212025
goto rollback;
20222026

2023-
FTRACE_WARN_ON(rec->flags & FTRACE_FL_DIRECT);
2027+
/*
2028+
* If this is called by __modify_ftrace_direct()
2029+
* then it is only chaning where the direct
2030+
* pointer is jumping to, and the record already
2031+
* points to a direct trampoline. If it isn't
2032+
* then it is a bug to update ipmodify on a direct
2033+
* caller.
2034+
*/
2035+
FTRACE_WARN_ON(!update_target &&
2036+
(rec->flags & FTRACE_FL_DIRECT));
20242037

20252038
/*
20262039
* Another ops with IPMODIFY is already
@@ -2076,7 +2089,7 @@ static int ftrace_hash_ipmodify_enable(struct ftrace_ops *ops)
20762089
if (ftrace_hash_empty(hash))
20772090
hash = NULL;
20782091

2079-
return __ftrace_hash_update_ipmodify(ops, EMPTY_HASH, hash);
2092+
return __ftrace_hash_update_ipmodify(ops, EMPTY_HASH, hash, false);
20802093
}
20812094

20822095
/* Disabling always succeeds */
@@ -2087,7 +2100,7 @@ static void ftrace_hash_ipmodify_disable(struct ftrace_ops *ops)
20872100
if (ftrace_hash_empty(hash))
20882101
hash = NULL;
20892102

2090-
__ftrace_hash_update_ipmodify(ops, hash, EMPTY_HASH);
2103+
__ftrace_hash_update_ipmodify(ops, hash, EMPTY_HASH, false);
20912104
}
20922105

20932106
static int ftrace_hash_ipmodify_update(struct ftrace_ops *ops,
@@ -2101,7 +2114,7 @@ static int ftrace_hash_ipmodify_update(struct ftrace_ops *ops,
21012114
if (ftrace_hash_empty(new_hash))
21022115
new_hash = NULL;
21032116

2104-
return __ftrace_hash_update_ipmodify(ops, old_hash, new_hash);
2117+
return __ftrace_hash_update_ipmodify(ops, old_hash, new_hash, false);
21052118
}
21062119

21072120
static void print_ip_ins(const char *fmt, const unsigned char *p)
@@ -6108,7 +6121,7 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_direct);
61086121
static int
61096122
__modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
61106123
{
6111-
struct ftrace_hash *hash;
6124+
struct ftrace_hash *hash = ops->func_hash->filter_hash;
61126125
struct ftrace_func_entry *entry, *iter;
61136126
static struct ftrace_ops tmp_ops = {
61146127
.func = ftrace_stub,
@@ -6128,13 +6141,21 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
61286141
if (err)
61296142
return err;
61306143

6144+
/*
6145+
* Call __ftrace_hash_update_ipmodify() here, so that we can call
6146+
* ops->ops_func for the ops. This is needed because the above
6147+
* register_ftrace_function_nolock() worked on tmp_ops.
6148+
*/
6149+
err = __ftrace_hash_update_ipmodify(ops, hash, hash, true);
6150+
if (err)
6151+
goto out;
6152+
61316153
/*
61326154
* Now the ftrace_ops_list_func() is called to do the direct callers.
61336155
* We can safely change the direct functions attached to each entry.
61346156
*/
61356157
mutex_lock(&ftrace_lock);
61366158

6137-
hash = ops->func_hash->filter_hash;
61386159
size = 1 << hash->size_bits;
61396160
for (i = 0; i < size; i++) {
61406161
hlist_for_each_entry(iter, &hash->buckets[i], hlist) {
@@ -6149,6 +6170,7 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
61496170

61506171
mutex_unlock(&ftrace_lock);
61516172

6173+
out:
61526174
/* Removing the tmp_ops will add the updated direct callers to the functions */
61536175
unregister_ftrace_function(&tmp_ops);
61546176

0 commit comments

Comments
 (0)