1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2021, Oracle and/or its affiliates. */
3 
4 #include "vmlinux.h"
5 
6 #include <bpf/bpf_helpers.h>
7 #include <bpf/bpf_tracing.h>
8 #include <bpf/bpf_core_read.h>
9 
10 #define barrier_var(var) asm volatile("" : "=r"(var) : "0"(var))
11 
12 char _license[] SEC("license") = "GPL";
13 
14 unsigned int exception_triggered;
15 int test_pid;
16 
17 /* TRACE_EVENT(task_newtask,
18  *         TP_PROTO(struct task_struct *p, u64 clone_flags)
19  */
20 SEC("tp_btf/task_newtask")
21 int BPF_PROG(trace_task_newtask, struct task_struct *task, u64 clone_flags)
22 {
23 	int pid = bpf_get_current_pid_tgid() >> 32;
24 	struct callback_head *work;
25 	void *func;
26 
27 	if (test_pid != pid)
28 		return 0;
29 
30 	/* To verify we hit an exception we dereference task->task_works->func.
31 	 * If task work has been added,
32 	 * - task->task_works is non-NULL; and
33 	 * - task->task_works->func is non-NULL also (the callback function
34 	 *   must be specified for the task work.
35 	 *
36 	 * However, for a newly-created task, task->task_works is NULLed,
37 	 * so we know the exception handler triggered if task_works is
38 	 * NULL and func is NULL.
39 	 */
40 	work = task->task_works;
41 	func = work->func;
42 	/* Currently verifier will fail for `btf_ptr |= btf_ptr` * instruction.
43 	 * To workaround the issue, use barrier_var() and rewrite as below to
44 	 * prevent compiler from generating verifier-unfriendly code.
45 	 */
46 	barrier_var(work);
47 	if (work)
48 		return 0;
49 	barrier_var(func);
50 	if (func)
51 		return 0;
52 	exception_triggered++;
53 	return 0;
54 }
55