Skip to content

Commit 97c753e

Browse files
mhiramatrostedt
authored andcommitted
tracing/kprobe: Fix to support kretprobe events on unloaded modules
Fix kprobe_on_func_entry() returns error code instead of false so that register_kretprobe() can return an appropriate error code. append_trace_kprobe() expects the kprobe registration returns -ENOENT when the target symbol is not found, and it checks whether the target module is unloaded or not. If the target module doesn't exist, it defers to probe the target symbol until the module is loaded. However, since register_kretprobe() returns -EINVAL instead of -ENOENT in that case, it always fail on putting the kretprobe event on unloaded modules. e.g. Kprobe event: /sys/kernel/debug/tracing # echo p xfs:xfs_end_io >> kprobe_events [ 16.515574] trace_kprobe: This probe might be able to register after target module is loaded. Continue. Kretprobe event: (p -> r) /sys/kernel/debug/tracing # echo r xfs:xfs_end_io >> kprobe_events sh: write error: Invalid argument /sys/kernel/debug/tracing # cat error_log [ 41.122514] trace_kprobe: error: Failed to register probe event Command: r xfs:xfs_end_io ^ To fix this bug, change kprobe_on_func_entry() to detect symbol lookup failure and return -ENOENT in that case. Otherwise it returns -EINVAL or 0 (succeeded, given address is on the entry). Link: https://lkml.kernel.org/r/161176187132.1067016.8118042342894378981.stgit@devnote2 Cc: [email protected] Fixes: 59158ec ("tracing/kprobes: Check the probe on unloaded module correctly") Reported-by: Jianlin Lv <[email protected]> Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent da7f84c commit 97c753e

File tree

3 files changed

+32
-14
lines changed

3 files changed

+32
-14
lines changed

include/linux/kprobes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ extern void kprobes_inc_nmissed_count(struct kprobe *p);
266266
extern bool arch_within_kprobe_blacklist(unsigned long addr);
267267
extern int arch_populate_kprobe_blacklist(void);
268268
extern bool arch_kprobe_on_func_entry(unsigned long offset);
269-
extern bool kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset);
269+
extern int kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset);
270270

271271
extern bool within_kprobe_blacklist(unsigned long addr);
272272
extern int kprobe_add_ksym_blacklist(unsigned long entry);

kernel/kprobes.c

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,29 +1954,45 @@ bool __weak arch_kprobe_on_func_entry(unsigned long offset)
19541954
return !offset;
19551955
}
19561956

1957-
bool kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset)
1957+
/**
1958+
* kprobe_on_func_entry() -- check whether given address is function entry
1959+
* @addr: Target address
1960+
* @sym: Target symbol name
1961+
* @offset: The offset from the symbol or the address
1962+
*
1963+
* This checks whether the given @addr+@offset or @sym+@offset is on the
1964+
* function entry address or not.
1965+
* This returns 0 if it is the function entry, or -EINVAL if it is not.
1966+
* And also it returns -ENOENT if it fails the symbol or address lookup.
1967+
* Caller must pass @addr or @sym (either one must be NULL), or this
1968+
* returns -EINVAL.
1969+
*/
1970+
int kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset)
19581971
{
19591972
kprobe_opcode_t *kp_addr = _kprobe_addr(addr, sym, offset);
19601973

19611974
if (IS_ERR(kp_addr))
1962-
return false;
1975+
return PTR_ERR(kp_addr);
19631976

1964-
if (!kallsyms_lookup_size_offset((unsigned long)kp_addr, NULL, &offset) ||
1965-
!arch_kprobe_on_func_entry(offset))
1966-
return false;
1977+
if (!kallsyms_lookup_size_offset((unsigned long)kp_addr, NULL, &offset))
1978+
return -ENOENT;
19671979

1968-
return true;
1980+
if (!arch_kprobe_on_func_entry(offset))
1981+
return -EINVAL;
1982+
1983+
return 0;
19691984
}
19701985

19711986
int register_kretprobe(struct kretprobe *rp)
19721987
{
1973-
int ret = 0;
1988+
int ret;
19741989
struct kretprobe_instance *inst;
19751990
int i;
19761991
void *addr;
19771992

1978-
if (!kprobe_on_func_entry(rp->kp.addr, rp->kp.symbol_name, rp->kp.offset))
1979-
return -EINVAL;
1993+
ret = kprobe_on_func_entry(rp->kp.addr, rp->kp.symbol_name, rp->kp.offset);
1994+
if (ret)
1995+
return ret;
19801996

19811997
if (kretprobe_blacklist_size) {
19821998
addr = kprobe_addr(&rp->kp);

kernel/trace/trace_kprobe.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,9 @@ bool trace_kprobe_on_func_entry(struct trace_event_call *call)
221221
{
222222
struct trace_kprobe *tk = trace_kprobe_primary_from_call(call);
223223

224-
return tk ? kprobe_on_func_entry(tk->rp.kp.addr,
224+
return tk ? (kprobe_on_func_entry(tk->rp.kp.addr,
225225
tk->rp.kp.addr ? NULL : tk->rp.kp.symbol_name,
226-
tk->rp.kp.addr ? 0 : tk->rp.kp.offset) : false;
226+
tk->rp.kp.addr ? 0 : tk->rp.kp.offset) == 0) : false;
227227
}
228228

229229
bool trace_kprobe_error_injectable(struct trace_event_call *call)
@@ -828,9 +828,11 @@ static int trace_kprobe_create(int argc, const char *argv[])
828828
}
829829
if (is_return)
830830
flags |= TPARG_FL_RETURN;
831-
if (kprobe_on_func_entry(NULL, symbol, offset))
831+
ret = kprobe_on_func_entry(NULL, symbol, offset);
832+
if (ret == 0)
832833
flags |= TPARG_FL_FENTRY;
833-
if (offset && is_return && !(flags & TPARG_FL_FENTRY)) {
834+
/* Defer the ENOENT case until register kprobe */
835+
if (ret == -EINVAL && is_return) {
834836
trace_probe_log_err(0, BAD_RETPROBE);
835837
goto parse_error;
836838
}

0 commit comments

Comments
 (0)