132 | * Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization[[Packt]](https://www.packtpub.com/free-ebook/linux-kernel-programming-part-2-char-device-drivers-and-kernel-synchronization/9781801079518) [[Amazon]](https://www.amazon.in/Linux-Kernel-Programming-Part-Synchronization-ebook/dp/B08ZSV58G8)
133 |
134 | * Mastering Linux Device Driver Development [[Packt]](https://www.packtpub.com/product/mastering-linux-device-driver-development/9781789342048) [[Amazon]](https://www.amazon.in/Mastering-Linux-Device-Driver-Development-ebook/dp/B08M6G6Q4N)
135 |
136 | ## Get to Know the Author
137 | **Kaiwan N Billimoria**
138 | He taught himself programming on his dad's PC in 1983. By the early 90s, he had discovered the joys of programming on Unix, and by 1997, on Linux!
139 | Kaiwan has worked on many aspects of the Linux system programming stack, including Bash, system programming in C, kernel internals, device drivers, and embedded Linux. He has actively worked on commercial/FOSS projects. His contributions include drivers for the mainline Linux OS and many smaller projects hosted on GitHub. His Linux passion feeds well into his passion for teaching these topics to engineers, which he has done for close to three decades now. He's the author of Hands-On System Programming with Linux and Linux Kernel Programming. He is a recreational ultrarunner too.
140 |
141 | ### Download a free PDF
142 |
143 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
144 | https://packt.link/free-ebook/9781801075039
145 |
--------------------------------------------------------------------------------
/ch10/cause_oops_panic.sh:
--------------------------------------------------------------------------------
1 | sudo sh -c "echo 1 > /proc/sys/kernel/panic_on_oops"
2 | sudo sh -c "echo 1 > /proc/sys/kernel/sysrq"
3 | sync; sleep .5
4 | sudo sh -c "echo c > /proc/sysrq-trigger"
5 |
--------------------------------------------------------------------------------
/ch10/kthread_stuck/Makefile:
--------------------------------------------------------------------------------
1 | # ch5/lkm_template/Makefile
2 | # ***************************************************************
3 | # This program is part of the source code released for the book
4 | # "Learn Linux Kernel Development"
5 | # (c) Author: Kaiwan N Billimoria
6 | # Publisher: Packt
7 | # GitHub repository:
8 | # https://github.com/PacktPublishing/Learn-Linux-Kernel-Development
9 | #
10 | # From: Ch 5 : Writing Your First Kernel Module LKMs, Part 2
11 | # ***************************************************************
12 | # Brief Description:
13 | # A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
14 | # the 'usual' targets (the build, install and clean), we incorporate targets to
15 | # do useful (and indeed required) stuff like:
16 | # - adhering to kernel coding style (indent+checkpatch)
17 | # - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
18 | # - two 'dummy' dynamic analysis targets (KASAN, LOCKDEP)
19 | # - a packaging (.tar.xz) target and
20 | # - a help target.
21 | #
22 | # To get started, just type:
23 | # make help
24 | #
25 | # For details, please refer the book, Ch 5.
26 |
27 | # To support cross-compiling for kernel modules:
28 | # For architecture (cpu) 'arch', invoke make as:
29 | # make ARCH= CROSS_COMPILE=
30 | ifeq ($(ARCH),arm)
31 | # *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
32 | KDIR ?= ~/rpi_work/kernel_rpi/linux
33 | else ifeq ($(ARCH),arm64)
34 | # *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
35 | # tree on your box
36 | KDIR ?= ~/kernel/linux-4.14
37 | else ifeq ($(ARCH),powerpc)
38 | # *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
39 | KDIR ?= ~/kernel/linux-4.9.1
40 | else
41 | # 'KDIR' is the Linux 'kernel headers' package on your host system; this is
42 | # usually an x86_64, but could be anything, really (f.e. building directly
43 | # on a Raspberry Pi implies that it's the host)
44 | KDIR ?= /lib/modules/$(shell uname -r)/build
45 | endif
46 |
47 | # Set FNAME_C to the kernel module name source filename (without .c)
48 | FNAME_C := kthread_stuck
49 |
50 | PWD := $(shell pwd)
51 | obj-m += ${FNAME_C}.o
52 | EXTRA_CFLAGS += -DDEBUG
53 |
54 | all:
55 | @echo
56 | @echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} EXTRA_CFLAGS=${EXTRA_CFLAGS} ---'
57 | @echo
58 | make -C $(KDIR) M=$(PWD) modules
59 | install:
60 | @echo
61 | @echo "--- installing ---"
62 | @echo " [First, invoke the 'make' ]"
63 | make
64 | @echo
65 | @echo " [Now for the 'sudo make install' ]"
66 | sudo make -C $(KDIR) M=$(PWD) modules_install
67 | sudo depmod
68 | clean:
69 | @echo
70 | @echo "--- cleaning ---"
71 | @echo
72 | make -C $(KDIR) M=$(PWD) clean
73 | rm -f *~ # from 'indent'
74 |
75 | #--------------- More (useful) targets! -------------------------------
76 | INDENT := indent
77 |
78 | # code-style : "wrapper" target over the following kernel code style targets
79 | code-style:
80 | make indent
81 | make checkpatch
82 |
83 | # indent- "beautifies" C code - to conform to the the Linux kernel
84 | # coding style guidelines.
85 | # Note! original source file(s) is overwritten, so we back it up.
86 | indent:
87 | @echo
88 | @echo "--- applying kernel code style indentation with indent ---"
89 | @echo
90 | mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
91 | ${INDENT} -linux --line-length95 *.[chsS]
92 | # add source files as required
93 |
94 | # Detailed check on the source code styling / etc
95 | checkpatch:
96 | make clean
97 | @echo
98 | @echo "--- kernel code style check with checkpatch.pl ---"
99 | @echo
100 | $(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
101 | # add source files as required
102 |
103 | #--- Static Analysis
104 | # sa : "wrapper" target over the following kernel static analyzer targets
105 | sa:
106 | make sa_sparse
107 | make sa_gcc
108 | make sa_flawfinder
109 | make sa_cppcheck
110 |
111 | # static analysis with sparse
112 | sa_sparse:
113 | make clean
114 | @echo
115 | @echo "--- static analysis with sparse ---"
116 | @echo
117 | # if you feel it's too much, use C=1 instead
118 | make C=2 CHECK="/usr/bin/sparse" -C $(KDIR) M=$(PWD) modules
119 |
120 | # static analysis with gcc
121 | sa_gcc:
122 | make clean
123 | @echo
124 | @echo "--- static analysis with gcc ---"
125 | @echo
126 | make W=1 -C $(KDIR) M=$(PWD) modules
127 |
128 | # static analysis with flawfinder
129 | sa_flawfinder:
130 | make clean
131 | @echo
132 | @echo "--- static analysis with flawfinder ---"
133 | @echo
134 | flawfinder *.[ch]
135 |
136 | # static analysis with cppcheck
137 | sa_cppcheck:
138 | make clean
139 | @echo
140 | @echo "--- static analysis with cppcheck ---"
141 | @echo
142 | cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
143 |
144 | # Packaging; just tar.xz as of now
145 | PKG_NAME := ${FNAME_C}
146 | tarxz-pkg:
147 | rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
148 | make clean
149 | @echo
150 | @echo "--- packaging ---"
151 | @echo
152 | tar caf ../${PKG_NAME}.tar.xz *
153 | ls -l ../${PKG_NAME}.tar.xz
154 | @echo '=== package created: ../$(PKG_NAME).tar.xz ==='
155 |
156 | help:
157 | @echo '=== Makefile Help : additional targets available ==='
158 | @echo
159 | @echo 'TIP: type make to show all valid targets'
160 | @echo
161 |
162 | @echo '--- 'usual' kernel LKM targets ---'
163 | @echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
164 | @echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default here: /lib/modules/$(shell uname -r)/)'
165 | @echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
166 |
167 | @echo
168 | @echo '--- kernel code style targets ---'
169 | @echo 'code-style : "wrapper" target over the following kernel code style targets'
170 | @echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
171 | @echo ' checkpatch : run the kernel code style checker tool on source file(s)'
172 |
173 | @echo
174 | @echo '--- kernel static analyzer targets ---'
175 | @echo 'sa : "wrapper" target over the following kernel static analyzer targets'
176 | @echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
177 | @echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
178 | @echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
179 | @echo ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
180 | @echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
181 |
182 | @echo
183 | @echo '--- kernel dynamic analysis targets ---'
184 | @echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
185 | @echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
186 | @echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
187 |
188 | @echo
189 | @echo '--- misc targets ---'
190 | @echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
191 | @echo 'help : this help target'
192 |
--------------------------------------------------------------------------------
/ch10/kthread_stuck/kthread_stuck.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch10/kthread_stuck/kthread_stuck.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 10 : Kernel panic, lockups and hangs
12 | ****************************************************************
13 | * Brief Description:
14 | * Added buggy code to deliberately spin on the CPU, in order to kick the
15 | * kernel watchdog into action and detect the softlockup caused!
16 | *
17 | * Original comment:
18 | * A simple LKM to demo delays and sleeps in the kernel.
19 | *
20 | * For details, please refer the book, Ch 10.
21 | */
22 | #define pr_fmt(fmt) "%s:%s(): " fmt, KBUILD_MODNAME, __func__
23 |
24 | #include
25 | #include
26 | #include
27 | #include // {get,put}_task_struct()
28 | #include // signal_pending()
29 | #include // allow_signal()
30 | #include
31 | #include
32 | #include
33 | #include "../../convenient.h"
34 |
35 | #define KTHREAD_NAME "kt_stuck"
36 | #define DO_SOFT_LOCKUP 1
37 | #define DO_HARD_LOCKUP 2
38 |
39 | MODULE_AUTHOR("[insert name]");
40 | MODULE_DESCRIPTION("a simple LKM to demo the kernel softlockup detector!");
41 | MODULE_LICENSE("Dual MIT/GPL"); // or whatever
42 | MODULE_VERSION("0.1");
43 |
44 | static int lockup_type = DO_SOFT_LOCKUP;
45 | module_param(lockup_type, int, 0);
46 | MODULE_PARM_DESC(lockup_type, "specify the lockup type; pass 1 for soft lockup (default), 2 for hard lockup");
47 |
48 | static struct task_struct *gkthrd_ts;
49 | static spinlock_t spinlock;
50 |
51 | /* Our simple kernel thread. */
52 | static int simple_kthread(void *arg)
53 | {
54 | u64 i = 0;
55 |
56 | PRINT_CTX();
57 | if (!current->mm)
58 | pr_info("mm field NULL, we are a kernel thread!\n");
59 |
60 | /*
61 | * By default all signals are masked for the kthread; allow a couple
62 | * so that we can 'manually' kill it
63 | */
64 | allow_signal(SIGINT);
65 | allow_signal(SIGQUIT);
66 |
67 | while(!kthread_should_stop()) {
68 | //------------------------------------
69 | pr_info("DELIBERATELY spinning on CPU core now...\n");
70 |
71 | if (lockup_type == DO_SOFT_LOCKUP)
72 | spin_lock(&spinlock);
73 | else
74 | spin_lock_irq(&spinlock);
75 |
76 | while (i < 10000000000) { // adjust these arbit #s for your system if reqd..
77 | i ++;
78 | if (!(i%50000000))
79 | PRINT_CTX();
80 | }
81 |
82 | if (lockup_type == DO_SOFT_LOCKUP)
83 | spin_unlock(&spinlock);
84 | else
85 | spin_unlock_irq(&spinlock);
86 | //------------------------------------
87 |
88 | pr_info("FYI, I, kernel thread PID %d, am going to sleep now...\n",
89 | current->pid);
90 | set_current_state(TASK_INTERRUPTIBLE);
91 | schedule(); // yield the processor, go to sleep...
92 | /* Aaaaaand we're back! Here, it's typically due to either the
93 | * SIGINT or SIGQUIT signal hitting us, or due to the rmmod (or shutdown)
94 | */
95 | if (signal_pending(current))
96 | break;
97 | }
98 |
99 | // We've been (rudely) interrupted by a signal...
100 | set_current_state(TASK_RUNNING);
101 | pr_info("FYI, I, kernel thread PID %d, have been rudely awoken; I shall"
102 | " now exit... Good day Sir!\n", current->pid);
103 |
104 | return 0;
105 | }
106 |
107 | static int kthread_simple_init(void)
108 | {
109 | int ret=0;
110 |
111 | if (lockup_type != DO_SOFT_LOCKUP && lockup_type != DO_HARD_LOCKUP) {
112 | pr_info("pass parameter lockup_type correctly\n");
113 | return -EINVAL;
114 | }
115 | pr_info("lockup type to test: %s\n", lockup_type == DO_HARD_LOCKUP ? "hard":"soft");
116 |
117 | spin_lock_init(&spinlock);
118 | pr_info("Lets now create a kernel thread...\n");
119 |
120 | /*
121 | * kthread_run(threadfn, data, namefmt, ...)
122 | * - it's just a thin wrapper over the kthread_create() API
123 | * The 2nd arg is any (void * arg) to pass to the just-born kthread,
124 | * and the return value is the task struct pointer on success
125 | */
126 | gkthrd_ts = kthread_run(simple_kthread, NULL, "lkd/%s", KTHREAD_NAME);
127 | if (IS_ERR(gkthrd_ts)) {
128 | ret = PTR_ERR(gkthrd_ts); // it's usually -ENOMEM
129 | pr_err("kthread creation failed (%d)\n", ret);
130 | return ret;
131 | }
132 | get_task_struct(gkthrd_ts); /* increment the kthread task structure's
133 | * reference count, marking it as being
134 | * in use
135 | */
136 | pr_info("Initialized, kernel thread task ptr is 0x%pK (actual=0x%px)\n"
137 | "See the new kernel thread 'lkd/%s' with ps (and kill it with SIGINT or SIGQUIT)\n",
138 | gkthrd_ts, gkthrd_ts, KTHREAD_NAME);
139 |
140 | return 0; // success
141 | }
142 |
143 | static void kthread_simple_exit(void)
144 | {
145 | kthread_stop(gkthrd_ts);
146 | /* waits for our kthread to terminate; it also
147 | * internally invokes the put_task_struct() to
148 | * decrement task's reference count
149 | */
150 | pr_info("kthread stopped, and LKM removed.\n");
151 | }
152 |
153 | module_init(kthread_simple_init);
154 | module_exit(kthread_simple_exit);
155 |
--------------------------------------------------------------------------------
/ch10/letspanic/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile
2 | # ***************************************************************
3 | # This program is part of the source code released for the book
4 | # "Linux Kernel Debugging"
5 | # (c) Author: Kaiwan N Billimoria
6 | # Publisher: Packt
7 | # GitHub repository:
8 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
9 | #
10 | # ***************************************************************
11 | # Brief Description:
12 | # A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
13 | # the 'usual' targets (the build, install and clean), we incorporate targets to
14 | # do useful (and indeed required) stuff like:
15 | # - adhering to kernel coding style (indent+checkpatch)
16 | # - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
17 | # - two _dummy_ dynamic analysis targets (KASAN, LOCKDEP); just to remind you!
18 | # - a packaging (.tar.xz) target and
19 | # - a help target.
20 | #
21 | # To get started, just type:
22 | # make help
23 | #
24 | # For details on this so-called 'better' Makefile, please refer my earlier book
25 | # 'Linux Kernel Programming', Packt, Mar 2021, Ch 5 section 'A "better" Makefile
26 | # template for your kernel modules'.
27 |
28 | #------------------------------------------------------------------
29 | # Set FNAME_C to the kernel module name source filename (without .c)
30 | # This enables you to use this Makefile as a template; just update this variable!
31 | # As well, the MYDEBUG variable (see it below) can be set to 'y' or 'n' (no being
32 | # the default)
33 | FNAME_C := letspanic
34 | #------------------------------------------------------------------
35 |
36 | # To support cross-compiling for kernel modules:
37 | # For architecture (cpu) 'arch', invoke make as:
38 | # make ARCH= CROSS_COMPILE=
39 | # The KDIR var is set to a sample path below; you're expected to update it on
40 | # your box to the appropriate path to the kernel src tree for that arch.
41 | ifeq ($(ARCH),arm)
42 | # *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
43 | KDIR ?= ~/rpi_work/kernel_rpi/linux
44 | else ifeq ($(ARCH),arm64)
45 | # *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
46 | # tree on your box
47 | KDIR ?= ~/kernel/linux-5.4
48 | else ifeq ($(ARCH),powerpc)
49 | # *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
50 | KDIR ?= ~/kernel/linux-5.0
51 | else
52 | # 'KDIR' is the Linux 'kernel headers' package on your host system; this is
53 | # usually an x86_64, but could be anything, really (f.e. building directly
54 | # on a Raspberry Pi implies that it's the host)
55 | KDIR ?= /lib/modules/$(shell uname -r)/build
56 | endif
57 |
58 | # Compiler
59 | CC := $(CROSS_COMPILE)gcc
60 | #CC := $(CROSS_COMPILE)gcc-10
61 | #CC := clang
62 |
63 | PWD := $(shell pwd)
64 | obj-m += ${FNAME_C}.o
65 |
66 | #--- Debug or production mode?
67 | # Set the MYDEBUG variable accordingly to y/n resp.
68 | # (Actually, debug info is always going to be generated when you build the
69 | # module on a debug kernel, where CONFIG_DEBUG_INFO is defined, making this
70 | # setting of the ccflags-y (or EXTRA_CFLAGS) variable mostly redundant (besides
71 | # the -DDEBUG).
72 | # This simply helps us influence the build on a production kernel, forcing
73 | # generation of debug symbols, if so required. Also, realize that the DEBUG
74 | # macro is turned on by many CONFIG_*DEBUG* options; hence, we use a different
75 | # macro var name, MYDEBUG).
76 | MYDEBUG := n
77 | ifeq (${MYDEBUG}, y)
78 |
79 | # https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
80 | # EXTRA_CFLAGS deprecated; use ccflags-y
81 | ccflags-y += -DDEBUG -g -ggdb -gdwarf-4 -Wall -fno-omit-frame-pointer -fvar-tracking-assignments
82 | else
83 | INSTALL_MOD_STRIP := 1
84 | #ccflags-y += --strip-debug
85 | endif
86 | # We always keep the dynamic debug facility enabled; this allows us to turn
87 | # dynamically turn on/off debug printk's later... To disable it simply comment
88 | # out the following line
89 | ccflags-y += -DDYNAMIC_DEBUG_MODULE
90 |
91 | KMODDIR ?= /lib/modules/$(shell uname -r)
92 | STRIP := ${CROSS_COMPILE}strip
93 |
94 | # gcc-10 issue:
95 | #ccflags-y += $(call cc-option,--allow-store-data-races)
96 |
97 | all:
98 | @echo
99 | @echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y=${ccflags-y} ---'
100 | @${CC} --version|head -n1
101 | @echo
102 | make -C $(KDIR) M=$(PWD) modules
103 | $(shell [ "${MYDEBUG}" != "y" ] && ${STRIP} --strip-debug ./${FNAME_C}.ko)
104 | install:
105 | @echo
106 | @echo "--- installing ---"
107 | @echo " [First, invoking the 'make' ]"
108 | make
109 | @echo
110 | @echo " [Now for the 'sudo make install' ]"
111 | sudo make -C $(KDIR) M=$(PWD) modules_install
112 | @echo " [If !debug, stripping debug info from ${KMODDIR}/extra/${FNAME_C}.ko]"
113 | $(shell if [ "${MYDEBUG}" != "y" ]; then sudo ${STRIP} --strip-debug ${KMODDIR}/extra/${FNAME_C}.ko; fi)
114 | clean:
115 | @echo
116 | @echo "--- cleaning ---"
117 | @echo
118 | make -C $(KDIR) M=$(PWD) clean
119 | # from 'indent'
120 | rm -f *~
121 |
122 | # Any usermode programs to build? Insert the build target(s) here
123 |
124 | #--------------- More (useful) targets! -------------------------------
125 | INDENT := indent
126 |
127 | # code-style : "wrapper" target over the following kernel code style targets
128 | code-style:
129 | make indent
130 | make checkpatch
131 |
132 | # indent- "beautifies" C code - to conform to the the Linux kernel
133 | # coding style guidelines.
134 | # Note! original source file(s) is overwritten, so we back it up.
135 | indent:
136 | @echo
137 | @echo "--- applying kernel code style indentation with indent ---"
138 | @echo
139 | mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
140 | ${INDENT} -linux --line-length95 *.[chsS]
141 | # add source files as required
142 |
143 | # Detailed check on the source code styling / etc
144 | checkpatch:
145 | make clean
146 | @echo
147 | @echo "--- kernel code style check with checkpatch.pl ---"
148 | @echo
149 | $(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
150 | # add source files as required
151 |
152 | #--- Static Analysis
153 | # sa : "wrapper" target over the following kernel static analyzer targets
154 | sa:
155 | make sa_sparse
156 | make sa_gcc
157 | make sa_flawfinder
158 | make sa_cppcheck
159 |
160 | # static analysis with sparse
161 | sa_sparse:
162 | make clean
163 | @echo
164 | @echo "--- static analysis with sparse ---"
165 | @echo
166 | # if you feel it's too much, use C=1 instead
167 | # NOTE: deliberately IGNORING warnings from kernel headers!
168 | make -Wsparse-all C=2 CHECK="/usr/bin/sparse --os=linux --arch=$(ARCH)" -C $(KDIR) M=$(PWD) modules 2>&1 |egrep -v "^\./include/.*\.h|^\./arch/.*\.h"
169 |
170 | # static analysis with gcc
171 | sa_gcc:
172 | make clean
173 | @echo
174 | @echo "--- static analysis with gcc ---"
175 | @echo
176 | make W=1 -C $(KDIR) M=$(PWD) modules
177 |
178 | # static analysis with flawfinder
179 | sa_flawfinder:
180 | make clean
181 | @echo
182 | @echo "--- static analysis with flawfinder ---"
183 | @echo
184 | flawfinder *.[ch]
185 |
186 | # static analysis with cppcheck
187 | sa_cppcheck:
188 | make clean
189 | @echo
190 | @echo "--- static analysis with cppcheck ---"
191 | @echo
192 | cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
193 |
194 | # Packaging; just tar.xz as of now
195 | PKG_NAME := ${FNAME_C}
196 | tarxz-pkg:
197 | rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
198 | make clean
199 | @echo
200 | @echo "--- packaging ---"
201 | @echo
202 | tar caf ../${PKG_NAME}.tar.xz *
203 | ls -l ../${PKG_NAME}.tar.xz
204 | @echo '=== package created: ../$(PKG_NAME).tar.xz ==='
205 | @echo 'Tip: when extracting, to extract into a dir of the same name as the tar file,'
206 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
207 |
208 | help:
209 | @echo '=== Makefile Help : additional targets available ==='
210 | @echo
211 | @echo 'TIP: type make to show all valid targets'
212 | @echo
213 |
214 | @echo '--- 'usual' kernel LKM targets ---'
215 | @echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
216 | @echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default here: /lib/modules/$(shell uname -r)/)'
217 | @echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
218 |
219 | @echo
220 | @echo '--- kernel code style targets ---'
221 | @echo 'code-style : "wrapper" target over the following kernel code style targets'
222 | @echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
223 | @echo ' checkpatch : run the kernel code style checker tool on source file(s)'
224 |
225 | @echo
226 | @echo '--- kernel static analyzer targets ---'
227 | @echo 'sa : "wrapper" target over the following kernel static analyzer targets'
228 | @echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
229 | @echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
230 | @echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
231 | @echo ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
232 | @echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
233 |
234 | @echo
235 | @echo '--- kernel dynamic analysis targets ---'
236 | @echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
237 | @echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
238 | @echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
239 |
240 | @echo
241 | @echo '--- misc targets ---'
242 | @echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
243 | @echo ' Tip: when extracting, to extract into a dir of the same name as the tar file,'
244 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
245 | @echo 'help : this help target'
246 |
--------------------------------------------------------------------------------
/ch10/letspanic/letspanic.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch10/letspanic/letspanic.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 10: Kernel panic, hangcheck and watchdogs
12 | ****************************************************************
13 | * Brief Description:
14 | * "To conquer the beast one must first understand it. In that spirit, let' panic!"
15 | *
16 | * For details, please refer the book, Ch 10.
17 | */
18 | #define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__
19 |
20 | #include
21 | #include
22 | #include
23 |
24 | MODULE_AUTHOR("");
25 | MODULE_DESCRIPTION("LKD book:ch10/letspanic: deliberately cause a kernel panic");
26 | MODULE_LICENSE("Dual MIT/GPL");
27 | MODULE_VERSION("0.1");
28 |
29 | static int myglobalstate = 0xeee;
30 | static int __init letspanic_init(void)
31 | {
32 | pr_warn("Hello, panic world (yay!)\n");
33 | panic("whoa, a kernel panic! myglobalstate = 0x%x", myglobalstate);
34 |
35 | return 0; /* success */
36 | }
37 | module_init(letspanic_init);
38 |
39 | // a quick trick- don't specify a cleanup function, as we'll never get there
40 | #if 0
41 | static void __exit letspanic_exit(void)
42 | {
43 | pr_info("Goodbye, panic world\n");
44 | }
45 | module_exit(letspanic_exit);
46 | #endif
47 |
--------------------------------------------------------------------------------
/ch10/netcon:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # netcon
3 | # Simple helper script to get netconsole running on the sender system.
4 | # You'd typically want to run a net receiver on the receiver system:
5 | # netcat -d -u -l 6666
6 | name=$(basename $0)
7 | [ $# -ne 1 ] && {
8 | echo "Usage: ${name} receiver-IPaddr"
9 | exit 1
10 | }
11 |
12 | SENDER_IFNAME=enp0s8 # !ASSUMPTION! update as reqd
13 | SENDER_IP=$(ifconfig ${SENDER_IFNAME} | awk '/inet /{print $2}')
14 | #RECV_IFNAME=enp0s8 # !ASSUMPTION! update as reqd
15 | RECV_IP=$1
16 |
17 | sudo rmmod netconsole 2>/dev/null # rm any stale instance first
18 | echo "sudo modprobe netconsole netconsole=@${SENDER_IP}/${SENDER_IFNAME},@${RECV_IP}/"
19 | sudo modprobe netconsole netconsole=@${SENDER_IP}/${SENDER_IFNAME},@${RECV_IP}/
20 | lsmod|grep "^netconsole" || {
21 | echo "netconsole module not loaded?"
22 | exit 1
23 | }
24 | sudo dmesg|grep "netconsole:"
25 | exit 0
26 |
--------------------------------------------------------------------------------
/ch10/panic_notifier/panic_notifier_lkm.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch10/panic_notifier/panic_notifier_lkm.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 10: Kernel panic, hangcheck and watchdogs
12 | ****************************************************************
13 | * Brief Description:
14 | * This module is a simple PoC; it registers a custom panic handler callback,
15 | * by registering with the kernel's predefined panic notifier chain (an atomic
16 | * type of notifier chain). Thus, our custom panic handler will be invoked upon
17 | * kernel panic.
18 | *
19 | * For details, please refer the book, Ch 10.
20 | */
21 | #define pr_fmt(fmt) "%s:%s(): " fmt, KBUILD_MODNAME, __func__
22 | #include
23 | #include
24 | #include
25 |
26 | // see kernel commit f39650de687e35766572ac89dbcd16a5911e2f0a
27 | #include
28 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)
29 | #include
30 | #else
31 | #include
32 | #endif
33 |
34 | /* The atomic_notifier_chain_[un]register() api's are GPL-exported! */
35 | MODULE_LICENSE("Dual MIT/GPL");
36 |
37 | /* Do what's required here for the product/project,
38 | * but keep it simple. Left essentially empty here..
39 | */
40 | static void dev_ring_alarm(void)
41 | {
42 | pr_emerg("!!! ALARM !!!\n");
43 | }
44 |
45 | static int mypanic_handler(struct notifier_block *nb, unsigned long val, void *data)
46 | {
47 | pr_emerg("\n************ Panic : SOUNDING ALARM ************\n\
48 | val = %lu\n\
49 | data(str) = \"%s\"\n", val, (char *)data);
50 | dev_ring_alarm();
51 |
52 | return NOTIFY_OK;
53 | }
54 |
55 | static struct notifier_block mypanic_nb = {
56 | .notifier_call = mypanic_handler,
57 | // .priority = INT_MAX
58 | };
59 |
60 | static int __init panic_notifier_lkm_init(void)
61 | {
62 | atomic_notifier_chain_register(&panic_notifier_list, &mypanic_nb);
63 | pr_info("Registered panic notifier\n");
64 |
65 | /*
66 | * Make #if 1 to have this module panic all by itself :-)
67 | * Else, we use our ../cause_oops_panic.sh script to trigger an
68 | * Oops and kernel panic!
69 | */
70 | #if 0
71 | mdelay(500);
72 | panic("Linux Kernel Debugging!");
73 | #endif
74 |
75 | return 0; /* success */
76 | }
77 |
78 | static void __exit panic_notifier_lkm_exit(void)
79 | {
80 | atomic_notifier_chain_unregister(&panic_notifier_list, &mypanic_nb);
81 | pr_info("Unregistered panic notifier\n");
82 | }
83 |
84 | module_init(panic_notifier_lkm_init);
85 | module_exit(panic_notifier_lkm_exit);
86 |
--------------------------------------------------------------------------------
/ch10/workq_stall/workq_stall.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch10/workq_stall/workq_stall.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 10 : Kernel panic, lockups and hangs
12 | ****************************************************************
13 | * Brief Description:
14 | * This code is originally from my earlier LKP-2 book, here:
15 | * https://github.com/PacktPublishing/Linux-Kernel-Programming-Part-2/tree/main/ch5/workq_simple
16 | * To simulate a workqueue stall, we insert a couple of lines of CPU-intensive
17 | * code in our kernel-default workqueue work function.
18 | * The kernel's workqueue stall etection code detects this - via the WQ watchdog
19 | * - and calls BUG() !
20 | *
21 | * Original comment:
22 | * A demo of a simple workqueue in action. We use the default kernel-global
23 | * workqueue; we enqueue a work item onto it (via INIT_WORK()) and 'schedule'
24 | * it to execute via the workqueue kthreads (using the schedule_work() API).
25 | * We cleanup (in the exit method) via the cancel_work_sync() API.
26 | * This module is built upon our earlier ch5/timer_simple LKM. Here, we also
27 | * demo making use of the container_of() macro to be able to access data from
28 | * within our work queue callback function - a pretty typical thing. Also, we
29 | * setup the timer to keep expiring until our 'data' variable hits zero; in
30 | * addition this time, we use this - the kernel timeout - as an opportunity to
31 | * 'schedule' our work queue function to run...
32 | *
33 | * For details, please refer the book, Ch 10.
34 | */
35 | #define pr_fmt(fmt) "%s:%s(): " fmt, KBUILD_MODNAME, __func__
36 |
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include "../../convenient.h"
44 |
45 | #define INITIAL_VALUE 3
46 |
47 | MODULE_AUTHOR("[insert name]");
48 | MODULE_DESCRIPTION("a LKM to demo a simple workqueue stall");
49 | MODULE_LICENSE("Dual MIT/GPL"); // or whatever
50 | MODULE_VERSION("0.1");
51 |
52 | static struct st_ctx {
53 | struct work_struct work;
54 | struct timer_list tmr;
55 | int data;
56 | } ctx;
57 | static unsigned long exp_ms = 420;
58 | static u64 t1, t2;
59 |
60 | /*
61 | * ding() - our timer's callback function!
62 | */
63 | static void ding(struct timer_list *timer)
64 | {
65 | struct st_ctx *priv = from_timer(priv, timer, tmr);
66 |
67 | pr_info("timed out... data=%d\n", priv->data);
68 | priv->data--;
69 | PRINT_CTX();
70 |
71 | /* until countdown done, fire it again! */
72 | if (priv->data)
73 | mod_timer(&priv->tmr, jiffies + msecs_to_jiffies(exp_ms));
74 |
75 | /* Now 'schedule' our workqueue function to run */
76 | if (!schedule_work(&priv->work)) {
77 | pr_notice("not added work item as it's already on the kernel-global workqueue!\n");
78 | return;
79 | }
80 | t1 = ktime_get_real_ns();
81 | }
82 |
83 | /*
84 | * work_func() - our workqueue callback function!
85 | */
86 | static void work_func(struct work_struct *work)
87 | {
88 | struct st_ctx *priv = container_of(work, struct st_ctx, work);
89 | u64 i = 0;
90 |
91 | t2 = ktime_get_real_ns();
92 | pr_info("In our workq function: data=%d\n", priv->data);
93 | PRINT_CTX();
94 | SHOW_DELTA(t2, t1);
95 |
96 | /* Deliberately spin for a loooong while... causing the kernel softlockup
97 | * detector to swing into action!
98 | */
99 | pr_info("Deliberately locking up the cpu now!\n");
100 | while (1)
101 | i += 3;
102 | }
103 |
104 | static int __init workq_simple_init(void)
105 | {
106 | u64 i = 0;
107 |
108 | pr_info("Deliberately locking up the cpu now!\n");
109 | while (1)
110 | i ++;
111 |
112 |
113 | ctx.data = INITIAL_VALUE;
114 |
115 | /* Initialize our workqueue */
116 | INIT_WORK(&ctx.work, work_func);
117 |
118 | /* Initialize our kernel timer */
119 | ctx.tmr.expires = jiffies + msecs_to_jiffies(exp_ms);
120 | ctx.tmr.flags = 0;
121 | timer_setup(&ctx.tmr, ding, 0);
122 |
123 | pr_info("Work queue initialized, timer set to expire in %ld ms\n", exp_ms);
124 | add_timer(&ctx.tmr); /* Arm it; lets get going! */
125 |
126 | return 0; /* success */
127 | }
128 |
129 | static void __exit workq_simple_exit(void)
130 | {
131 | // Wait for any pending work (queue) to finish
132 | if (cancel_work_sync(&ctx.work))
133 | pr_info("yes, there was indeed some pending work; now done...\n");
134 | /* flush_scheduled_work(); Alternative, but harder to use correctly, as well
135 | * as overkill; cancel_work_[delayed_]sync() is simpler.. */
136 |
137 | // Wait for possible timeouts to complete... and then delete the timer
138 | del_timer_sync(&ctx.tmr);
139 | pr_info("removed\n");
140 | }
141 |
142 | module_init(workq_simple_init);
143 | module_exit(workq_simple_exit);
144 |
--------------------------------------------------------------------------------
/ch11/.gitattributes:
--------------------------------------------------------------------------------
1 | rootfs_deb.img.7z filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/ch11/README.txt:
--------------------------------------------------------------------------------
1 | README.txt
2 | Ch 11 - Using KGDB
3 |
4 | (As explained in Ch 11 - 'Using Kernel GDB (KGDB)' section 'Technical requirements'):
5 |
6 | For the section 'Debugging kernel modules with KGDB':
7 | These are the source files to begin with; this is after you've downloaded the 7zip file...
8 | The explanation of how to download and set it up is in the 'Technical requirements'
9 | section of this chapter:
10 |
11 | ch11 $ tree .
12 | .
13 | ├── gdbline.sh
14 | ├── kconfig_x86-64_target
15 | ├── kgdb_try
16 | │ ├── kgdb_try.c
17 | │ └── Makefile
18 | ├── README.txt
19 | ├── rootfs_deb.img.7z
20 | └── run_target.sh
21 |
22 | 1 directory, 7 files
23 |
24 |
25 | Now extract it the root filesystem:
26 | ch11 $ 7z x rootfs_deb.img.7z
27 | [...]
28 |
29 | You'll get the uncompressed rootfs image file images/rootfs_deb.img (size 512 MB).
30 |
31 | ch11 $ tree .
32 | .
33 | ├── gdbline.sh
34 | ├── images
35 | │ └── rootfs_deb.img
36 | ├── kconfig_x86-64_target
37 | ├── kgdb_try
38 | │ ├── kgdb_try.c
39 | │ └── Makefile
40 | ├── README.txt
41 | ├── rootfs_deb.img.7z
42 | └── run_target.sh
43 |
44 | 2 directories, 8 files
45 | ch11 $
46 |
47 | Use it (and the other source files) as explained in the book, Ch 11 section
48 | 'Debugging kernel modules with KGDB'.
49 |
--------------------------------------------------------------------------------
/ch11/gdbline.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # gdbline module image
3 | #
4 | # Outputs an add-symbol-file line suitable for pasting into gdb to examine
5 | # a loaded module.
6 | # (Here used in conjunction with the hello_gdb.ko kernel module; gdb demo).
7 | #
8 | # Credit: mostly based on the script from the venerable LDD3 book!
9 | name=$(basename $0)
10 | [ $(id -u) -ne 0 ] && {
11 | echo "${name} requires root."
12 | exit 1
13 | }
14 |
15 | if [ $# -ne 2 ]; then
16 | echo "Usage: ${name} module-name image-filename"
17 | echo " module-name: name of the (already inserted) kernel module (without the .ko)"
18 | echo " image-filename: pathname to the kernel module."
19 | exit 1
20 | fi
21 | if [ ! -d /sys/module/$1/sections ]; then
22 | echo "${name}: $1: module not inserted?"
23 | exit 1
24 | fi
25 | if [ ! -f $2 ]; then
26 | echo "${name}: $2 not a valid file"
27 | exit 1
28 | fi
29 |
30 | cd /sys/module/$1/sections
31 | echo "Copy-paste the following lines into GDB"
32 | echo "---snip---"
33 |
34 | [ -f .text ] && echo -n add-symbol-file $2 $(/bin/cat .text)
35 |
36 | for section in .[a-z]*
37 | do
38 | #echo "sec = ${section}"
39 | case "${section}" in
40 | .strtab|.symtab|.text) continue
41 | ;;
42 | *)
43 | echo " \\"
44 | echo -n " -s" ${section} $(/bin/cat ${section})
45 | esac
46 | done
47 |
48 | echo "
49 | ---snip---"
50 | echo
51 |
--------------------------------------------------------------------------------
/ch11/kgdb_try/kgdb_try.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch11/kgdb_try/kgdb_try.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 11: Using Kernel GDB (KGDB)
12 | ****************************************************************
13 | * Brief Description:
14 | *
15 | * For details, please refer the book, Ch 11.
16 | */
17 | #define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include "../../convenient.h"
27 |
28 | MODULE_AUTHOR("");
29 | MODULE_DESCRIPTION("LKD book:ch11/kgdb_try: support for a simple demo using KGDB on a module");
30 | MODULE_LICENSE("Dual MIT/GPL");
31 | MODULE_VERSION("0.1");
32 |
33 | #define NUM 32
34 | u8 gdata[NUM];
35 |
36 | static void do_the_work(struct work_struct *);
37 | static DECLARE_DELAYED_WORK(my_work, do_the_work);
38 |
39 | /*
40 | * Our delayed workqueue callback function
41 | */
42 | static void do_the_work(struct work_struct *work)
43 | {
44 | u8 buf[10];
45 | int i;
46 |
47 | pr_info("In our workq function\n");
48 | for (i=0; i <=10; i++)
49 | buf[i] = (u8)i;
50 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, 10);
51 |
52 | /* Interesting! This bug (below) is caught at compile time on x86_64! Output:
53 | * ...
54 | * In function 'memset',
55 | * inlined from 'do_the_work' at <...>/ch11/kgdb_try/kgdb_try.c:46:2:
56 | * ./include/linux/string.h:381:3: error: call to '__write_overflow' declared with attribute error: detected write beyond size of object passed as 1st parameter
57 | * 381 | __write_overflow();
58 | * So, we leave it commented out...
59 | */
60 | //memset(gdata, 0xff, NUM+PAGE_SIZE/2);
61 | pr_info("done\n");
62 | }
63 |
64 | static int __init kgdb_try_init(void)
65 | {
66 | pr_info("Generating Oops via kernel bug in a delayed workqueue function\n");
67 | INIT_DELAYED_WORK(&my_work, do_the_work);
68 | schedule_delayed_work(&my_work, msecs_to_jiffies(2500));
69 |
70 | return 0; /* success */
71 | }
72 |
73 | static void __exit kgdb_try_exit(void)
74 | {
75 | cancel_delayed_work_sync(&my_work);
76 | pr_info("Goodbye\n");
77 | }
78 |
79 | module_init(kgdb_try_init);
80 | module_exit(kgdb_try_exit);
81 |
--------------------------------------------------------------------------------
/ch11/rootfs_deb.img.7z:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:d5401f0603433adb3d190a687fa943d8c68a234996f8a9c4a6630d141b6046e6
3 | size 185959864
4 |
--------------------------------------------------------------------------------
/ch11/run_target.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # run_target.sh
3 | # Helper script to run Qemu such that the kernel waits in early boot for the
4 | # GDB client to connect
5 | # (Part of content covered in Linux Kernel Debugging, Kaiwan N Billimoria, Packt
6 | # Ch 11 - Using Kernel GDB (KGDB))
7 | name=$(basename $0)
8 | [ $# -ne 2 ] && {
9 | echo "Usage: ${name} path-to-kernel-[b]zimage path-to-rootfs-image"
10 | exit 1
11 | }
12 | KERNIMG=$1
13 | ROOTFS=$2
14 |
15 | [ ! -f ${KERNIMG} ] && {
16 | echo "${name}: kernel image \"$1\" not found? aborting"; exit 1
17 | }
18 | [ ! -f ${ROOTFS} ] && {
19 | echo "${name}: rootfs image \"$2\" not found? aborting"; exit 1
20 | }
21 | RAM=1G
22 | CPU_CORES=2
23 |
24 | echo "Note:
25 | 1. First shut down any other hypervisor instance that may be running
26 | 2. Once run, this guest qemu system will *wait* for GDB to connect from the host:
27 | On the host, do:
28 |
29 | $ gdb -q /vmlinux
30 | (gdb) target remote :1234
31 | "
32 | echo "qemu-system-x86_64 \
33 | -kernel ${KERNIMG}
34 | -append "console=ttyS0 root=/dev/sda earlyprintk=serial rootfstype=ext4 rootwait nokaslr" \
35 | -hda ${ROOTFS} \
36 | -nographic -m ${RAM} -smp ${CPU_CORES} \
37 | -S -s"
38 | qemu-system-x86_64 \
39 | -kernel ${KERNIMG} \
40 | -append "console=ttyS0 root=/dev/sda earlyprintk=serial rootfstype=ext4 rootwait nokaslr" \
41 | -hda ${ROOTFS} \
42 | -nographic -m ${RAM} -smp ${CPU_CORES} \
43 | -S -s
44 | # -S Do not start CPU at startup (you must type 'c' in the monitor).
45 | # -s Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234.
46 |
--------------------------------------------------------------------------------
/ch3/dyndbg.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # dyndbg
3 | # Simple frontend to the kernel's Dynamic Debug facility
4 | # (c) 2021, Kaiwan NB
5 | # License: MIT
6 | name=$(basename $0)
7 | SEP="------------------------------------------------------------------------"
8 |
9 | get_control_file()
10 | {
11 | local ctrlfile
12 | mount|grep -q -w debugfs
13 | if [ $? -eq 0 ]; then
14 | local dbgfs_mnt=$(mount|grep -w debugfs|awk '{print $3}')
15 | [ -z "${dbgfs_mnt}" ] && return -1
16 | ctrlfile="${dbgfs_mnt}/dynamic_debug/control"
17 | else
18 | ctrlfile="/proc/dynamic_debug/control"
19 | fi
20 | echo "${ctrlfile}"
21 | }
22 |
23 | # show_dbgpr_on()
24 | # Params:
25 | # $1 : dyn debug control file
26 | show_dbgpr_on()
27 | {
28 | [ $# -lt 1 ] && return
29 | # first line is the format-spec
30 | local num_on=$(grep -v " =_ " ${1} |sed '1d' |wc -l)
31 | if [ ${num_on} -le 0 ]; then
32 | echo "No dynamic debug prints are currently enabled"
33 | return
34 | fi
35 | echo "${SEP}
36 | ${num_on} dynamic debug prints are currently enabled
37 | ${SEP}"
38 | grep -v " =_ " ${1}
39 | }
40 |
41 | # show_dbgpr_off()
42 | # Params:
43 | # $1 : dyn debug control file
44 | show_dbgpr_off()
45 | {
46 | [ $# -lt 1 ] && return
47 | # first line is the format-spec
48 | local num_on=$(grep -v " =_ " ${1} |sed '1d' |wc -l)
49 | if [ ${num_on} -le 0 ]; then
50 | echo "No dynamic debug prints are currently enabled"
51 | return
52 | fi
53 | echo "${SEP}
54 | ${num_on} dynamic debug prints are currently enabled
55 | ${SEP}"
56 | grep " =_ " ${1}
57 | }
58 |
59 | #--- 'main' ---
60 |
61 | if [ $(id -u) -ne 0 ] ; then
62 | echo "${name}: needs root."
63 | exit 1
64 | fi
65 |
66 | CTRLFILE=$(get_control_file)
67 | show_dbgpr_on ${CTRLFILE}
68 |
69 | exit 0
70 |
--------------------------------------------------------------------------------
/ch3/miscdrv_rdwr/rdwr_test_secret.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch1/miscdrv_rdwr/rdwr_test_secret.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Programming (Part 2)"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Programming-Part-2
10 | *
11 | * From: Ch 1 : Writing a Simple Misc Character Device Driver
12 | ****************************************************************
13 | * Brief Description:
14 | * A simple test bed for the miscdrv_rdwr demo driver; a small user space app
15 | * to issue the read(2) and write(2) system calls upon a given (device) file.
16 | * Also, again as a demo, we use the read(2) to retreive the 'secret'
17 | * from the driver within kernel-space. Equivalently, one can use the write(2)
18 | * change the 'secret' (just plain text).
19 | *
20 | * For details, please refer the book, Ch 1.
21 | * License: Dual MIT/GPL
22 | */
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | #define MAXBYTES 128 /* Must match the driver; we should actually use a
33 | * common header file for things like this */
34 | static int stay_alive;
35 |
36 | static inline void usage(char *prg)
37 | {
38 | fprintf(stderr,
39 | "Usage: %s opt=read/write device_file [\"secret-msg\"]\n"
40 | " opt = 'r' => we shall issue the read(2), retrieving the 'secret' form the driver\n"
41 | " opt = 'w' => we shall issue the write(2), writing the secret message \n"
42 | " (max %d bytes)\n", prg, MAXBYTES);
43 | }
44 |
45 | int main(int argc, char **argv)
46 | {
47 | char opt = 'r';
48 | int fd, flags = O_RDONLY;
49 | ssize_t n;
50 | char *buf = NULL;
51 | size_t num = 0;
52 |
53 | if (argc < 3) {
54 | usage(argv[0]);
55 | exit(EXIT_FAILURE);
56 | }
57 |
58 | opt = argv[1][0];
59 | if (opt != 'r' && opt != 'w') {
60 | usage(argv[0]);
61 | exit(EXIT_FAILURE);
62 | }
63 | if ((opt == 'w' && argc != 4) || (opt == 'r' && argc != 3)) {
64 | usage(argv[0]);
65 | exit(EXIT_FAILURE);
66 | }
67 | if ('w' == opt && strnlen(argv[3], MAXBYTES) > MAXBYTES) {
68 | fprintf(stderr, "%s: too big a secret (%zu bytes); pl restrict"
69 | " to %d bytes max\n", argv[0], strnlen(argv[3],
70 | MAXBYTES),
71 | MAXBYTES);
72 | exit(EXIT_FAILURE);
73 | }
74 |
75 | if ('w' == opt)
76 | flags = O_WRONLY;
77 | fd = open(argv[2], flags, 0);
78 | if (fd == -1) {
79 | fprintf(stderr, "%s: open(2) on %s failed\n", argv[0], argv[2]);
80 | perror("open");
81 | exit(EXIT_FAILURE);
82 | }
83 | printf("Device file %s opened (in %s mode): fd=%d\n",
84 | argv[2], (flags == O_RDONLY ? "read-only" : "write-only"), fd);
85 |
86 | if ('w' == opt) {
87 | num = strnlen(argv[3], MAXBYTES) + 1; // IMP! +1 to include the NULL byte!
88 | if (num > MAXBYTES)
89 | num = MAXBYTES;
90 | } else
91 | num = MAXBYTES;
92 |
93 | buf = malloc(num);
94 | if (!buf) {
95 | fprintf(stderr, "%s: out of memory!\n", argv[0]);
96 | close(fd);
97 | exit(EXIT_FAILURE);
98 | }
99 |
100 | if ('r' == opt) {
101 | n = read(fd, buf, num);
102 | if (n < 0) {
103 | perror("read failed");
104 | fprintf(stderr, "Tip: see kernel log\n");
105 | free(buf);
106 | close(fd);
107 | exit(EXIT_FAILURE);
108 | }
109 | printf("%s: read %zd bytes from %s\n", argv[0], n, argv[2]);
110 | printf("The 'secret' is:\n \"%.*s\"\n", (int)n, buf);
111 | } else {
112 | strncpy(buf, argv[3], num);
113 | n = write(fd, buf, num);
114 | if (n < 0) {
115 | perror("write failed");
116 | fprintf(stderr, "Tip: see kernel log\n");
117 | free(buf);
118 | close(fd);
119 | exit(EXIT_FAILURE);
120 | }
121 | printf("%s: wrote %zd bytes to %s\n", argv[0], n, argv[2]);
122 | }
123 |
124 | if (stay_alive) {
125 | printf("%s:%d: stayin' alive (in pause()) ... \n", argv[0],
126 | getpid());
127 | pause();
128 | }
129 |
130 | free(buf);
131 | close(fd);
132 | exit(EXIT_SUCCESS);
133 | }
134 |
--------------------------------------------------------------------------------
/ch3/printk_loglevels/printk_loglevels.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch5/printk_loglevels/printk_loglevels.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 5: Debug via Instrumentation - printk and friends
12 | ****************************************************************
13 | * Brief Description:
14 | * Quick test to see kernel printk's at all 8 log levels (and the pr_devel());
15 | * when run on a console, only those messages < current (console) log level
16 | * (seen as the first integer in the output of /proc/sys/kernel/printk) will
17 | * appear on the console device.
18 | *
19 | * For details, please refer the book, Ch 5.
20 | */
21 | #define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__
22 |
23 | #include
24 | #include
25 | #include
26 |
27 | MODULE_AUTHOR("");
28 | MODULE_DESCRIPTION("LKD book:ch5/printk_loglevels: print at each kernel log level");
29 | MODULE_LICENSE("Dual MIT/GPL");
30 | MODULE_VERSION("0.1");
31 |
32 | static int __init printk_loglevels_init(void)
33 | {
34 | pr_emerg("Hello, debug world @ log-level KERN_EMERG [%d]\n", LOGLEVEL_EMERG);
35 | pr_alert("Hello, debug world @ log-level KERN_ALERT [%d]\n", LOGLEVEL_ALERT);
36 | pr_crit("Hello, debug world @ log-level KERN_CRIT [%d]\n", LOGLEVEL_CRIT);
37 | pr_err("Hello, debug world @ log-level KERN_ERR [%d]\n", LOGLEVEL_ERR);
38 | pr_warn("Hello, debug world @ log-level KERN_WARNING [%d]\n", LOGLEVEL_WARNING);
39 | pr_notice("Hello, debug world @ log-level KERN_NOTICE [%d]\n", LOGLEVEL_NOTICE);
40 | pr_info("Hello, debug world @ log-level KERN_INFO [%d]\n", LOGLEVEL_INFO);
41 | pr_debug("Hello, debug world @ log-level KERN_DEBUG [%d]\n", LOGLEVEL_DEBUG);
42 | pr_devel("Hello, debug world via the pr_devel() macro (eff @KERN_DEBUG) [%d]\n", LOGLEVEL_DEBUG);
43 |
44 | return 0; /* success */
45 | }
46 |
47 | static void __exit printk_loglevels_exit(void)
48 | {
49 | pr_info("Goodbye, debug world @ log-level KERN_INFO [%d]\n", LOGLEVEL_INFO);
50 | }
51 |
52 | module_init(printk_loglevels_init);
53 | module_exit(printk_loglevels_exit);
54 |
--------------------------------------------------------------------------------
/ch3/ratelimit_test/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile
2 | # ***************************************************************
3 | # This program is part of the source code released for the book
4 | # "Linux Kernel Debugging"
5 | # (c) Author: Kaiwan N Billimoria
6 | # Publisher: Packt
7 | # GitHub repository:
8 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
9 | #
10 | # ***************************************************************
11 | # Brief Description:
12 | # A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
13 | # the 'usual' targets (the build, install and clean), we incorporate targets to
14 | # do useful (and indeed required) stuff like:
15 | # - adhering to kernel coding style (indent+checkpatch)
16 | # - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
17 | # - two _dummy_ dynamic analysis targets (KASAN, LOCKDEP); just to remind you!
18 | # - a packaging (.tar.xz) target and
19 | # - a help target.
20 | #
21 | # To get started, just type:
22 | # make help
23 | #
24 | # For details on this so-called 'better' Makefile, please refer my earlier book
25 | # 'Linux Kernel Programming', Packt, Mar 2021, Ch 5 section 'A "better" Makefile
26 | # template for your kernel modules'.
27 |
28 | #------------------------------------------------------------------
29 | # Set FNAME_C to the kernel module name source filename (without .c)
30 | # This enables you to use this Makefile as a template; just update this variable!
31 | # As well, the MYDEBUG variable (see it below) can be set to 'y' or 'n' (no being
32 | # the default)
33 | FNAME_C := ratelimit_test
34 | #------------------------------------------------------------------
35 |
36 | # To support cross-compiling for kernel modules:
37 | # For architecture (cpu) 'arch', invoke make as:
38 | # make ARCH= CROSS_COMPILE=
39 | # The KDIR var is set to a sample path below; you're expected to update it on
40 | # your box to the appropriate path to the kernel src tree for that arch.
41 | ifeq ($(ARCH),arm)
42 | # *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
43 | KDIR ?= ~/rpi_work/kernel_rpi/linux
44 | else ifeq ($(ARCH),arm64)
45 | # *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
46 | # tree on your box
47 | KDIR ?= ~/kernel/linux-5.4
48 | else ifeq ($(ARCH),powerpc)
49 | # *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
50 | KDIR ?= ~/kernel/linux-5.0
51 | else
52 | # 'KDIR' is the Linux 'kernel headers' package on your host system; this is
53 | # usually an x86_64, but could be anything, really (f.e. building directly
54 | # on a Raspberry Pi implies that it's the host)
55 | KDIR ?= /lib/modules/$(shell uname -r)/build
56 | endif
57 |
58 | # Compiler
59 | CC := $(CROSS_COMPILE)gcc
60 | #CC := $(CROSS_COMPILE)gcc-10
61 | #CC := clang
62 |
63 | PWD := $(shell pwd)
64 | obj-m += ${FNAME_C}.o
65 |
66 | #--- Debug or production mode?
67 | # Set the MYDEBUG variable accordingly to y/n resp.
68 | # (Actually, debug info is always going to be generated when you build the
69 | # module on a debug kernel, where CONFIG_DEBUG_INFO is defined, making this
70 | # setting of the ccflags-y (or EXTRA_CFLAGS) variable mostly redundant (besides
71 | # the -DDEBUG).
72 | # This simply helps us influence the build on a production kernel, forcing
73 | # generation of debug symbols, if so required. Also, realize that the DEBUG
74 | # macro is turned on by many CONFIG_*DEBUG* options; hence, we use a different
75 | # macro var name, MYDEBUG).
76 | MYDEBUG := n
77 | ifeq (${MYDEBUG}, y)
78 |
79 | # https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
80 | # EXTRA_CFLAGS deprecated; use ccflags-y
81 | ccflags-y += -DDEBUG -g -ggdb -gdwarf-4 -Wall -fno-omit-frame-pointer -fvar-tracking-assignments
82 | else
83 | INSTALL_MOD_STRIP := 1
84 | #ccflags-y += --strip-debug
85 | endif
86 | # We always keep the dynamic debug facility enabled; this allows us to turn
87 | # dynamically turn on/off debug printk's later... To disable it simply comment
88 | # out the following line
89 | ccflags-y += -DDYNAMIC_DEBUG_MODULE
90 |
91 | KMODDIR ?= /lib/modules/$(shell uname -r)
92 | STRIP := ${CROSS_COMPILE}strip
93 |
94 | # gcc-10 issue:
95 | #ccflags-y += $(call cc-option,--allow-store-data-races)
96 |
97 | all:
98 | @echo
99 | @echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y=${ccflags-y} ---'
100 | @${CC} --version|head -n1
101 | @echo
102 | make -C $(KDIR) M=$(PWD) modules
103 | $(shell [ "${MYDEBUG}" != "y" ] && ${STRIP} --strip-debug ./${FNAME_C}.ko)
104 | install:
105 | @echo
106 | @echo "--- installing ---"
107 | @echo " [First, invoking the 'make' ]"
108 | make
109 | @echo
110 | @echo " [Now for the 'sudo make install' ]"
111 | sudo make -C $(KDIR) M=$(PWD) modules_install
112 | @echo " [If !debug, stripping debug info from ${KMODDIR}/extra/${FNAME_C}.ko]"
113 | $(shell if [ "${MYDEBUG}" != "y" ]; then sudo ${STRIP} --strip-debug ${KMODDIR}/extra/${FNAME_C}.ko; fi)
114 | clean:
115 | @echo
116 | @echo "--- cleaning ---"
117 | @echo
118 | make -C $(KDIR) M=$(PWD) clean
119 | # from 'indent'
120 | rm -f *~
121 |
122 | # Any usermode programs to build? Insert the build target(s) here
123 |
124 | #--------------- More (useful) targets! -------------------------------
125 | INDENT := indent
126 |
127 | # code-style : "wrapper" target over the following kernel code style targets
128 | code-style:
129 | make indent
130 | make checkpatch
131 |
132 | # indent- "beautifies" C code - to conform to the the Linux kernel
133 | # coding style guidelines.
134 | # Note! original source file(s) is overwritten, so we back it up.
135 | indent:
136 | @echo
137 | @echo "--- applying kernel code style indentation with indent ---"
138 | @echo
139 | mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
140 | ${INDENT} -linux --line-length95 *.[chsS]
141 | # add source files as required
142 |
143 | # Detailed check on the source code styling / etc
144 | checkpatch:
145 | make clean
146 | @echo
147 | @echo "--- kernel code style check with checkpatch.pl ---"
148 | @echo
149 | $(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
150 | # add source files as required
151 |
152 | #--- Static Analysis
153 | # sa : "wrapper" target over the following kernel static analyzer targets
154 | sa:
155 | make sa_sparse
156 | make sa_gcc
157 | make sa_flawfinder
158 | make sa_cppcheck
159 |
160 | # static analysis with sparse
161 | sa_sparse:
162 | make clean
163 | @echo
164 | @echo "--- static analysis with sparse ---"
165 | @echo
166 | # if you feel it's too much, use C=1 instead
167 | # NOTE: deliberately IGNORING warnings from kernel headers!
168 | make -Wsparse-all C=2 CHECK="/usr/bin/sparse --os=linux --arch=$(ARCH)" -C $(KDIR) M=$(PWD) modules 2>&1 |egrep -v "^\./include/.*\.h|^\./arch/.*\.h"
169 |
170 | # static analysis with gcc
171 | sa_gcc:
172 | make clean
173 | @echo
174 | @echo "--- static analysis with gcc ---"
175 | @echo
176 | make W=1 -C $(KDIR) M=$(PWD) modules
177 |
178 | # static analysis with flawfinder
179 | sa_flawfinder:
180 | make clean
181 | @echo
182 | @echo "--- static analysis with flawfinder ---"
183 | @echo
184 | flawfinder *.[ch]
185 |
186 | # static analysis with cppcheck
187 | sa_cppcheck:
188 | make clean
189 | @echo
190 | @echo "--- static analysis with cppcheck ---"
191 | @echo
192 | cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
193 |
194 | # Packaging; just tar.xz as of now
195 | PKG_NAME := ${FNAME_C}
196 | tarxz-pkg:
197 | rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
198 | make clean
199 | @echo
200 | @echo "--- packaging ---"
201 | @echo
202 | tar caf ../${PKG_NAME}.tar.xz *
203 | ls -l ../${PKG_NAME}.tar.xz
204 | @echo '=== package created: ../$(PKG_NAME).tar.xz ==='
205 | @echo 'Tip: when extracting, to extract into a dir of the same name as the tar file,'
206 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
207 |
208 | help:
209 | @echo '=== Makefile Help : additional targets available ==='
210 | @echo
211 | @echo 'TIP: type make to show all valid targets'
212 | @echo
213 |
214 | @echo '--- 'usual' kernel LKM targets ---'
215 | @echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
216 | @echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default here: /lib/modules/$(shell uname -r)/)'
217 | @echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
218 |
219 | @echo
220 | @echo '--- kernel code style targets ---'
221 | @echo 'code-style : "wrapper" target over the following kernel code style targets'
222 | @echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
223 | @echo ' checkpatch : run the kernel code style checker tool on source file(s)'
224 |
225 | @echo
226 | @echo '--- kernel static analyzer targets ---'
227 | @echo 'sa : "wrapper" target over the following kernel static analyzer targets'
228 | @echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
229 | @echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
230 | @echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
231 | @echo ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
232 | @echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
233 |
234 | @echo
235 | @echo '--- kernel dynamic analysis targets ---'
236 | @echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
237 | @echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
238 | @echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
239 |
240 | @echo
241 | @echo '--- misc targets ---'
242 | @echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
243 | @echo ' Tip: when extracting, to extract into a dir of the same name as the tar file,'
244 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
245 | @echo 'help : this help target'
246 |
--------------------------------------------------------------------------------
/ch3/ratelimit_test/ratelimit_test.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch5/ratelimit_test/ratelimit_test.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 5: Debug via Instrumentation - printk and friends
12 | ****************************************************************
13 | * Brief Description:
14 | * Quick test to see the behavior of the kernel printk when rate-limiting
15 | * is used.
16 | * Useful to limit / throttle down the same printk's being issued in bursts...
17 | *
18 | * For details, please refer the book, Ch 5.
19 | */
20 | #define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | MODULE_AUTHOR("");
28 | MODULE_DESCRIPTION("LKD book:ch5/ratelimit_test: print a burst to test rate-limiting");
29 | MODULE_LICENSE("Dual MIT/GPL");
30 | MODULE_VERSION("0.1");
31 |
32 | static int num_burst_prints = 7;
33 | module_param(num_burst_prints, int, 0644);
34 | MODULE_PARM_DESC(num_burst_prints,
35 | "Number of printk's to generate in a burst (defaults to 7).");
36 |
37 | static int __init ratelimit_test_init(void)
38 | {
39 | int i;
40 |
41 | pr_info("num_burst_prints=%d. Attempting to emit %d printk's in a burst:\n",
42 | num_burst_prints, num_burst_prints);
43 |
44 | for (i = 0; i < num_burst_prints; i++) {
45 | pr_info_ratelimited("[%d] ratelimited printk @ KERN_INFO [%d]\n", i,
46 | LOGLEVEL_INFO);
47 | mdelay(100); /* the delay helps magnify the rate-limiting effect, triggering the kernel's
48 | "'n' callbacks suppressed" message... */
49 | }
50 |
51 | return 0; /* success */
52 | }
53 |
54 | static void __exit ratelimit_test_exit(void)
55 | {
56 | pr_info_ratelimited("Goodbye, ratelimited printk @ log-level KERN_INFO [%d]\n",
57 | LOGLEVEL_INFO);
58 | }
59 |
60 | module_init(ratelimit_test_init);
61 | module_exit(ratelimit_test_exit);
62 |
--------------------------------------------------------------------------------
/ch4/kprobes/1_kprobe/1_kprobe.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch4/kprobes/1_kprobe/1_kprobe.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 4: Debug via Instrumentation - Kprobes
12 | ****************************************************************
13 | * Brief Description:
14 | * Traditional and manual approach, simplest case: attaching a kprobe,
15 | * hard-coding it (to the open system call).
16 | * NOTE- you might find that this particular call - do_sys_open() - isn't
17 | * perhaps being issued often... leading to your thinking that the kprobe
18 | * isn't working. It is... so, trying with other functions can be helpful
19 | * at times...
20 | *
21 | * For details, please refer the book, Ch 4.
22 | */
23 | #define pr_fmt(fmt) "%s:%s(): " fmt, KBUILD_MODNAME, __func__
24 |
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include "../../../convenient.h"
36 |
37 | MODULE_AUTHOR("");
38 | MODULE_DESCRIPTION("LKD book:ch4/kprobes/1_kprobe: simple Kprobes 1st demo module");
39 | MODULE_LICENSE("Dual MIT/GPL");
40 | MODULE_VERSION("0.1");
41 |
42 | static spinlock_t lock;
43 | static struct kprobe kpb;
44 | static u64 tm_start, tm_end;
45 |
46 | /*
47 | * This probe runs just prior to the function "do_sys_open()" is invoked.
48 | */
49 | static int handler_pre(struct kprobe *p, struct pt_regs *regs)
50 | {
51 | PRINT_CTX(); // uses pr_debug()
52 |
53 | spin_lock(&lock);
54 | tm_start = ktime_get_real_ns();
55 | spin_unlock(&lock);
56 |
57 | return 0;
58 | }
59 |
60 | /*
61 | * This probe runs immediately after the function "do_sys_open()" completes.
62 | */
63 | static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
64 | {
65 | spin_lock(&lock);
66 | tm_end = ktime_get_real_ns();
67 | PRINT_CTX(); // uses pr_debug()
68 | SHOW_DELTA(tm_end, tm_start);
69 | spin_unlock(&lock);
70 | pr_debug("\n"); // silly- just to see the output clearly via dmesg/journalctl
71 | }
72 |
73 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
74 | /*
75 | * fault_handler: this is called if an exception is generated for any
76 | * instruction within the pre- or post-handler, or when Kprobes
77 | * single-steps the probed instruction.
78 | */
79 | static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
80 | {
81 | pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr);
82 | /* Return 0 because we don't handle the fault. */
83 | return 0;
84 | }
85 | NOKPROBE_SYMBOL(handler_fault);
86 | #endif
87 |
88 | static int __init kprobe_lkm_init(void)
89 | {
90 | /* Register the kprobe handler */
91 | kpb.pre_handler = handler_pre;
92 | kpb.post_handler = handler_post;
93 | // 5.14: commit ec6aba3; 'kprobes: Remove kprobe::fault_handler'
94 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
95 | kpb.fault_handler = handler_fault;
96 | #endif
97 | #if 1
98 | kpb.symbol_name = "do_sys_open";
99 | #else
100 | kpb.symbol_name = "kmem_cache_alloc";
101 | #endif
102 | if (register_kprobe(&kpb)) {
103 | pr_alert("register_kprobe on do_sys_open() failed!\n");
104 | return -EINVAL;
105 | }
106 | pr_info("registering kernel probe @ 'do_sys_open()'\n");
107 | spin_lock_init(&lock);
108 |
109 | return 0; /* success */
110 | }
111 |
112 | static void __exit kprobe_lkm_exit(void)
113 | {
114 | unregister_kprobe(&kpb);
115 | pr_info("bye, unregistering kernel probe @ 'do_sys_open()'\n");
116 | }
117 |
118 | module_init(kprobe_lkm_init);
119 | module_exit(kprobe_lkm_exit);
120 |
--------------------------------------------------------------------------------
/ch4/kprobes/1_kprobe/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile
2 | # ***************************************************************
3 | # This program is part of the source code released for the book
4 | # "Linux Kernel Debugging"
5 | # (c) Author: Kaiwan N Billimoria
6 | # Publisher: Packt
7 | # GitHub repository:
8 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
9 | #
10 | # ***************************************************************
11 | # Brief Description:
12 | # A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
13 | # the 'usual' targets (the build, install and clean), we incorporate targets to
14 | # do useful (and indeed required) stuff like:
15 | # - adhering to kernel coding style (indent+checkpatch)
16 | # - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
17 | # - two _dummy_ dynamic analysis targets (KASAN, LOCKDEP); just to remind you!
18 | # - a packaging (.tar.xz) target and
19 | # - a help target.
20 | #
21 | # To get started, just type:
22 | # make help
23 | #
24 | # For details on this so-called 'better' Makefile, please refer my earlier book
25 | # 'Linux Kernel Programming', Packt, Mar 2021, Ch 5 section 'A "better" Makefile
26 | # template for your kernel modules'.
27 |
28 | #------------------------------------------------------------------
29 | # Set FNAME_C to the kernel module name source filename (without .c)
30 | # This enables you to use this Makefile as a template; just update this variable!
31 | # As well, the MYDEBUG variable (see it below) can be set to 'y' or 'n' (no being
32 | # the default)
33 | FNAME_C := 1_kprobe
34 | #------------------------------------------------------------------
35 |
36 | # To support cross-compiling for kernel modules:
37 | # For architecture (cpu) 'arch', invoke make as:
38 | # make ARCH= CROSS_COMPILE=
39 | # The KDIR var is set to a sample path below; you're expected to update it on
40 | # your box to the appropriate path to the kernel src tree for that arch.
41 | ifeq ($(ARCH),arm)
42 | # *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
43 | KDIR ?= ~/rpi_work/kernel_rpi/linux
44 | else ifeq ($(ARCH),arm64)
45 | # *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
46 | # tree on your box
47 | KDIR ?= ~/kernel/linux-5.4
48 | else ifeq ($(ARCH),powerpc)
49 | # *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
50 | KDIR ?= ~/kernel/linux-5.0
51 | else
52 | # 'KDIR' is the Linux 'kernel headers' package on your host system; this is
53 | # usually an x86_64, but could be anything, really (f.e. building directly
54 | # on a Raspberry Pi implies that it's the host)
55 | KDIR ?= /lib/modules/$(shell uname -r)/build
56 | endif
57 |
58 | # Compiler
59 | CC := $(CROSS_COMPILE)gcc
60 | #CC := $(CROSS_COMPILE)gcc-10
61 | #CC := clang
62 |
63 | PWD := $(shell pwd)
64 | obj-m += ${FNAME_C}.o
65 |
66 | #--- Debug or production mode?
67 | # Set the MYDEBUG variable accordingly to y/n resp.
68 | # (Actually, debug info is always going to be generated when you build the
69 | # module on a debug kernel, where CONFIG_DEBUG_INFO is defined, making this
70 | # setting of the ccflags-y (or EXTRA_CFLAGS) variable mostly redundant (besides
71 | # the -DDEBUG).
72 | # This simply helps us influence the build on a production kernel, forcing
73 | # generation of debug symbols, if so required. Also, realize that the DEBUG
74 | # macro is turned on by many CONFIG_*DEBUG* options; hence, we use a different
75 | # macro var name, MYDEBUG).
76 | MYDEBUG := n
77 | ifeq (${MYDEBUG}, y)
78 |
79 | # https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
80 | # EXTRA_CFLAGS deprecated; use ccflags-y
81 | ccflags-y += -DDEBUG -g -ggdb -gdwarf-4 -Wall -fno-omit-frame-pointer -fvar-tracking-assignments
82 | else
83 | INSTALL_MOD_STRIP := 1
84 | #ccflags-y += --strip-debug
85 | endif
86 | # We always keep the dynamic debug facility enabled; this allows us to turn
87 | # dynamically turn on/off debug printk's later... To disable it simply comment
88 | # out the following line
89 | ccflags-y += -DDYNAMIC_DEBUG_MODULE
90 |
91 | KMODDIR ?= /lib/modules/$(shell uname -r)
92 | STRIP := ${CROSS_COMPILE}strip
93 |
94 | # gcc-10 issue:
95 | #ccflags-y += $(call cc-option,--allow-store-data-races)
96 |
97 | all:
98 | @echo
99 | @echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y=${ccflags-y} ---'
100 | @${CC} --version|head -n1
101 | @echo
102 | make -C $(KDIR) M=$(PWD) modules
103 | $(shell [ "${MYDEBUG}" != "y" ] && ${STRIP} --strip-debug ./${FNAME_C}.ko)
104 | install:
105 | @echo
106 | @echo "--- installing ---"
107 | @echo " [First, invoking the 'make' ]"
108 | make
109 | @echo
110 | @echo " [Now for the 'sudo make install' ]"
111 | sudo make -C $(KDIR) M=$(PWD) modules_install
112 | @echo " [If !debug, stripping debug info from ${KMODDIR}/extra/${FNAME_C}.ko]"
113 | $(shell if [ "${MYDEBUG}" != "y" ]; then sudo ${STRIP} --strip-debug ${KMODDIR}/extra/${FNAME_C}.ko; fi)
114 | clean:
115 | @echo
116 | @echo "--- cleaning ---"
117 | @echo
118 | make -C $(KDIR) M=$(PWD) clean
119 | # from 'indent'
120 | rm -f *~
121 |
122 | # Any usermode programs to build? Insert the build target(s) here
123 |
124 | #--------------- More (useful) targets! -------------------------------
125 | INDENT := indent
126 |
127 | # code-style : "wrapper" target over the following kernel code style targets
128 | code-style:
129 | make indent
130 | make checkpatch
131 |
132 | # indent- "beautifies" C code - to conform to the the Linux kernel
133 | # coding style guidelines.
134 | # Note! original source file(s) is overwritten, so we back it up.
135 | indent:
136 | @echo
137 | @echo "--- applying kernel code style indentation with indent ---"
138 | @echo
139 | mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
140 | ${INDENT} -linux --line-length95 *.[chsS]
141 | # add source files as required
142 |
143 | # Detailed check on the source code styling / etc
144 | checkpatch:
145 | make clean
146 | @echo
147 | @echo "--- kernel code style check with checkpatch.pl ---"
148 | @echo
149 | $(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
150 | # add source files as required
151 |
152 | #--- Static Analysis
153 | # sa : "wrapper" target over the following kernel static analyzer targets
154 | sa:
155 | make sa_sparse
156 | make sa_gcc
157 | make sa_flawfinder
158 | make sa_cppcheck
159 |
160 | # static analysis with sparse
161 | sa_sparse:
162 | make clean
163 | @echo
164 | @echo "--- static analysis with sparse ---"
165 | @echo
166 | # if you feel it's too much, use C=1 instead
167 | # NOTE: deliberately IGNORING warnings from kernel headers!
168 | make -Wsparse-all C=2 CHECK="/usr/bin/sparse --os=linux --arch=$(ARCH)" -C $(KDIR) M=$(PWD) modules 2>&1 |egrep -v "^\./include/.*\.h|^\./arch/.*\.h"
169 |
170 | # static analysis with gcc
171 | sa_gcc:
172 | make clean
173 | @echo
174 | @echo "--- static analysis with gcc ---"
175 | @echo
176 | make W=1 -C $(KDIR) M=$(PWD) modules
177 |
178 | # static analysis with flawfinder
179 | sa_flawfinder:
180 | make clean
181 | @echo
182 | @echo "--- static analysis with flawfinder ---"
183 | @echo
184 | flawfinder *.[ch]
185 |
186 | # static analysis with cppcheck
187 | sa_cppcheck:
188 | make clean
189 | @echo
190 | @echo "--- static analysis with cppcheck ---"
191 | @echo
192 | cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
193 |
194 | # Packaging; just tar.xz as of now
195 | PKG_NAME := ${FNAME_C}
196 | tarxz-pkg:
197 | rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
198 | make clean
199 | @echo
200 | @echo "--- packaging ---"
201 | @echo
202 | tar caf ../${PKG_NAME}.tar.xz *
203 | ls -l ../${PKG_NAME}.tar.xz
204 | @echo '=== package created: ../$(PKG_NAME).tar.xz ==='
205 | @echo 'Tip: when extracting, to extract into a dir of the same name as the tar file,'
206 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
207 |
208 | help:
209 | @echo '=== Makefile Help : additional targets available ==='
210 | @echo
211 | @echo 'TIP: type make to show all valid targets'
212 | @echo
213 |
214 | @echo '--- 'usual' kernel LKM targets ---'
215 | @echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
216 | @echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default here: /lib/modules/$(shell uname -r)/)'
217 | @echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
218 |
219 | @echo
220 | @echo '--- kernel code style targets ---'
221 | @echo 'code-style : "wrapper" target over the following kernel code style targets'
222 | @echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
223 | @echo ' checkpatch : run the kernel code style checker tool on source file(s)'
224 |
225 | @echo
226 | @echo '--- kernel static analyzer targets ---'
227 | @echo 'sa : "wrapper" target over the following kernel static analyzer targets'
228 | @echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
229 | @echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
230 | @echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
231 | @echo ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
232 | @echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
233 |
234 | @echo
235 | @echo '--- kernel dynamic analysis targets ---'
236 | @echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
237 | @echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
238 | @echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
239 |
240 | @echo
241 | @echo '--- misc targets ---'
242 | @echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
243 | @echo ' Tip: when extracting, to extract into a dir of the same name as the tar file,'
244 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
245 | @echo 'help : this help target'
246 |
--------------------------------------------------------------------------------
/ch4/kprobes/1_kprobe/run:
--------------------------------------------------------------------------------
1 | KMOD=1_kprobe
2 | echo "sudo dmesg -C && make && ./test.sh && sleep 5 && sudo rmmod ${KMOD} 2>/dev/null ; sudo dmesg"
3 | sudo dmesg -C && make && ./test.sh && sleep 5 && sudo rmmod ${KMOD} 2>/dev/null ; sudo dmesg
4 |
--------------------------------------------------------------------------------
/ch4/kprobes/1_kprobe/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # test.sh
3 | KMOD=1_kprobe
4 | VERBOSE=1
5 |
6 | DYNDBG_CTRL=/sys/kernel/debug/dynamic_debug/control
7 | if [ ! -f ${DYNDBG_CTRL} ]; then
8 | [ -f /proc/dynamic_debug/control ] && DYNDBG_CTRL=/proc/dynamic_debug/control \
9 | || DYNDBG_CTRL=""
10 | fi
11 | [ -z "${DYNDBG_CTRL}" ] && {
12 | echo "No dynamic debug control file available..."
13 | exit 1
14 | }
15 |
16 | echo "Module ${KMOD}: function to probe: do_sys_open()
17 | "
18 | if [ ! -f ./${KMOD}.ko ]; then
19 | echo "Building ${KMOD} ..."
20 | make || exit 1
21 | fi
22 | sudo rmmod ${KMOD} 2>/dev/null # rm any stale instance
23 | sudo insmod ./${KMOD}.ko || exit 1
24 |
25 | echo "-- Module ${KMOD} now inserted, turn on any dynamic debug prints now --"
26 | sudo bash -c "grep \"${KMOD} .* =_ \" ${DYNDBG_CTRL}" && echo "Wrt module ${KMOD}, one or more dynamic debug prints are Off" || \
27 | echo "Wrt module ${KMOD}, one or more dynamic debug prints are On"
28 | # turn On debug prints
29 | sudo bash -c "echo -n \"module ${KMOD} +p\" > ${DYNDBG_CTRL}"
30 | sudo bash -c "grep \"${KMOD}\" ${DYNDBG_CTRL}"
31 | echo "-- All set, look up kernel log with, f.e., journalctl -k -f --"
32 | exit 0
33 |
--------------------------------------------------------------------------------
/ch4/kprobes/2_kprobe/2_kprobe.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch4/kprobes/2_kprobe/2_kprobe.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 4: Debug via Instrumentation - Kprobes
12 | ****************************************************************
13 | * Brief Description:
14 | * Traditional and manual approach: attaching a kprobe, slightly better,
15 | * soft-coding it via a module parameter (to the open system call);
16 | * via a module parameter.
17 | *
18 | * For details, please refer the book, Ch 4.
19 | */
20 | #define pr_fmt(fmt) "%s:%s(): " fmt, KBUILD_MODNAME, __func__
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include "../../../convenient.h"
33 |
34 | MODULE_AUTHOR("");
35 | MODULE_DESCRIPTION("LKD book:ch4/2_kprobes/2_kprobe: simple Kprobes demo module with modparam");
36 | MODULE_LICENSE("Dual MIT/GPL");
37 | MODULE_VERSION("0.1");
38 |
39 | #undef SKIP_IF_NOT_VI
40 | //#define SKIP_IF_NOT_VI
41 |
42 | static spinlock_t lock;
43 | static struct kprobe kpb;
44 | static u64 tm_start, tm_end;
45 |
46 | #define MAX_FUNCNAME_LEN 64
47 | static char kprobe_func[MAX_FUNCNAME_LEN];
48 | module_param_string(kprobe_func, kprobe_func, sizeof(kprobe_func), 0);
49 | MODULE_PARM_DESC(kprobe_func, "function name to attach a kprobe to");
50 |
51 | static int verbose;
52 | module_param(verbose, int, 0644);
53 | MODULE_PARM_DESC(verbose, "Set to 1 to get verbose printk's (defaults to 0).");
54 |
55 | /*
56 | * This probe runs just prior to the function "kprobe_func()" is invoked.
57 | * Here, we're assuming you've setup a kprobe into the do_sys_open():
58 | * long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
59 | */
60 | static int handler_pre(struct kprobe *p, struct pt_regs *regs)
61 | {
62 | #ifdef SKIP_IF_NOT_VI
63 | /* For the purpose of this demo, we only log information when the process
64 | * context is 'vi'
65 | */
66 | if (strncmp(current->comm, "vi", 2))
67 | return 0;
68 | #endif
69 |
70 | PRINT_CTX();
71 | spin_lock(&lock);
72 | tm_start = ktime_get_real_ns();
73 | spin_unlock(&lock);
74 |
75 | return 0;
76 | }
77 |
78 | /*
79 | * This probe runs immediately after the function "kprobe_func()" completes.
80 | */
81 | static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
82 | {
83 | #ifdef SKIP_IF_NOT_VI
84 | if (strncmp(current->comm, "vi", 2))
85 | return;
86 | #endif
87 |
88 | spin_lock(&lock);
89 | tm_end = ktime_get_real_ns();
90 |
91 | if (verbose)
92 | PRINT_CTX();
93 |
94 | SHOW_DELTA(tm_end, tm_start);
95 | spin_unlock(&lock);
96 | }
97 |
98 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
99 | /*
100 | * fault_handler: this is called if an exception is generated for any
101 | * instruction within the pre- or post-handler, or when Kprobes
102 | * single-steps the probed instruction.
103 | */
104 | static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
105 | {
106 | pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr);
107 | /* Return 0 because we don't handle the fault. */
108 | return 0;
109 | }
110 | NOKPROBE_SYMBOL(handler_fault);
111 | #endif
112 |
113 | static int __init kprobe_lkm_init(void)
114 | {
115 | /* Verify that the function to kprobe has been passed as a parameter to
116 | * this module
117 | */
118 | if (kprobe_func[0] == '\0') {
119 | pr_warn("expect a valid kprobe_func= module parameter");
120 | return -EINVAL;
121 | }
122 | /********* Possible SECURITY concern:
123 | * We just assume the pointer passed is valid and okay, and not in the
124 | * kprobes 'blacklist'.
125 | * Minimally, ensure that the passed function is NOT marked with any of:
126 | * __kprobes or nokprobe_inline annotation nor marked via the NOKPROBE_SYMBOL
127 | * macro
128 | */
129 | /* Register the kprobe handler */
130 | kpb.pre_handler = handler_pre;
131 | kpb.post_handler = handler_post;
132 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
133 | kpb.fault_handler = handler_fault;
134 | #endif
135 | kpb.symbol_name = kprobe_func;
136 | if (register_kprobe(&kpb)) {
137 | pr_alert("register_kprobe failed!\n\
138 | Check: is function '%s' invalid, static, inline; or blacklisted: attribute-marked '__kprobes'\n\
139 | or nokprobe_inline, or is marked with the NOKPROBE_SYMBOL macro?\n", kprobe_func);
140 | return -EINVAL;
141 | }
142 | pr_info("registering kernel probe @ '%s'\n", kprobe_func);
143 | #ifdef SKIP_IF_NOT_VI
144 | pr_info("NOTE: Skipping if not vi ...\n");
145 | #endif
146 | spin_lock_init(&lock);
147 |
148 | return 0; /* success */
149 | }
150 |
151 | static void __exit kprobe_lkm_exit(void)
152 | {
153 | unregister_kprobe(&kpb);
154 | pr_info("bye, unregistering kernel probe @ '%s'\n", kprobe_func);
155 | }
156 |
157 | module_init(kprobe_lkm_init);
158 | module_exit(kprobe_lkm_exit);
159 |
--------------------------------------------------------------------------------
/ch4/kprobes/2_kprobe/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile
2 | # ***************************************************************
3 | # This program is part of the source code released for the book
4 | # "Linux Kernel Debugging"
5 | # (c) Author: Kaiwan N Billimoria
6 | # Publisher: Packt
7 | # GitHub repository:
8 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
9 | #
10 | # ***************************************************************
11 | # Brief Description:
12 | # A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
13 | # the 'usual' targets (the build, install and clean), we incorporate targets to
14 | # do useful (and indeed required) stuff like:
15 | # - adhering to kernel coding style (indent+checkpatch)
16 | # - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
17 | # - two _dummy_ dynamic analysis targets (KASAN, LOCKDEP); just to remind you!
18 | # - a packaging (.tar.xz) target and
19 | # - a help target.
20 | #
21 | # To get started, just type:
22 | # make help
23 | #
24 | # For details on this so-called 'better' Makefile, please refer my earlier book
25 | # 'Linux Kernel Programming', Packt, Mar 2021, Ch 5 section 'A "better" Makefile
26 | # template for your kernel modules'.
27 |
28 | #------------------------------------------------------------------
29 | # Set FNAME_C to the kernel module name source filename (without .c)
30 | # This enables you to use this Makefile as a template; just update this variable!
31 | # As well, the MYDEBUG variable (see it below) can be set to 'y' or 'n' (no being
32 | # the default)
33 | FNAME_C := 2_kprobe
34 | #------------------------------------------------------------------
35 |
36 | # To support cross-compiling for kernel modules:
37 | # For architecture (cpu) 'arch', invoke make as:
38 | # make ARCH= CROSS_COMPILE=
39 | # The KDIR var is set to a sample path below; you're expected to update it on
40 | # your box to the appropriate path to the kernel src tree for that arch.
41 | ifeq ($(ARCH),arm)
42 | # *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
43 | KDIR ?= ~/rpi_work/kernel_rpi/linux
44 | else ifeq ($(ARCH),arm64)
45 | # *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
46 | # tree on your box
47 | KDIR ?= ~/kernel/linux-5.4
48 | else ifeq ($(ARCH),powerpc)
49 | # *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
50 | KDIR ?= ~/kernel/linux-5.0
51 | else
52 | # 'KDIR' is the Linux 'kernel headers' package on your host system; this is
53 | # usually an x86_64, but could be anything, really (f.e. building directly
54 | # on a Raspberry Pi implies that it's the host)
55 | KDIR ?= /lib/modules/$(shell uname -r)/build
56 | endif
57 |
58 | # Compiler
59 | CC := $(CROSS_COMPILE)gcc
60 | #CC := $(CROSS_COMPILE)gcc-10
61 | #CC := clang
62 |
63 | PWD := $(shell pwd)
64 | obj-m += ${FNAME_C}.o
65 |
66 | #--- Debug or production mode?
67 | # Set the MYDEBUG variable accordingly to y/n resp.
68 | # (Actually, debug info is always going to be generated when you build the
69 | # module on a debug kernel, where CONFIG_DEBUG_INFO is defined, making this
70 | # setting of the ccflags-y (or EXTRA_CFLAGS) variable mostly redundant (besides
71 | # the -DDEBUG).
72 | # This simply helps us influence the build on a production kernel, forcing
73 | # generation of debug symbols, if so required. Also, realize that the DEBUG
74 | # macro is turned on by many CONFIG_*DEBUG* options; hence, we use a different
75 | # macro var name, MYDEBUG).
76 | MYDEBUG := n
77 | ifeq (${MYDEBUG}, y)
78 |
79 | # https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
80 | # EXTRA_CFLAGS deprecated; use ccflags-y
81 | ccflags-y += -DDEBUG -g -ggdb -gdwarf-4 -Wall -fno-omit-frame-pointer -fvar-tracking-assignments
82 | else
83 | INSTALL_MOD_STRIP := 1
84 | #ccflags-y += --strip-debug
85 | endif
86 | # We always keep the dynamic debug facility enabled; this allows us to turn
87 | # dynamically turn on/off debug printk's later... To disable it simply comment
88 | # out the following line
89 | ccflags-y += -DDYNAMIC_DEBUG_MODULE
90 |
91 | KMODDIR ?= /lib/modules/$(shell uname -r)
92 | STRIP := ${CROSS_COMPILE}strip
93 |
94 | # gcc-10 issue:
95 | #ccflags-y += $(call cc-option,--allow-store-data-races)
96 |
97 | all:
98 | @echo
99 | @echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y=${ccflags-y} ---'
100 | @${CC} --version|head -n1
101 | @echo
102 | make -C $(KDIR) M=$(PWD) modules
103 | $(shell [ "${MYDEBUG}" != "y" ] && ${STRIP} --strip-debug ./${FNAME_C}.ko)
104 | install:
105 | @echo
106 | @echo "--- installing ---"
107 | @echo " [First, invoking the 'make' ]"
108 | make
109 | @echo
110 | @echo " [Now for the 'sudo make install' ]"
111 | sudo make -C $(KDIR) M=$(PWD) modules_install
112 | @echo " [If !debug, stripping debug info from ${KMODDIR}/extra/${FNAME_C}.ko]"
113 | $(shell if [ "${MYDEBUG}" != "y" ]; then sudo ${STRIP} --strip-debug ${KMODDIR}/extra/${FNAME_C}.ko; fi)
114 | clean:
115 | @echo
116 | @echo "--- cleaning ---"
117 | @echo
118 | make -C $(KDIR) M=$(PWD) clean
119 | # from 'indent'
120 | rm -f *~
121 |
122 | # Any usermode programs to build? Insert the build target(s) here
123 |
124 | #--------------- More (useful) targets! -------------------------------
125 | INDENT := indent
126 |
127 | # code-style : "wrapper" target over the following kernel code style targets
128 | code-style:
129 | make indent
130 | make checkpatch
131 |
132 | # indent- "beautifies" C code - to conform to the the Linux kernel
133 | # coding style guidelines.
134 | # Note! original source file(s) is overwritten, so we back it up.
135 | indent:
136 | @echo
137 | @echo "--- applying kernel code style indentation with indent ---"
138 | @echo
139 | mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
140 | ${INDENT} -linux --line-length95 *.[chsS]
141 | # add source files as required
142 |
143 | # Detailed check on the source code styling / etc
144 | checkpatch:
145 | make clean
146 | @echo
147 | @echo "--- kernel code style check with checkpatch.pl ---"
148 | @echo
149 | $(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
150 | # add source files as required
151 |
152 | #--- Static Analysis
153 | # sa : "wrapper" target over the following kernel static analyzer targets
154 | sa:
155 | make sa_sparse
156 | make sa_gcc
157 | make sa_flawfinder
158 | make sa_cppcheck
159 |
160 | # static analysis with sparse
161 | sa_sparse:
162 | make clean
163 | @echo
164 | @echo "--- static analysis with sparse ---"
165 | @echo
166 | # if you feel it's too much, use C=1 instead
167 | # NOTE: deliberately IGNORING warnings from kernel headers!
168 | make -Wsparse-all C=2 CHECK="/usr/bin/sparse --os=linux --arch=$(ARCH)" -C $(KDIR) M=$(PWD) modules 2>&1 |egrep -v "^\./include/.*\.h|^\./arch/.*\.h"
169 |
170 | # static analysis with gcc
171 | sa_gcc:
172 | make clean
173 | @echo
174 | @echo "--- static analysis with gcc ---"
175 | @echo
176 | make W=1 -C $(KDIR) M=$(PWD) modules
177 |
178 | # static analysis with flawfinder
179 | sa_flawfinder:
180 | make clean
181 | @echo
182 | @echo "--- static analysis with flawfinder ---"
183 | @echo
184 | flawfinder *.[ch]
185 |
186 | # static analysis with cppcheck
187 | sa_cppcheck:
188 | make clean
189 | @echo
190 | @echo "--- static analysis with cppcheck ---"
191 | @echo
192 | cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
193 |
194 | # Packaging; just tar.xz as of now
195 | PKG_NAME := ${FNAME_C}
196 | tarxz-pkg:
197 | rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
198 | make clean
199 | @echo
200 | @echo "--- packaging ---"
201 | @echo
202 | tar caf ../${PKG_NAME}.tar.xz *
203 | ls -l ../${PKG_NAME}.tar.xz
204 | @echo '=== package created: ../$(PKG_NAME).tar.xz ==='
205 | @echo 'Tip: when extracting, to extract into a dir of the same name as the tar file,'
206 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
207 |
208 | help:
209 | @echo '=== Makefile Help : additional targets available ==='
210 | @echo
211 | @echo 'TIP: type make to show all valid targets'
212 | @echo
213 |
214 | @echo '--- 'usual' kernel LKM targets ---'
215 | @echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
216 | @echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default here: /lib/modules/$(shell uname -r)/)'
217 | @echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
218 |
219 | @echo
220 | @echo '--- kernel code style targets ---'
221 | @echo 'code-style : "wrapper" target over the following kernel code style targets'
222 | @echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
223 | @echo ' checkpatch : run the kernel code style checker tool on source file(s)'
224 |
225 | @echo
226 | @echo '--- kernel static analyzer targets ---'
227 | @echo 'sa : "wrapper" target over the following kernel static analyzer targets'
228 | @echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
229 | @echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
230 | @echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
231 | @echo ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
232 | @echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
233 |
234 | @echo
235 | @echo '--- kernel dynamic analysis targets ---'
236 | @echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
237 | @echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
238 | @echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
239 |
240 | @echo
241 | @echo '--- misc targets ---'
242 | @echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
243 | @echo ' Tip: when extracting, to extract into a dir of the same name as the tar file,'
244 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
245 | @echo 'help : this help target'
246 |
--------------------------------------------------------------------------------
/ch4/kprobes/2_kprobe/run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # ch4/kprobes/2_kprobe/run
3 | KMOD=2_kprobe
4 |
5 | # Try making the below condition true and then running...
6 | [ 0 -eq 1 ] && {
7 | sudo dmesg -C && make && ./test.sh && sleep .1
8 | vi /etc/passwd # just to get vi running..; quit out of it immd
9 | sudo rmmod ${KMOD} 2>/dev/null && sudo dmesg
10 | } || {
11 | echo "sudo dmesg -C && make && ./test.sh && sleep 1 && sudo rmmod ${KMOD} 2>/dev/null && sudo dmesg"
12 | sudo dmesg -C && make && ./test.sh && sleep 1 && sudo rmmod ${KMOD} 2>/dev/null && sudo dmesg
13 | }
14 |
--------------------------------------------------------------------------------
/ch4/kprobes/2_kprobe/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # test.sh
3 | KMOD=2_kprobe
4 | # You can change the function to kprobe here!
5 | FUNC_TO_KPROBE=kmem_cache_alloc #do_sys_open # vfs_write
6 | VERBOSE=1
7 |
8 | DYNDBG_CTRL=/sys/kernel/debug/dynamic_debug/control
9 | if [ ! -f ${DYNDBG_CTRL} ]; then
10 | [ -f /proc/dynamic_debug/control ] && DYNDBG_CTRL=/proc/dynamic_debug/control \
11 | || DYNDBG_CTRL=""
12 | fi
13 |
14 | echo "Module ${KMOD}: function to probe: ${FUNC_TO_KPROBE}
15 | "
16 | if [ ! -f ./${KMOD}.ko ]; then
17 | echo "Building ${KMOD} ..."
18 | make || exit 1
19 | fi
20 | sudo rmmod ${KMOD} 2>/dev/null # rm any stale instance
21 | # Ideally, first check that the function to kprobe isn't blacklisted; we skip
22 | # this here, doing this in the more sophisticated ch4/kprobes/4_kprobe_helper/kp_load.sh script
23 | sudo insmod ./${KMOD}.ko kprobe_func=${FUNC_TO_KPROBE} verbose=${VERBOSE} || exit 1
24 |
25 | [ -z "${DYNDBG_CTRL}" ] && {
26 | echo "No dynamic debug control file available..."
27 | exit
28 | }
29 |
30 | echo "-- Module ${KMOD} now inserted, turn on dynamic debug prints now --"
31 | sudo bash -c "grep \"${KMOD} .* =_ \" ${DYNDBG_CTRL}" && echo "Wrt module ${KMOD}, one or more dynamic debug prints are Off" || \
32 | echo "Wrt module ${KMOD}, one or more dynamic debug prints are On"
33 | # turn On debug prints
34 | sudo bash -c "echo -n \"module ${KMOD} +p\" > ${DYNDBG_CTRL}"
35 | sudo bash -c "grep \"${KMOD}\" ${DYNDBG_CTRL}"
36 | echo "-- All set, look up kernel log with, f.e., journalctl -k -f --"
37 |
--------------------------------------------------------------------------------
/ch4/kprobes/3_kprobe/3_kprobe.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch4/kprobes/3_kprobe/3_kprobe.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 4: Debug via Instrumentation - Kprobes
12 | ****************************************************************
13 | * Brief Description:
14 | * Traditional and manual approach: attaching a kprobe via a module parameter
15 | * (to the open system call), plus retrieving the pathname to the file being
16 | * opened (useful!).
17 | * To gain access to the second parameter (holding the pointer to the file
18 | * being opened), we use our knowledge of the relevant processor ABI.
19 | *
20 | * For details, please refer the book, Ch 4.
21 | */
22 | #define pr_fmt(fmt) "%s:%s(): " fmt, KBUILD_MODNAME, __func__
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include "../../../convenient.h"
35 |
36 | MODULE_AUTHOR("");
37 | MODULE_DESCRIPTION("LKD book:ch4/kprobes/3_kprobe: simple Kprobes demo module with fname displayed");
38 | MODULE_LICENSE("Dual MIT/GPL");
39 | MODULE_VERSION("0.1");
40 |
41 | static spinlock_t lock;
42 | static struct kprobe kpb;
43 | static u64 tm_start, tm_end;
44 | static char *fname;
45 |
46 | #define MAX_FUNCNAME_LEN 64
47 | static char kprobe_func[MAX_FUNCNAME_LEN];
48 | module_param_string(kprobe_func, kprobe_func, sizeof(kprobe_func), 0);
49 | MODULE_PARM_DESC(kprobe_func, "function name to attach a kprobe to");
50 |
51 | static int verbose;
52 | module_param(verbose, int, 0644);
53 | MODULE_PARM_DESC(verbose, "Set to 1 to get verbose printk's (defaults to 0).");
54 |
55 | static int skip_if_not_vi = 1;
56 | module_param(skip_if_not_vi, int, 0644);
57 | MODULE_PARM_DESC(skip_if_not_vi, "Set to 1 to ONLY see printk's when vi runs and opens files (default=1).");
58 |
59 | /*
60 | * This probe runs just prior to the function "kprobe_func()" is invoked.
61 | * IMP: Here, we're assuming you've setup a kprobe into the do_sys_open():
62 | * long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
63 | * The second parameter is of interest; we retrieve it in an arch-specific way
64 | * (by referring the ABI for that processor, and the struct pt_regs)
65 | */
66 | static int handler_pre(struct kprobe *p, struct pt_regs *regs)
67 | {
68 | char *param_fname_reg;
69 |
70 | if (skip_if_not_vi) {
71 | /* For the purpose of this demo, we only log information when the process
72 | * context is 'vi'
73 | */
74 | if (strncmp(current->comm, "vi", 2))
75 | return 0;
76 | }
77 |
78 | #ifdef CONFIG_X86
79 | param_fname_reg = (char __user *)regs->si;
80 | #endif
81 | #ifdef CONFIG_ARM
82 | /* ARM-32 ABI:
83 | * First four parameters to a function are in the foll GPRs:
84 | * r0, r1, r2, r3
85 | * See the kernel's pt_regs structure - rendition of the CPU registers here:
86 | * https://elixir.bootlin.com/linux/v5.10.60/source/arch/arm/include/uapi/asm/ptrace.h#L15
87 | */
88 | param_fname_reg = (char __user *)regs->ARM_r1;
89 | #endif
90 | #ifdef CONFIG_ARM64
91 | /* AArch64 ABI:
92 | * First eight parameters to a function (and return val) are in the foll GPRs:
93 | * x0 to x7 (64-bit GPRs)
94 | * See the kernel's pt_regs structure - rendition of the CPU registers here:
95 | * https://elixir.bootlin.com/linux/v5.10.60/source/arch/arm64/include/asm/ptrace.h#L15
96 | */
97 | param_fname_reg = (char __user *)regs->regs[1];
98 | #endif
99 |
100 | PRINT_CTX();
101 | /*
102 | * We want the filename; to get it, we *must* copy it in from it's userspace
103 | * buffer, the pointer to which is in an arch-specific register.
104 | * Using strncpy_from_user() here is considered a bug! as we're in an atomic
105 | * context in this kprobe pre-handler...
106 | * [ ... ]
107 | * [ 2552.898142] BUG: sleeping function called from invalid context at lib/strncpy_from_user.c:117
108 | * [ 2552.904085] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 390, name: systemd-journal
109 | * [ ... ]
110 | * [ 2542.112886] Call Trace:
111 | * [ 2542.112892] dump_stack+0xbd/0xfa
112 | * [ 2542.112897] ___might_sleep.cold+0x63/0x74
113 | * [ 2542.112902] __might_sleep+0x73/0xe0
114 | * [ 2542.112908] __might_fault+0x52/0xd0
115 | * [ 2542.112912] strncpy_from_user+0x2b/0x280
116 | * [ 2542.112919] ? handler_pre+0x1dd/0x2e0 [kprobe_lkm]
117 | * [ 2542.118917] [390] kprobe_lkm:handler_pre(): 003) systemd-journal :390 | ...1 \* handler_pre() *\
118 | * [ 2542.124541] handler_pre+0x97/0x2e0 [kprobe_lkm]
119 | * [ ... ]
120 | * (shows up ONLY on our debug kernel!)
121 | * Not really much choice here, we use it ... :-/
122 | */
123 | #if 1
124 | if (!strncpy_from_user(fname, param_fname_reg, PATH_MAX))
125 | #else
126 | /* Attempting to use the 'usual' copy_from_user() here simply causes a hard
127 | * hang... avoid it */
128 | if (!copy_from_user(fname, (const char __user *)regs->si,
129 | strnlen_user((const char __user *)regs->si, PATH_MAX)))
130 | #endif
131 | return -EFAULT;
132 |
133 | pr_info("FILE being opened: reg:0x%px fname:%s\n",
134 | (void *)param_fname_reg, fname);
135 |
136 | spin_lock(&lock);
137 | tm_start = ktime_get_real_ns();
138 | spin_unlock(&lock);
139 |
140 | return 0;
141 | }
142 |
143 | /*
144 | * This probe runs immediately after the function "kprobe_func()" completes.
145 | */
146 | static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
147 | {
148 | if (skip_if_not_vi) {
149 | /* For the purpose of this demo, we only log information when the process
150 | * context is 'vi'
151 | */
152 | if (strncmp(current->comm, "vi", 2))
153 | return;
154 | }
155 |
156 | spin_lock(&lock);
157 | tm_end = ktime_get_real_ns();
158 |
159 | if (verbose)
160 | PRINT_CTX();
161 |
162 | SHOW_DELTA(tm_end, tm_start);
163 | spin_unlock(&lock);
164 | }
165 |
166 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
167 | /*
168 | * fault_handler: this is called if an exception is generated for any
169 | * instruction within the pre- or post-handler, or when Kprobes
170 | * single-steps the probed instruction.
171 | */
172 | static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
173 | {
174 | pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr);
175 | /* Return 0 because we don't handle the fault. */
176 | return 0;
177 | }
178 | NOKPROBE_SYMBOL(handler_fault);
179 | #endif
180 |
181 | static int __init kprobe_lkm_init(void)
182 | {
183 | /* Verify that the function to kprobe has been passed as a parameter to
184 | * this module
185 | */
186 | if (kprobe_func[0] == '\0') {
187 | pr_warn("expect a valid kprobe_func= module parameter");
188 | return -EINVAL;
189 | }
190 | pr_info("FYI, skip_if_not_vi is %s, verbose=%d\n", (skip_if_not_vi==1?"on":"off"), verbose);
191 |
192 | /********* Possible SECURITY concern:
193 | * We just assume the function pointer passed is valid and okay.
194 | * Minimally, ensure that the passed function is NOT marked with any of:
195 | * __kprobes or nokprobe_inline annotation nor marked via the NOKPROBE_SYMBOL
196 | * macro (and isn't blacklisted).
197 | */
198 | fname = kzalloc(PATH_MAX, GFP_ATOMIC);
199 | if (unlikely(!fname))
200 | return -ENOMEM;
201 |
202 | /* Register the kprobe handler */
203 | kpb.pre_handler = handler_pre;
204 | kpb.post_handler = handler_post;
205 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
206 | kpb.fault_handler = handler_fault;
207 | #endif
208 | kpb.symbol_name = kprobe_func;
209 | if (register_kprobe(&kpb)) {
210 | pr_alert("register_kprobe failed!\n\
211 | Check: is function '%s' invalid, static, inline; or blacklisted: attribute-marked '__kprobes'\n\
212 | or nokprobe_inline, or is marked with the NOKPROBE_SYMBOL macro?\n", kprobe_func);
213 | return -EINVAL;
214 | }
215 | pr_info("registering kernel probe @ '%s'\n", kprobe_func);
216 | spin_lock_init(&lock);
217 |
218 | return 0; /* success */
219 | }
220 |
221 | static void __exit kprobe_lkm_exit(void)
222 | {
223 | kfree(fname);
224 | unregister_kprobe(&kpb);
225 | pr_info("bye, unregistering kernel probe @ '%s'\n", kprobe_func);
226 | }
227 |
228 | module_init(kprobe_lkm_init);
229 | module_exit(kprobe_lkm_exit);
230 |
--------------------------------------------------------------------------------
/ch4/kprobes/3_kprobe/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile
2 | # ***************************************************************
3 | # This program is part of the source code released for the book
4 | # "Linux Kernel Debugging"
5 | # (c) Author: Kaiwan N Billimoria
6 | # Publisher: Packt
7 | # GitHub repository:
8 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
9 | #
10 | # ***************************************************************
11 | # Brief Description:
12 | # A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
13 | # the 'usual' targets (the build, install and clean), we incorporate targets to
14 | # do useful (and indeed required) stuff like:
15 | # - adhering to kernel coding style (indent+checkpatch)
16 | # - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
17 | # - two _dummy_ dynamic analysis targets (KASAN, LOCKDEP); just to remind you!
18 | # - a packaging (.tar.xz) target and
19 | # - a help target.
20 | #
21 | # To get started, just type:
22 | # make help
23 | #
24 | # For details on this so-called 'better' Makefile, please refer my earlier book
25 | # 'Linux Kernel Programming', Packt, Mar 2021, Ch 5 section 'A "better" Makefile
26 | # template for your kernel modules'.
27 |
28 | #------------------------------------------------------------------
29 | # Set FNAME_C to the kernel module name source filename (without .c)
30 | # This enables you to use this Makefile as a template; just update this variable!
31 | # As well, the MYDEBUG variable (see it below) can be set to 'y' or 'n' (no being
32 | # the default)
33 | FNAME_C := 3_kprobe
34 | #------------------------------------------------------------------
35 |
36 | # To support cross-compiling for kernel modules:
37 | # For architecture (cpu) 'arch', invoke make as:
38 | # make ARCH= CROSS_COMPILE=
39 | # The KDIR var is set to a sample path below; you're expected to update it on
40 | # your box to the appropriate path to the kernel src tree for that arch.
41 | ifeq ($(ARCH),arm)
42 | # *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
43 | KDIR ?= ~/rpi_work/kernel_rpi/linux
44 | else ifeq ($(ARCH),arm64)
45 | # *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
46 | # tree on your box
47 | KDIR ?= ~/kernel/linux-5.4
48 | else ifeq ($(ARCH),powerpc)
49 | # *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
50 | KDIR ?= ~/kernel/linux-5.0
51 | else
52 | # 'KDIR' is the Linux 'kernel headers' package on your host system; this is
53 | # usually an x86_64, but could be anything, really (f.e. building directly
54 | # on a Raspberry Pi implies that it's the host)
55 | KDIR ?= /lib/modules/$(shell uname -r)/build
56 | endif
57 |
58 | # Compiler
59 | CC := $(CROSS_COMPILE)gcc
60 | #CC := $(CROSS_COMPILE)gcc-10
61 | #CC := clang
62 |
63 | PWD := $(shell pwd)
64 | obj-m += ${FNAME_C}.o
65 |
66 | #--- Debug or production mode?
67 | # Set the MYDEBUG variable accordingly to y/n resp.
68 | # (Actually, debug info is always going to be generated when you build the
69 | # module on a debug kernel, where CONFIG_DEBUG_INFO is defined, making this
70 | # setting of the ccflags-y (or EXTRA_CFLAGS) variable mostly redundant (besides
71 | # the -DDEBUG).
72 | # This simply helps us influence the build on a production kernel, forcing
73 | # generation of debug symbols, if so required. Also, realize that the DEBUG
74 | # macro is turned on by many CONFIG_*DEBUG* options; hence, we use a different
75 | # macro var name, MYDEBUG).
76 | MYDEBUG := n
77 | ifeq (${MYDEBUG}, y)
78 |
79 | # https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
80 | # EXTRA_CFLAGS deprecated; use ccflags-y
81 | ccflags-y += -DDEBUG -g -ggdb -gdwarf-4 -Wall -fno-omit-frame-pointer -fvar-tracking-assignments
82 | else
83 | INSTALL_MOD_STRIP := 1
84 | #ccflags-y += --strip-debug
85 | endif
86 | # We always keep the dynamic debug facility enabled; this allows us to turn
87 | # dynamically turn on/off debug printk's later... To disable it simply comment
88 | # out the following line
89 | ccflags-y += -DDYNAMIC_DEBUG_MODULE
90 |
91 | KMODDIR ?= /lib/modules/$(shell uname -r)
92 | STRIP := ${CROSS_COMPILE}strip
93 |
94 | # gcc-10 issue:
95 | #ccflags-y += $(call cc-option,--allow-store-data-races)
96 |
97 | all:
98 | @echo
99 | @echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y=${ccflags-y} ---'
100 | @${CC} --version|head -n1
101 | @echo
102 | make -C $(KDIR) M=$(PWD) modules
103 | $(shell [ "${MYDEBUG}" != "y" ] && ${STRIP} --strip-debug ./${FNAME_C}.ko)
104 | install:
105 | @echo
106 | @echo "--- installing ---"
107 | @echo " [First, invoking the 'make' ]"
108 | make
109 | @echo
110 | @echo " [Now for the 'sudo make install' ]"
111 | sudo make -C $(KDIR) M=$(PWD) modules_install
112 | @echo " [If !debug, stripping debug info from ${KMODDIR}/extra/${FNAME_C}.ko]"
113 | $(shell if [ "${MYDEBUG}" != "y" ]; then sudo ${STRIP} --strip-debug ${KMODDIR}/extra/${FNAME_C}.ko; fi)
114 | clean:
115 | @echo
116 | @echo "--- cleaning ---"
117 | @echo
118 | make -C $(KDIR) M=$(PWD) clean
119 | # from 'indent'
120 | rm -f *~
121 |
122 | # Any usermode programs to build? Insert the build target(s) here
123 |
124 | #--------------- More (useful) targets! -------------------------------
125 | INDENT := indent
126 |
127 | # code-style : "wrapper" target over the following kernel code style targets
128 | code-style:
129 | make indent
130 | make checkpatch
131 |
132 | # indent- "beautifies" C code - to conform to the the Linux kernel
133 | # coding style guidelines.
134 | # Note! original source file(s) is overwritten, so we back it up.
135 | indent:
136 | @echo
137 | @echo "--- applying kernel code style indentation with indent ---"
138 | @echo
139 | mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
140 | ${INDENT} -linux --line-length95 *.[chsS]
141 | # add source files as required
142 |
143 | # Detailed check on the source code styling / etc
144 | checkpatch:
145 | make clean
146 | @echo
147 | @echo "--- kernel code style check with checkpatch.pl ---"
148 | @echo
149 | $(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
150 | # add source files as required
151 |
152 | #--- Static Analysis
153 | # sa : "wrapper" target over the following kernel static analyzer targets
154 | sa:
155 | make sa_sparse
156 | make sa_gcc
157 | make sa_flawfinder
158 | make sa_cppcheck
159 |
160 | # static analysis with sparse
161 | sa_sparse:
162 | make clean
163 | @echo
164 | @echo "--- static analysis with sparse ---"
165 | @echo
166 | # if you feel it's too much, use C=1 instead
167 | # NOTE: deliberately IGNORING warnings from kernel headers!
168 | make -Wsparse-all C=2 CHECK="/usr/bin/sparse --os=linux --arch=$(ARCH)" -C $(KDIR) M=$(PWD) modules 2>&1 |egrep -v "^\./include/.*\.h|^\./arch/.*\.h"
169 |
170 | # static analysis with gcc
171 | sa_gcc:
172 | make clean
173 | @echo
174 | @echo "--- static analysis with gcc ---"
175 | @echo
176 | make W=1 -C $(KDIR) M=$(PWD) modules
177 |
178 | # static analysis with flawfinder
179 | sa_flawfinder:
180 | make clean
181 | @echo
182 | @echo "--- static analysis with flawfinder ---"
183 | @echo
184 | flawfinder *.[ch]
185 |
186 | # static analysis with cppcheck
187 | sa_cppcheck:
188 | make clean
189 | @echo
190 | @echo "--- static analysis with cppcheck ---"
191 | @echo
192 | cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
193 |
194 | # Packaging; just tar.xz as of now
195 | PKG_NAME := ${FNAME_C}
196 | tarxz-pkg:
197 | rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
198 | make clean
199 | @echo
200 | @echo "--- packaging ---"
201 | @echo
202 | tar caf ../${PKG_NAME}.tar.xz *
203 | ls -l ../${PKG_NAME}.tar.xz
204 | @echo '=== package created: ../$(PKG_NAME).tar.xz ==='
205 | @echo 'Tip: when extracting, to extract into a dir of the same name as the tar file,'
206 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
207 |
208 | help:
209 | @echo '=== Makefile Help : additional targets available ==='
210 | @echo
211 | @echo 'TIP: type make to show all valid targets'
212 | @echo
213 |
214 | @echo '--- 'usual' kernel LKM targets ---'
215 | @echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
216 | @echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default here: /lib/modules/$(shell uname -r)/)'
217 | @echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
218 |
219 | @echo
220 | @echo '--- kernel code style targets ---'
221 | @echo 'code-style : "wrapper" target over the following kernel code style targets'
222 | @echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
223 | @echo ' checkpatch : run the kernel code style checker tool on source file(s)'
224 |
225 | @echo
226 | @echo '--- kernel static analyzer targets ---'
227 | @echo 'sa : "wrapper" target over the following kernel static analyzer targets'
228 | @echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
229 | @echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
230 | @echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
231 | @echo ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
232 | @echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
233 |
234 | @echo
235 | @echo '--- kernel dynamic analysis targets ---'
236 | @echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
237 | @echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
238 | @echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
239 |
240 | @echo
241 | @echo '--- misc targets ---'
242 | @echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
243 | @echo ' Tip: when extracting, to extract into a dir of the same name as the tar file,'
244 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
245 | @echo 'help : this help target'
246 |
--------------------------------------------------------------------------------
/ch4/kprobes/3_kprobe/run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # run
3 | KMOD=3_kprobe
4 | USE_VI=0
5 |
6 | [ ${USE_VI} -eq 1 ] && {
7 | sudo dmesg -C && make && sudo ./test.sh ${USE_VI} && sleep .1
8 | vi /0setup*
9 | sudo rmmod ${KMOD} 2>/dev/null && sudo dmesg
10 | } || {
11 | echo "sudo dmesg -C && make && sudo ./test.sh ${USE_VI} && sleep 5 && sudo rmmod ${KMOD} 2>/dev/null && sudo dmesg"
12 | sudo dmesg -C && make && sudo ./test.sh ${USE_VI}
13 | ls -l >/dev/null; sleep 5
14 | sudo rmmod ${KMOD} 2>/dev/null
15 | sudo dmesg
16 | }
17 |
--------------------------------------------------------------------------------
/ch4/kprobes/3_kprobe/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # test.sh
3 | KMOD=3_kprobe
4 | # As pointed out here:
5 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging#updates--observations
6 | # As of now (early 2023), attempting to trace file open's via the do_sys_open()
7 | # doesn't seem to cut it... I find that instead using the do_sys_openat2()
8 | # works! So, substitute this function in place of the do_sys_open() being used
9 | # and you may get better results...
10 | # (In fact, our book's Figure 4.13 shows the do_sys_openat2() being invoked!).
11 | FUNC_TO_KPROBE=do_sys_openat2
12 | #FUNC_TO_KPROBE=do_sys_open
13 | VERBOSE=1
14 |
15 | [ $# -ne 1 ] && {
16 | echo "Usage: $0 USE_VI ; 0 = show for all, 1 = show for vi only"
17 | exit 1
18 | }
19 | SKIP_NOT_VI=$1
20 | DYNDBG_CTRL=/sys/kernel/debug/dynamic_debug/control
21 | if [ ! -f ${DYNDBG_CTRL} ]; then
22 | [ -f /proc/dynamic_debug/control ] && DYNDBG_CTRL=/proc/dynamic_debug/control \
23 | || DYNDBG_CTRL=""
24 | fi
25 |
26 | echo "Module ${KMOD}: function to probe: ${FUNC_TO_KPROBE}
27 | "
28 | if [ ! -f ./${KMOD}.ko ]; then
29 | echo "Building ${KMOD} ..."
30 | make || exit 1
31 | fi
32 | sudo rmmod ${KMOD} 2>/dev/null # rm any stale instance
33 | # Ideally, first check that the function to kprobe isn't blacklisted; we skip
34 | # this here, doing this in the more sophisticated ch4/kprobes/4_kprobe_helper/kp_load.sh script
35 | sudo insmod ./${KMOD}.ko kprobe_func=${FUNC_TO_KPROBE} verbose=${VERBOSE} skip_if_not_vi=${SKIP_NOT_VI} || exit 1
36 |
37 | [ -z "${DYNDBG_CTRL}" ] && {
38 | echo "No dynamic debug control file available..."
39 | exit 1
40 | }
41 |
42 | echo "-- Module ${KMOD} now inserted, turn on dynamic debug prints now --"
43 | sudo bash -c "grep \"${KMOD} .* =_ \" ${DYNDBG_CTRL}" && echo "Wrt module ${KMOD}, one or more dynamic debug prints are Off" || \
44 | echo "Wrt module ${KMOD}, one or more dynamic debug prints are On"
45 | # turn On debug prints
46 | sudo bash -c "echo -n \"module ${KMOD} +p\" > ${DYNDBG_CTRL}"
47 | sudo bash -c "grep \"${KMOD}\" ${DYNDBG_CTRL}"
48 | echo "-- All set, look up kernel log with, f.e., journalctl -k -f --"
49 | exit 0
50 |
--------------------------------------------------------------------------------
/ch4/kprobes/4_kprobe_helper/Readme.txt:
--------------------------------------------------------------------------------
1 | Tips:
2 | -----
3 | 1. Kprobes is a kernel feature; first, check whether Kprobes is enabled
4 | for the current kernel:
5 | grep CONFIG_KPROBES /boot/config-$(uname -r)
6 | (the helper script will do so in any case...)
7 |
8 | 2. Further, verify Kprobes support in this manner too:
9 | sudo grep -w register_kprobe /boot/System.map-$(uname -r)
10 | (the helper script will do so in any case...)
11 |
12 | 3. If required, enable Kprobes within the kernel with:
13 | cd
14 | make menuconfig
15 | General Setup / Kprobes : turn it ON
16 | Exit with Save
17 | .
18 |
--------------------------------------------------------------------------------
/ch4/kprobes/4_kprobe_helper/common.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #------------------------------------------------------------------
3 | # common.sh
4 | #
5 | # Common convenience routines
6 | #
7 | # (c) Kaiwan N Billimoria
8 | # kaiwan -at- designergraphix -dot- com
9 | # GPL / LGPL
10 | # Last Updt: 29Dec2013
11 | #------------------------------------------------------------------
12 |
13 | export TOPDIR=$(pwd)
14 | ON=1
15 | OFF=0
16 |
17 | ### UPDATE for your box
18 | source ./err_common.sh || {
19 | echo "$name: could not source err_common.sh, aborting..."
20 | exit 1
21 | }
22 |
23 |
24 | # DesktopNotify
25 | # Ubuntu desktop notify-send wrapper func
26 | # Parameter(s)
27 | # $1 : String to display in desktop notification message [required]
28 | function DesktopNotify()
29 | {
30 | # Ubuntu : notify-send !
31 | [ $# -ne 1 ] && MSG="" || MSG="$1"
32 | notify-send --urgency=low "${MSG}"
33 | }
34 |
35 |
36 | # genLogFilename
37 | # Generates a logfile name that includes the date/timestamp
38 | # Format:
39 | # ddMmmYYYY[_HHMMSS]
40 | # Parameter(s)
41 | # #$1 : String to prefix to log filename, null okay as well [required]
42 | # $1 : Include time component or not [required]
43 | # $1 = 0 : Don't include the time component (only date) in the log filename
44 | # $1 = 1 : include the time component in the log filename
45 | genLogFilename()
46 | {
47 | [ $1 -eq 0 ] && log_filename=$(date +%d%b%Y)
48 | [ $1 -eq 1 ] && log_filename=$(date +%d%b%Y_%H%M%S)
49 | echo ${log_filename}
50 | }
51 |
52 | # Debug echo :-)
53 | decho()
54 | {
55 | Prompt "$@"
56 | }
57 |
58 | verbose=1
59 | # Echo
60 | # Echo string (with timestamp prefixed) to stdout and to Log file
61 | # if so specified.
62 | # Parameter(s):
63 | # $1 : String to display to stdout [required]
64 | # $2 : Log pathname to also append the $1 string to [optional]
65 | Echo()
66 | {
67 | #echo "# p = $#"
68 | [ $# -eq 0 ] && return 1
69 | [ ${verbose} -eq 1 ] && {
70 | echo -n "$(date) : "
71 | echo "$1"
72 | [ $# -ge 2 ] && {
73 | [ -f $2 -a -w $2 ] && {
74 | echo -n "$(date) : " >> $2
75 | echo "$1" >> $2
76 | }
77 | }
78 | }
79 | }
80 |
81 |
82 | # ShowTitle
83 | # Display a string in "title" form
84 | # Parameter(s):
85 | # $1 : String to display [required]
86 | # Returns: 0 on success, 1 on failure
87 | ShowTitle()
88 | {
89 | [ $# -ne 1 ] && return 1
90 | SEP='-------------------------------------------------------------------------------'
91 | echo $SEP
92 | echo $1
93 | echo $SEP
94 | }
95 |
96 | # check_root_AIA
97 | # Check whether we are running as root user; if not, exit with failure!
98 | # Parameter(s):
99 | # None.
100 | # "AIA" = Abort If Absent :-)
101 | check_root_AIA()
102 | {
103 | if [ `id -u` -ne 0 ]; then
104 | Echo "Error: need to run as root! Aborting..."
105 | exit 1
106 | fi
107 | }
108 |
109 | # check_file_AIA
110 | # Check whether the file, passed as a parameter, exists; if not, exit with failure!
111 | # Parameter(s):
112 | # $1 : Pathname of file to check for existence. [required]
113 | # "AIA" = Abort If Absent :-)
114 | # Returns: 0 on success, 1 on failure
115 | check_file_AIA()
116 | {
117 | [ $# -ne 1 ] && return 1
118 | [ ! -f $1 ] && {
119 | Echo "Error: file \"$1\" does not exist. Aborting..."
120 | exit 1
121 | }
122 | }
123 |
124 | # check_folder_AIA
125 | # Check whether the directory, passed as a parameter, exists; if not, exit with failure!
126 | # Parameter(s):
127 | # $1 : Pathname of folder to check for existence. [required]
128 | # "AIA" = Abort If Absent :-)
129 | # Returns: 0 on success, 1 on failure
130 | check_folder_AIA()
131 | {
132 | [ $# -ne 1 ] && return 1
133 | [ ! -d $1 ] && {
134 | Echo "Error: folder \"$1\" does not exist. Aborting..."
135 | exit 1
136 | }
137 | }
138 |
139 | # check_folder_createIA
140 | # Check whether the directory, passed as a parameter, exists; if not, create it!
141 | # Parameter(s):
142 | # $1 : Pathname of folder to check for existence. [required]
143 | # "IA" = If Absent :-)
144 | # Returns: 0 on success, 1 on failure
145 | check_folder_createIA()
146 | {
147 | [ $# -ne 1 ] && return 1
148 | [ ! -d $1 ] && {
149 | Echo "Folder \"$1\" does not exist. Creating it..."
150 | mkdir -p $1 && return 0 || return 1
151 | }
152 | }
153 |
154 |
155 | # GetIP
156 | # Extract IP address from ifconfig output
157 | # Parameter(s):
158 | # $1 : name of network interface (string)
159 | # Returns: IPaddr on success, non-zero on failure
160 | GetIP()
161 | {
162 | [ $# -ne 1 ] && return 1
163 | ifconfig $1 >/dev/null 2>&1 || return 2
164 | ifconfig $1 |grep 'inet addr'|awk '{print $2}' |cut -f2 -d':'
165 | }
166 |
167 | # get_yn_reply
168 | # User's reply should be Y or N.
169 | # Returns:
170 | # 0 => user has answered 'Y'
171 | # 1 => user has answered 'N'
172 | get_yn_reply()
173 | {
174 | echo -n "Type Y or N please (followed by ENTER) : "
175 | str="${@}"
176 | while true
177 | do
178 | echo ${str}
179 | read reply
180 |
181 | case "$reply" in
182 | y | yes | Y | YES ) return 0
183 | ;;
184 | n* | N* ) return 1
185 | ;;
186 | *) echo "What? Pl type Y / N"
187 | esac
188 | done
189 | }
190 |
191 | # MountPartition
192 | # Mounts the partition supplied as $1
193 | # Parameters:
194 | # $1 : device node of partition to mount
195 | # $2 : mount point
196 | # Returns:
197 | # 0 => mount successful
198 | # 1 => mount failed
199 | MountPartition()
200 | {
201 | [ $# -ne 2 ] && {
202 | echo "MountPartition: parameter(s) missing!"
203 | return 1
204 | }
205 |
206 | DEVNODE=$1
207 | [ ! -b ${DEVNODE} ] && {
208 | echo "MountPartition: device node $1 does not exist?"
209 | return 1
210 | }
211 |
212 | MNTPT=$2
213 | [ ! -d ${MNTPT} ] && {
214 | echo "MountPartition: folder $2 does not exist?"
215 | return 1
216 | }
217 |
218 | mount |grep ${DEVNODE} >/dev/null || {
219 | #echo "The partition is not mounted, attempting to mount it now..."
220 | mount ${DEVNODE} -t auto ${MNTPT} || {
221 | echo "Could not mount the '$2' partition!"
222 | return 1
223 | }
224 | }
225 | return 0
226 | }
227 |
228 |
229 | #------------------- Colors!! Yay :-) -----------------------------------------
230 | # Ref: http://tldp.org/LDP/abs/html/colorizing.html
231 | black='\E[30;47m'
232 | red='\E[31;47m'
233 | green='\E[32;47m'
234 | yellow='\E[33;47m'
235 | blue='\E[34;47m'
236 | magenta='\E[35;47m'
237 | cyan='\E[36;47m'
238 | white='\E[37;47m'
239 |
240 | # Reset text attributes to normal without clearing screen.
241 | Reset()
242 | {
243 | tput sgr0
244 | }
245 |
246 | # !!!
247 | # Turn this ON for COLOR !!!
248 | # !!!
249 | COLOR=${OFF}
250 | #COLOR=${ON}
251 |
252 | # Color-echo.
253 | # Argument $1 = message
254 | # Argument $2 = color
255 | # Usage eg.:
256 | # cecho "This message is in blue!" $blue
257 | cecho ()
258 | {
259 | local default_msg="No message passed."
260 | # Doesn't really need to be a local variable.
261 | [ ${COLOR} -eq 0 ] && {
262 | echo $1
263 | return
264 | }
265 | #echo "cecho: nump = $# : $@"
266 |
267 | message=${1:-$default_msg} # Defaults to default message.
268 | color=${2:-$black} # Defaults to black, if not specified.
269 |
270 | echo -e "$color"
271 | echo "$message"
272 | Reset # Reset to normal.
273 |
274 | return
275 | }
276 | #----------------------------------------------------------------------
277 |
278 |
279 | ## is_kernel_thread
280 | # Param: PID
281 | # Returns:
282 | # 1 if $1 is a kernel thread, 0 if not, 127 on failure.
283 | is_kernel_thread()
284 | {
285 | [ $# -ne 1 ] && {
286 | echo "is_kernel_thread: parameter missing!" 1>&2
287 | return 127
288 | }
289 |
290 | prcs_name=$(ps aux |awk -v pid=$1 '$2 == pid {print $11}')
291 | #echo "prcs_name = ${prcs_name}"
292 | [ -z ${prcs_name} ] && {
293 | echo "is_kernel_thread: could not obtain process name!" 1>&2
294 | return 127
295 | }
296 |
297 | firstchar=$(echo "${prcs_name:0:1}")
298 | #echo "firstchar = ${firstchar}"
299 | len=${#prcs_name}
300 | let len=len-1
301 | lastchar=$(echo "${prcs_name:${len}:1}")
302 | #echo "lastchar = ${lastchar}"
303 | [ ${firstchar} = "[" -a ${lastchar} = "]" ] && return 1 || return 0
304 | }
305 |
306 |
--------------------------------------------------------------------------------
/ch4/kprobes/4_kprobe_helper/err_common.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #------------------------------------------------------------------
3 | # err_common.sh
4 | #
5 | # Common error handling routines.
6 | #
7 | # (c) Kaiwan N Billimoria
8 | # kaiwan -at- designergraphix -dot- com
9 | # GPL / LGPL
10 | # Last Updt: 31Mar2014
11 | #------------------------------------------------------------------
12 |
13 | GUI_MODE=1
14 | export TOPDIR=$(pwd)
15 | ON=1
16 | OFF=0
17 |
18 | # "Returns" (actually echoes)
19 | # 1 => zenity present
20 | # 0 => zenity absent
21 | verify_zenity()
22 | {
23 | which zenity >/dev/null 2>&1 && echo -n 1 || echo -n 0
24 | }
25 |
26 | # QP
27 | # QuickPrint ;-)
28 | # Print timestamp, script name, line#. Useful for debugging.
29 | QP()
30 | {
31 | _ERR_HDR_FMT="%.23s %s[%s]: "
32 | _ERR_MSG_FMT="${_ERR_HDR_FMT}%s\n"
33 | printf "$_ERR_MSG_FMT" $(date +%F.%T.%N) ${BASH_SOURCE[1]##*/} ${BASH_LINENO[0]} 1>&2 #"${@}"
34 | unset _ERR_HDR_FMT
35 | unset _ERR_MSG_FMT
36 | }
37 |
38 |
39 | cli_handle_error()
40 | {
41 | echo -n "FatalError :: " 1>&2
42 | QP
43 | [ $# -lt 1 ] && exit -1
44 | echo "${@}" 1>&2
45 | exit -1
46 | }
47 |
48 | # FatalError
49 | # Display the error message string and exit -1.
50 | # Parameter(s):
51 | # $1 : Error string to display [required]
52 | # Returns: -1 (255)
53 | FatalError()
54 | {
55 | [ ${GUI_MODE} -eq 0 ] && {
56 | cli_handle_error $@
57 | } || { # want GUI mode
58 | #n=$(verify_zenity)
59 | #echo "n=$n"
60 | #return
61 | [ $(verify_zenity) -eq 0 ] && {
62 | echo "FatalError :: !WARNING! zenity not installed?? "
63 | # fallback to non-gui err handling
64 | cli_handle_error $@
65 | }
66 | }
67 | # gui err handling, zenity there; whew
68 | zenity --error --title="${name}: Error" --text="Fatal Error :: $@"
69 | cli_handle_error $@
70 | exit -1
71 | }
72 |
73 | # Prompt
74 | # Interactive: prompt the user to continue by pressing ENTER or
75 | # abort by pressing Ctrl-C
76 | # Parameter(s):
77 | # $1 : string to display (string)
78 | Prompt()
79 | {
80 | [ $# -lt 1 ] && {
81 | echo "$0: Prompt function requires a string parameter!"
82 | return 1
83 | }
84 | echo "${@}"
85 | echo " Press ENTER to continue, Ctrl-C to abort now..."
86 | read
87 | }
88 |
89 |
90 |
--------------------------------------------------------------------------------
/ch4/kprobes/4_kprobe_helper/helper_kp.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch6/kprobes/4_kprobe_helper/helper_kp.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 6: Debug via Instrumentation - Kprobes
12 | ****************************************************************
13 | * Brief Description:
14 | * Our kprobes demo #4:
15 | * Traditional, semi-automated manual approach: a helper script generates a
16 | * template for both the kernel module C code and the Makefile, enabling
17 | * attaching a kprobe to a given function via module parameter.
18 | *
19 | * This 'C' source will act as a template:
20 | * the helper script kp_load.sh will:
21 | * copy it into a tmp/ folder (with name-timestamp.c format), a
22 | * Makefile built for it, it will be built (as a .ko) and, finally,
23 | * will be passed the name of a kernel or kernel module's function and
24 | * verbosity flag (during insmod).
25 | *
26 | * The job of this "helper" module is to setup the kprobe given the address.
27 | * The function must not be marked 'static' or 'inline' in the kernel / LKM.
28 | *
29 | * For details, please refer the book, Ch 6.
30 | * License: MIT
31 | */
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include "../../../../convenient.h"
40 |
41 | #define MODULE_VER "0.1"
42 |
43 | static char *funcname;
44 | /* module_param (var, type, sysfs_entry_permissions);
45 | * 0 in last => no sysfs entry
46 | */
47 | module_param(funcname, charp, 0);
48 | MODULE_PARM_DESC(funcname,
49 | "Function name of the target (LKM's) function to attach probe to.");
50 |
51 | static int verbose;
52 | module_param(verbose, int, 0644);
53 | MODULE_PARM_DESC(verbose, "Set to 1 to get verbose printk's (defaults to 0).");
54 |
55 | static int show_stack;
56 | module_param(show_stack, int, 0644);
57 | MODULE_PARM_DESC(show_stack, "Set to 1 to dump the kernel-mode stack; defaults to 0).");
58 |
59 | static struct kprobe kpb;
60 | static u64 tm_start = 0, tm_end = 0;
61 | static int running_avg=0;
62 | static spinlock_t lock;
63 |
64 | /*
65 | * This probe runs just prior to the function "funcname()" is invoked.
66 | */
67 | static int handler_pre(struct kprobe *p, struct pt_regs *regs)
68 | {
69 | spin_lock(&lock);
70 | tm_start = ktime_get_real_ns();
71 | spin_unlock(&lock);
72 |
73 | if (verbose) {
74 | pr_debug_ratelimited("%s:%s():Pre '%s'.\n", KBUILD_MODNAME, __func__, funcname);
75 | PRINT_CTX();
76 | }
77 | if (show_stack)
78 | dump_stack();
79 |
80 | return 0;
81 | }
82 |
83 | /*
84 | * This probe runs immediately after the function "funcname()" completes.
85 | */
86 | static void handler_post(struct kprobe *p, struct pt_regs *regs,
87 | unsigned long flags)
88 | {
89 | spin_lock(&lock);
90 | tm_end = ktime_get_real_ns();
91 |
92 | if (verbose) {
93 | pr_debug_ratelimited("%s:%s():%s:%d. Post '%s'.\n",
94 | KBUILD_MODNAME, __func__, current->comm, current->pid, funcname);
95 | }
96 |
97 | SHOW_DELTA(tm_end, tm_start);
98 | spin_unlock(&lock);
99 | }
100 |
101 | static int __init helper_kp_init_module(void)
102 | {
103 | if (!funcname) {
104 | pr_info("%s:%s():Must pass funcname as a module parameter\n", KBUILD_MODNAME, __func__);
105 | return -EINVAL;
106 | }
107 | spin_lock_init(&lock);
108 | pr_info("%s:%s():kprobe'ing function %s, verbose mode? %s, show stack? %s\n",
109 | KBUILD_MODNAME, __func__, funcname, (verbose==1?"Y":"N"), (show_stack==1?"Y":"N"));
110 |
111 | /********* Possible SECURITY concern:
112 | * We just assume the pointer passed is valid and okay.
113 | * Our kp_load.sh script has performed basic verification...
114 | */
115 | /* Register the kprobe handler */
116 | kpb.pre_handler = handler_pre;
117 | kpb.post_handler = handler_post;
118 | kpb.symbol_name = funcname;
119 | if (register_kprobe(&kpb)) {
120 | pr_alert("%s:%s():register_kprobe failed!\n"
121 | "Check: is function '%s' invalid, static, inline or attribute-marked '__kprobes' ?\n",
122 | KBUILD_MODNAME, __func__, funcname);
123 | return -EINVAL;
124 | }
125 | pr_info("%s:%s():registered kprobe for function %s\n", KBUILD_MODNAME, __func__, funcname);
126 | return 0; /* success */
127 | }
128 |
129 | static void helper_kp_cleanup_module(void)
130 | {
131 | unregister_kprobe(&kpb);
132 | pr_info("%s:%s():unregistered kprobe @ function %s\n", KBUILD_MODNAME, __func__, funcname);
133 | }
134 |
135 | module_init(helper_kp_init_module);
136 | module_exit(helper_kp_cleanup_module);
137 |
138 | MODULE_AUTHOR("Kaiwan N Billimoria");
139 | MODULE_DESCRIPTION("Helper Kprobe module; registers a kprobe to the passed function");
140 | MODULE_LICENSE("Dual MIT/GPL");
141 |
--------------------------------------------------------------------------------
/ch5/kmembugs_test/debugfs_kmembugs.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch7/kmembugs_test/debugfs_kmembugs.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 5: Debugging kernel memory issues
12 | ****************************************************************
13 | * Brief Description:
14 | * kmembugs_test.c: this source file:
15 | * This kernel module has buggy functions, each of which represents a simple
16 | * test case.
17 | *
18 | * debugfs_kmembugs.c:
19 | * Source for the debugfs infrastructure to run these test cases; it creates the
20 | * debugs file - typically
21 | * /sys/kernel/debug/test_kmembugs/lkd_dbgfs_run_testcase
22 | * used to execute individual testcases by writing the testcase # (as a string)
23 | * to this pseudo-file.
24 | *
25 | * IMP:
26 | * By default, KASAN will turn off reporting after the very first error
27 | * encountered; we can change this behavior (and therefore test more easily)
28 | * by using the API pair kasan_save_enable_multi_shot() / kasan_restore_multi_shot()
29 | * we do just this within the init and cleanup of this module
30 | * (note that these APIs require GPL licensing!).
31 | *
32 | * For details, please refer the book, Ch 5.
33 | */
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
41 | #include
42 | #include
43 | #else
44 | #include
45 | #endif
46 |
47 | //----------------- The testcase function prototypes, in order
48 | int umr(void); // testcase 1
49 | void *uar(void); // testcase 2
50 | void leak_simple1(void); // testcase 3.1
51 | void *leak_simple2(void); // testcase 3.2
52 | void leak_simple3(void); // testcase 3.3
53 |
54 | int global_mem_oob_right(int mode, char *p); // testcase 4.1/5.1
55 | int global_mem_oob_left(int mode, char *p); // testcase 4.2/5.2
56 | int dynamic_mem_oob_right(int mode); // testcase 4.3/5.3
57 | int dynamic_mem_oob_left(int mode); // testcase 4.4/5.4
58 |
59 | int uaf(void); // testcase 6
60 | int double_free(void); // testcase 7
61 |
62 | // UBSAN testcases 8.x
63 | void test_ubsan_add_overflow(void);
64 | void test_ubsan_sub_overflow(void);
65 | void test_ubsan_mul_overflow(void);
66 | void test_ubsan_negate_overflow(void);
67 | void test_ubsan_divrem_overflow(void);
68 | void test_ubsan_shift_out_of_bounds(void);
69 | void test_ubsan_out_of_bounds(void);
70 | void test_ubsan_load_invalid_value(void);
71 | void test_ubsan_misaligned_access(void);
72 | void test_ubsan_object_size_mismatch(void);
73 |
74 | noinline void oob_copy_user_test(void); // testcase 9
75 | int umr_slub(void); // SLUB debug testcase, testcase 10
76 | //----------------------------------------------
77 |
78 | struct dentry *gparent;
79 | EXPORT_SYMBOL(gparent);
80 |
81 | extern char global_arr1[], global_arr2[], global_arr3[];
82 |
83 | #define MAXUPASS 5
84 | static ssize_t dbgfs_run_testcase(struct file *filp, const char __user *ubuf, size_t count,
85 | loff_t *fpos)
86 | {
87 | char udata[MAXUPASS];
88 | volatile char *res1 = NULL, *res2 = NULL;
89 |
90 | if (count > MAXUPASS) {
91 | pr_warn("too much data attempted to be passed from userspace to here\n");
92 | return -ENOSPC;
93 | }
94 | if (copy_from_user(udata, ubuf, count))
95 | return -EIO;
96 | udata[count - 1] = '\0';
97 | pr_debug("testcase to run: %s\n", udata);
98 |
99 | /*
100 | * Now udata contains the data passed from userspace - the testcase # to run
101 | * (as a string)
102 | */
103 | if (!strncmp(udata, "1", 2))
104 | umr();
105 | else if (!strncmp(udata, "2", 2)) {
106 | res1 = uar();
107 | pr_info("testcase 2: UAR: res1 = \"%s\"\n",
108 | res1 == NULL ? "" : (char *)res1);
109 | } else if (!strncmp(udata, "3.1", 4))
110 | leak_simple1();
111 | else if (!strncmp(udata, "3.2", 4)) {
112 | res2 = (char *)leak_simple2(); // caller's expected to free the memory!
113 | pr_info(" res2 = \"%s\"\n",
114 | res2 == NULL ? "" : (char *)res2);
115 | if (0) /* test: ensure it isn't freed by us, the caller */
116 | kfree((char *)res2);
117 | } else if (!strncmp(udata, "3.3", 4))
118 | leak_simple3();
119 | else if (!strncmp(udata, "4.1", 4))
120 | global_mem_oob_right(READ, global_arr2);
121 | else if (!strncmp(udata, "4.2", 4))
122 | global_mem_oob_right(WRITE, global_arr2);
123 | else if (!strncmp(udata, "4.3", 4))
124 | global_mem_oob_left(READ, global_arr2);
125 | else if (!strncmp(udata, "4.4", 4))
126 | global_mem_oob_left(WRITE, global_arr2);
127 | else if (!strncmp(udata, "5.1", 4))
128 | dynamic_mem_oob_right(READ);
129 | else if (!strncmp(udata, "5.2", 4))
130 | dynamic_mem_oob_right(WRITE);
131 | else if (!strncmp(udata, "5.3", 4))
132 | dynamic_mem_oob_left(READ);
133 | else if (!strncmp(udata, "5.4", 4))
134 | dynamic_mem_oob_left(WRITE);
135 | else if (!strncmp(udata, "6", 2))
136 | uaf();
137 | else if (!strncmp(udata, "7", 2))
138 | double_free();
139 | else if (!strncmp(udata, "8.1", 4))
140 | test_ubsan_add_overflow();
141 | else if (!strncmp(udata, "8.2", 4))
142 | test_ubsan_sub_overflow();
143 | else if (!strncmp(udata, "8.3", 4))
144 | test_ubsan_mul_overflow();
145 | else if (!strncmp(udata, "8.4", 4))
146 | test_ubsan_negate_overflow();
147 | else if (!strncmp(udata, "8.4", 4))
148 | test_ubsan_divrem_overflow();
149 | else if (!strncmp(udata, "8.5", 4))
150 | test_ubsan_shift_out_of_bounds();
151 | else if (!strncmp(udata, "8.6", 4))
152 | test_ubsan_out_of_bounds();
153 | else if (!strncmp(udata, "8.7", 4))
154 | test_ubsan_load_invalid_value();
155 | else if (!strncmp(udata, "8.8", 4))
156 | test_ubsan_misaligned_access();
157 | else if (!strncmp(udata, "8.9", 4))
158 | test_ubsan_object_size_mismatch();
159 | else if (!strncmp(udata, "9", 2))
160 | oob_copy_user_test();
161 | else if (!strncmp(udata, "10", 3))
162 | umr_slub();
163 | else
164 | pr_warn("Invalid testcase # (%s) passed\n", udata);
165 |
166 | return count;
167 | }
168 |
169 | static const struct file_operations dbgfs_fops = {
170 | .write = dbgfs_run_testcase,
171 | };
172 |
173 | int debugfs_simple_intf_init(void)
174 | {
175 | int stat = 0;
176 | struct dentry *file1;
177 |
178 | if (!IS_ENABLED(CONFIG_DEBUG_FS)) {
179 | pr_warn("debugfs unsupported! Aborting ...\n");
180 | return -EINVAL;
181 | }
182 |
183 | /* 1. Create a dir under the debugfs mount point, whose name is the
184 | * module name */
185 | gparent = debugfs_create_dir(KBUILD_MODNAME, NULL);
186 | if (!gparent) {
187 | pr_info("debugfs_create_dir failed, aborting...\n");
188 | stat = PTR_ERR(gparent);
189 | goto out_fail_1;
190 | }
191 |
192 | /* Create a generic write-only-as-root debugfs file; arrange for a callback
193 | * function on write (via the classic file_operations structure).
194 | */
195 | #define DBGFS_FILE "lkd_dbgfs_run_testcase"
196 | file1 = debugfs_create_file(DBGFS_FILE, 0200, gparent, NULL, &dbgfs_fops);
197 | if (!file1) {
198 | pr_info("debugfs_create_file failed, aborting...\n");
199 | stat = PTR_ERR(file1);
200 | goto out_fail_2;
201 | }
202 | pr_debug("debugfs file 1 /%s/%s created\n",
203 | KBUILD_MODNAME, DBGFS_FILE);
204 |
205 | pr_info("debugfs entry initialized\n");
206 | return 0;
207 |
208 | out_fail_2:
209 | debugfs_remove_recursive(gparent);
210 | out_fail_1:
211 | return stat;
212 | }
213 |
--------------------------------------------------------------------------------
/ch5/kmembugs_test/load_testmod:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # load_testmod
3 | #***************************************************************
4 | # This program is part of the source code released for the book
5 | # "Linux Kernel Debugging"
6 | # (c) Author: Kaiwan N Billimoria
7 | # Publisher: Packt
8 | # GitHub repository:
9 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | #
11 | # From: Ch 5: Debugging kernel memory issues
12 | #***************************************************************
13 | # Simple bash wrapper to setup our custom testcases for KASAN & UBSAN.
14 | name=$(basename $0)
15 | # TODO
16 | # locate_kernel_config()
17 | KCONF=/boot/config-$(uname -r)
18 |
19 | setup_testmod()
20 | {
21 | KMOD=test_kmembugs
22 | if grep -q "CONFIG_CC_IS_GCC=y" ${KCONF}; then
23 | CCVAR=gcc
24 | echo "This kernel has been built with gcc"
25 | elif grep -q "CONFIG_CC_IS_CLANG=y" ${KCONF}; then
26 | CCVAR=clang
27 | echo "This kernel has been built with clang"
28 | fi
29 | make CC=${CCVAR} || return
30 | [ ! -f ${KMOD}.ko ] && {
31 | echo "Module ${KMOD}.ko not built? aborting..."
32 | exit 1
33 | }
34 | sudo rmmod ${KMOD} 2>/dev/null
35 | sudo dmesg -C
36 | sudo insmod ./${KMOD}.ko
37 | sudo dmesg
38 | }
39 |
40 | #--- 'main' here
41 |
42 | echo "Kernel ver: $(uname -r)"
43 | ubsan=1
44 | kasan=1
45 | if ! grep -q "CONFIG_UBSAN=y" ${KCONF}; then
46 | echo "UBSAN disabled for this kernel"
47 | ubsan=0
48 | else
49 | echo "UBSAN enabled"
50 | fi
51 | if ! grep -q "CONFIG_KASAN_GENERIC=y" ${KCONF}; then
52 | echo "Generic KASAN disabled for this kernel"
53 | kasan=0
54 | else
55 | echo "Generic KASAN enabled"
56 | fi
57 | [[ ${ubsan} -eq 0 ]] && [[ ${kasan} -eq 0 ]] && {
58 | echo "=== NOTE! Both KASAN and UBSAN are disabled for this kernel ==="
59 | }
60 | val=0
61 | [ ${kasan} -eq 1 ] && val=1
62 | setup_testmod ${val}
63 | exit 0
64 |
--------------------------------------------------------------------------------
/ch5/kmembugs_test/run_tests:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # run_tests
3 | #***************************************************************
4 | # This program is part of the source code released for the book
5 | # "Linux Kernel Debugging"
6 | # (c) Author: Kaiwan N Billimoria
7 | # Publisher: Packt
8 | # GitHub repository:
9 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | #
11 | # From: Ch 5: Debugging kernel memory issues - Part 1
12 | #***************************************************************
13 | # Simple bash wrapper to run our custom testcases for KASAN & UBSAN.
14 | # For details, please refer the book, Ch 5 and Ch 6.
15 | name=$(basename $0)
16 | KMOD=test_kmembugs
17 | DBGFS_MNT=/sys/kernel/debug
18 |
19 | INTERACTIVE=1
20 |
21 | # TODO
22 | # locate_kernel_config()
23 | KCONF=/boot/config-$(uname -r)
24 |
25 | # chkconf()
26 | # $1 : string decribing CONFIG_FOO to check
27 | # $1 : CONFIG_FOO to check
28 | chkconf()
29 | {
30 | [ $# -lt 2 ] && return
31 | echo -n "$1: "
32 | grep -q "$2=y" ${KCONF} && echo "enabled" || echo "disabled"
33 | }
34 |
35 | show_curr_config()
36 | {
37 | chkconf "Generic KASAN" CONFIG_KASAN_GENERIC
38 | chkconf "UBSAN" CONFIG_UBSAN
39 | chkconf "KMEMLEAK" CONFIG_DEBUG_KMEMLEAK
40 | #if echo "scan=on" > ${DBGFS_MNT}/kmemleak ; then
41 | #if [ -f ${DBGFS_MNT}/kmemleak ] ; then
42 | # echo "scan=on" > ${DBGFS_MNT}/kmemleak
43 | # chkconf "KMEMLEAK" CONFIG_DEBUG_KMEMLEAK
44 | #fi
45 | echo
46 | }
47 |
48 | # Parameter is the testcase # to run
49 | run_testcase()
50 | {
51 | [ $# -ne 1 ] && {
52 | echo "run_testcase(): pass the testcase # as the parameter"
53 | return
54 | }
55 | local testcase=$1
56 | echo "-------- Running testcase \"${testcase}\" via test module now..."
57 | [ ${no_clear} -eq 0 ] && dmesg -C
58 | echo "${testcase}" > ${KMOD_DBGFS_FILE} # the real work!
59 | dmesg
60 | }
61 |
62 | usage()
63 | {
64 | echo "Usage: ${name} [--no-clear]
65 | --no-clear: do NOT clear the kernel ring buffer before & after running the testcase
66 | optional, off by default"
67 | }
68 |
69 |
70 | #--- 'main'
71 | if [[ $# -ge 1 ]] && [[ $1 = "-h" ]]; then
72 | usage
73 | exit 0
74 | fi
75 | no_clear=0
76 | [[ $1 = "--no-clear" ]] && {
77 | no_clear=1
78 | echo "--no_clear: will not clear kernel log buffer after running a testcase"
79 | }
80 | if ! lsmod | grep -q ${KMOD} ; then
81 | echo "${name}: load the test module first by running the load_testmod script"
82 | exit 1
83 | fi
84 |
85 | [ $(id -u) -ne 0 ] && {
86 | echo "${name}: needs root."
87 | exit 1
88 | }
89 |
90 | #--- verify debugfs pseudo-file is present
91 | if grep -q "CONFIG_DEBUG_FS_DISALLOW_MOUNT=y" ${KCONF} ; then
92 | echo "${name}: debugfs mount invisible (CONFIG_DEBUG_FS_DISALLOW_MOUNT=y), can't proceed."
93 | exit 1
94 | fi
95 | [ ! -d ${DBGFS_MNT} ] && {
96 | echo "${name}: debugfs mount point \"${DBGFS_MNT}\" not present?
97 | If this is expected, pl fix this script to point to the correct debugfs mount location and retry"
98 | exit 1
99 | }
100 | KMOD_DBGFS_FILE=${DBGFS_MNT}/${KMOD}/lkd_dbgfs_run_testcase
101 | [ ! -f ${KMOD_DBGFS_FILE} ] && {
102 | echo "${name}: debugfs file \"${KMOD_DBGFS_FILE}\" not present? Aborting..."
103 | exit 1
104 | }
105 | echo "Debugfs file: ${KMOD_DBGFS_FILE}
106 | "
107 | show_curr_config
108 |
109 | MAX_TESTNUM=9
110 |
111 | if [ ${INTERACTIVE} -eq 1 ] ; then
112 |
113 | #--- all ok, let's go; display the menu, accept the testcase #
114 | echo "Select testcase to run:
115 | 1 Uninitialized Memory Read - UMR
116 | 2 Use After Return - UAR
117 |
118 | Memory leakage
119 | 3.1 simple memory leakage testcase1
120 | 3.2 simple memory leakage testcase2 - caller to free memory
121 | 3.3 simple memory leakage testcase3 - memleak in interrupt ctx
122 |
123 | OOB accesses on static (compile-time) global memory + on stack local memory
124 | 4.1 Read (right) overflow
125 | 4.2 Write (right) overflow
126 | 4.3 Read (left) underflow
127 | 4.4 Write (left) underflow
128 |
129 | OOB accesses on dynamic (kmalloc-ed) memory
130 | 5.1 Read (right) overflow
131 | 5.2 Write (right) overflow
132 | 5.3 Read (left) underflow
133 | 5.4 Write (left) underflow
134 |
135 | 6 Use After Free - UAF
136 | 7 Double-free
137 |
138 | UBSAN arithmetic UB testcases
139 | 8.1 add overflow
140 | 8.2 sub overflow
141 | 8.3 mul overflow
142 | 8.4 negate overflow
143 | 8.5 shift OOB
144 | 8.6 OOB
145 | 8.7 load invalid value
146 | 8.8 misaligned access
147 | 8.9 object size mismatch
148 |
149 | 9 copy_[to|from]_user*() tests
150 | 10 UMR on slab (SLUB) memory
151 |
152 | (Type in the testcase number to run): "
153 | read testcase
154 |
155 | # validate
156 | [ -z "${testcase}" ] && {
157 | echo "${name}: invalid testcase, can't be NULL"
158 | exit 1
159 | }
160 | MAX_TESTNUM=10
161 | pretend_int_tc=${testcase} # just to validate
162 | if [ ${#testcase} -eq 3 ]; then
163 | pretend_int_tc=${testcase::-2}
164 | elif [ ${#testcase} -eq 4 ]; then
165 | pretend_int_tc=${testcase::-3}
166 | fi
167 | if [ ${pretend_int_tc} -le 0 -o ${pretend_int_tc} -gt ${MAX_TESTNUM} ]; then
168 | echo "${name}: invalid testcase # (${testcase})"
169 | exit 1
170 | fi
171 | run_testcase ${testcase}
172 |
173 | else # non-interactive, run all !
174 |
175 | for testcase in 1 2 3.1 3.2 4.1 4.2 4.3 4.4 5.1 5.2 5.3 5.4 6 7 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 9 10
176 | do
177 | run_testcase ${testcase}
178 | done
179 |
180 | fi
181 |
182 | exit 0
183 |
--------------------------------------------------------------------------------
/ch7/oops_inirqv3/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile
2 | # ***************************************************************
3 | # This program is part of the source code released for the book
4 | # "Linux Kernel Debugging"
5 | # (c) Author: Kaiwan N Billimoria
6 | # Publisher: Packt
7 | # GitHub repository:
8 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
9 | #
10 | # ***************************************************************
11 | # Brief Description:
12 | # A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
13 | # the 'usual' targets (the build, install and clean), we incorporate targets to
14 | # do useful (and indeed required) stuff like:
15 | # - adhering to kernel coding style (indent+checkpatch)
16 | # - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
17 | # - two _dummy_ dynamic analysis targets (KASAN, LOCKDEP); just to remind you!
18 | # - a packaging (.tar.xz) target and
19 | # - a help target.
20 | #
21 | # To get started, just type:
22 | # make help
23 | #
24 | # For details on this so-called 'better' Makefile, please refer my earlier book
25 | # 'Linux Kernel Programming', Packt, Mar 2021, Ch 5 section 'A "better" Makefile
26 | # template for your kernel modules'.
27 |
28 | #------------------------------------------------------------------
29 | # Set FNAME_C to the kernel module name source filename (without .c)
30 | # This enables you to use this Makefile as a template; just update this variable!
31 | # As well, the MYDEBUG variable (see it below) can be set to 'y' or 'n' (no being
32 | # the default)
33 | FNAME_C := oops_inirqv3
34 | #------------------------------------------------------------------
35 |
36 | # To support cross-compiling for kernel modules:
37 | # For architecture (cpu) 'arch', invoke make as:
38 | # make ARCH= CROSS_COMPILE=
39 | # The KDIR var is set to a sample path below; you're expected to update it on
40 | # your box to the appropriate path to the kernel src tree for that arch.
41 | ifeq ($(ARCH),arm)
42 | # *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
43 | KDIR ?= ~/linux_rpi
44 | else ifeq ($(ARCH),arm64)
45 | # *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
46 | # tree on your box
47 | KDIR ?= ~/kernel/linux-5.4
48 | else ifeq ($(ARCH),powerpc)
49 | # *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
50 | KDIR ?= ~/kernel/linux-5.0
51 | else
52 | # 'KDIR' is the Linux 'kernel headers' package on your host system; this is
53 | # usually an x86_64, but could be anything, really (f.e. building directly
54 | # on a Raspberry Pi implies that it's the host)
55 | KDIR ?= /lib/modules/$(shell uname -r)/build
56 | endif
57 |
58 | # Compiler
59 | CC := $(CROSS_COMPILE)gcc
60 | #CC := $(CROSS_COMPILE)gcc-10
61 | #CC := clang
62 |
63 | PWD := $(shell pwd)
64 | obj-m += ${FNAME_C}.o
65 |
66 | #--- Debug or production mode?
67 | # Set the MYDEBUG variable accordingly to y/n resp.
68 | # (Actually, debug info is always going to be generated when you build the
69 | # module on a debug kernel, where CONFIG_DEBUG_INFO is defined, making this
70 | # setting of the ccflags-y (or EXTRA_CFLAGS) variable mostly redundant (besides
71 | # the -DDEBUG).
72 | # This simply helps us influence the build on a production kernel, forcing
73 | # generation of debug symbols, if so required. Also, realize that the DEBUG
74 | # macro is turned on by many CONFIG_*DEBUG* options; hence, we use a different
75 | # macro var name, MYDEBUG).
76 | MYDEBUG := n
77 | ifeq (${MYDEBUG}, y)
78 |
79 | # https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
80 | # EXTRA_CFLAGS deprecated; use ccflags-y
81 | ccflags-y += -DDEBUG -g -ggdb -gdwarf-4 -Wall -fno-omit-frame-pointer -fvar-tracking-assignments
82 | else
83 | INSTALL_MOD_STRIP := 1
84 | #ccflags-y += --strip-debug
85 | endif
86 | # We always keep the dynamic debug facility enabled; this allows us to turn
87 | # dynamically turn on/off debug printk's later... To disable it simply comment
88 | # out the following line
89 | ccflags-y += -DDYNAMIC_DEBUG_MODULE
90 |
91 | KMODDIR ?= /lib/modules/$(shell uname -r)
92 | STRIP := ${CROSS_COMPILE}strip
93 |
94 | # gcc-10 issue:
95 | #ccflags-y += $(call cc-option,--allow-store-data-races)
96 |
97 | all:
98 | @echo
99 | @echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y=${ccflags-y} ---'
100 | @${CC} --version|head -n1
101 | @echo
102 | make -C $(KDIR) M=$(PWD) modules
103 | $(shell [ "${MYDEBUG}" != "y" ] && ${STRIP} --strip-debug ./${FNAME_C}.ko)
104 | install:
105 | @echo
106 | @echo "--- installing ---"
107 | @echo " [First, invoking the 'make' ]"
108 | make
109 | @echo
110 | @echo " [Now for the 'sudo make install' ]"
111 | sudo make -C $(KDIR) M=$(PWD) modules_install
112 | @echo " [If !debug, stripping debug info from ${KMODDIR}/extra/${FNAME_C}.ko]"
113 | $(shell if [ "${MYDEBUG}" != "y" ]; then sudo ${STRIP} --strip-debug ${KMODDIR}/extra/${FNAME_C}.ko; fi)
114 | clean:
115 | @echo
116 | @echo "--- cleaning ---"
117 | @echo
118 | make -C $(KDIR) M=$(PWD) clean
119 | # from 'indent'
120 | rm -f *~
121 |
122 | # Any usermode programs to build? Insert the build target(s) here
123 |
124 | #--------------- More (useful) targets! -------------------------------
125 | INDENT := indent
126 |
127 | # code-style : "wrapper" target over the following kernel code style targets
128 | code-style:
129 | make indent
130 | make checkpatch
131 |
132 | # indent- "beautifies" C code - to conform to the the Linux kernel
133 | # coding style guidelines.
134 | # Note! original source file(s) is overwritten, so we back it up.
135 | indent:
136 | @echo
137 | @echo "--- applying kernel code style indentation with indent ---"
138 | @echo
139 | mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
140 | ${INDENT} -linux --line-length95 *.[chsS]
141 | # add source files as required
142 |
143 | # Detailed check on the source code styling / etc
144 | checkpatch:
145 | make clean
146 | @echo
147 | @echo "--- kernel code style check with checkpatch.pl ---"
148 | @echo
149 | $(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
150 | # add source files as required
151 |
152 | #--- Static Analysis
153 | # sa : "wrapper" target over the following kernel static analyzer targets
154 | sa:
155 | make sa_sparse
156 | make sa_gcc
157 | make sa_flawfinder
158 | make sa_cppcheck
159 |
160 | # static analysis with sparse
161 | sa_sparse:
162 | make clean
163 | @echo
164 | @echo "--- static analysis with sparse ---"
165 | @echo
166 | # if you feel it's too much, use C=1 instead
167 | # NOTE: deliberately IGNORING warnings from kernel headers!
168 | make -Wsparse-all C=2 CHECK="/usr/bin/sparse --os=linux --arch=$(ARCH)" -C $(KDIR) M=$(PWD) modules 2>&1 |egrep -v "^\./include/.*\.h|^\./arch/.*\.h"
169 |
170 | # static analysis with gcc
171 | sa_gcc:
172 | make clean
173 | @echo
174 | @echo "--- static analysis with gcc ---"
175 | @echo
176 | make W=1 -C $(KDIR) M=$(PWD) modules
177 |
178 | # static analysis with flawfinder
179 | sa_flawfinder:
180 | make clean
181 | @echo
182 | @echo "--- static analysis with flawfinder ---"
183 | @echo
184 | flawfinder *.[ch]
185 |
186 | # static analysis with cppcheck
187 | sa_cppcheck:
188 | make clean
189 | @echo
190 | @echo "--- static analysis with cppcheck ---"
191 | @echo
192 | cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
193 |
194 | # Packaging; just tar.xz as of now
195 | PKG_NAME := ${FNAME_C}
196 | tarxz-pkg:
197 | rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
198 | make clean
199 | @echo
200 | @echo "--- packaging ---"
201 | @echo
202 | tar caf ../${PKG_NAME}.tar.xz *
203 | ls -l ../${PKG_NAME}.tar.xz
204 | @echo '=== package created: ../$(PKG_NAME).tar.xz ==='
205 | @echo 'Tip: when extracting, to extract into a dir of the same name as the tar file,'
206 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
207 |
208 | help:
209 | @echo '=== Makefile Help : additional targets available ==='
210 | @echo
211 | @echo 'TIP: type make to show all valid targets'
212 | @echo
213 |
214 | @echo '--- 'usual' kernel LKM targets ---'
215 | @echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
216 | @echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default here: /lib/modules/$(shell uname -r)/)'
217 | @echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
218 |
219 | @echo
220 | @echo '--- kernel code style targets ---'
221 | @echo 'code-style : "wrapper" target over the following kernel code style targets'
222 | @echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
223 | @echo ' checkpatch : run the kernel code style checker tool on source file(s)'
224 |
225 | @echo
226 | @echo '--- kernel static analyzer targets ---'
227 | @echo 'sa : "wrapper" target over the following kernel static analyzer targets'
228 | @echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
229 | @echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
230 | @echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
231 | @echo ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
232 | @echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
233 |
234 | @echo
235 | @echo '--- kernel dynamic analysis targets ---'
236 | @echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
237 | @echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
238 | @echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
239 |
240 | @echo
241 | @echo '--- misc targets ---'
242 | @echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
243 | @echo ' Tip: when extracting, to extract into a dir of the same name as the tar file,'
244 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
245 | @echo 'help : this help target'
246 |
--------------------------------------------------------------------------------
/ch7/oops_inirqv3/oops_inirqv3.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch7/oops_inirqv3/oops_inirqv3.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 7: Oops! Interpreting the kernel bug diagnostic
12 | ****************************************************************
13 | * Brief Description:
14 | *
15 | * For details, please refer the book, Ch 7.
16 | */
17 | #define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include "../../convenient.h"
24 |
25 | MODULE_AUTHOR("");
26 | MODULE_DESCRIPTION("LKD book:ch7/oops_inirqv3: generates a kernel Oops! while in interrupt context");
27 | MODULE_LICENSE("Dual MIT/GPL");
28 | MODULE_VERSION("0.1");
29 |
30 | static struct irq_work irqwork;
31 |
32 | /*
33 | * This function runs in (hardirq) interrupt context
34 | */
35 | void irq_work(struct irq_work *irqwk)
36 | {
37 | int want_oops = 1;
38 |
39 | PRINT_CTX();
40 | if (!!want_oops) // okay, let's Oops in irq context!
41 | // a fatal hang can happen here!
42 | *(int *)0x100 = 'x';
43 | }
44 |
45 | static int __init try_oops_init(void)
46 | {
47 | init_irq_work(&irqwork, irq_work);
48 | irq_work_queue(&irqwork);
49 |
50 | return 0; /* success */
51 | }
52 |
53 | static void __exit try_oops_exit(void)
54 | {
55 | pr_info("Goodbye, from Oops in irq try v3\n");
56 | }
57 |
58 | module_init(try_oops_init);
59 | module_exit(try_oops_exit);
60 |
--------------------------------------------------------------------------------
/ch7/oops_tryv1/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile
2 | # ***************************************************************
3 | # This program is part of the source code released for the book
4 | # "Linux Kernel Debugging"
5 | # (c) Author: Kaiwan N Billimoria
6 | # Publisher: Packt
7 | # GitHub repository:
8 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
9 | #
10 | # ***************************************************************
11 | # Brief Description:
12 | # A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
13 | # the 'usual' targets (the build, install and clean), we incorporate targets to
14 | # do useful (and indeed required) stuff like:
15 | # - adhering to kernel coding style (indent+checkpatch)
16 | # - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
17 | # - two _dummy_ dynamic analysis targets (KASAN, LOCKDEP); just to remind you!
18 | # - a packaging (.tar.xz) target and
19 | # - a help target.
20 | #
21 | # To get started, just type:
22 | # make help
23 | #
24 | # For details on this so-called 'better' Makefile, please refer my earlier book
25 | # 'Linux Kernel Programming', Packt, Mar 2021, Ch 5 section 'A "better" Makefile
26 | # template for your kernel modules'.
27 |
28 | #------------------------------------------------------------------
29 | # Set FNAME_C to the kernel module name source filename (without .c)
30 | # This enables you to use this Makefile as a template; just update this variable!
31 | # As well, the MYDEBUG variable (see it below) can be set to 'y' or 'n' (no being
32 | # the default)
33 | FNAME_C := oops_tryv1
34 | #------------------------------------------------------------------
35 |
36 | # To support cross-compiling for kernel modules:
37 | # For architecture (cpu) 'arch', invoke make as:
38 | # make ARCH= CROSS_COMPILE=
39 | # The KDIR var is set to a sample path below; you're expected to update it on
40 | # your box to the appropriate path to the kernel src tree for that arch.
41 | ifeq ($(ARCH),arm)
42 | # *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
43 | KDIR ?= ~/rpi_work/kernel_rpi/linux
44 | else ifeq ($(ARCH),arm64)
45 | # *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
46 | # tree on your box
47 | KDIR ?= ~/kernel/linux-5.4
48 | else ifeq ($(ARCH),powerpc)
49 | # *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
50 | KDIR ?= ~/kernel/linux-5.0
51 | else
52 | # 'KDIR' is the Linux 'kernel headers' package on your host system; this is
53 | # usually an x86_64, but could be anything, really (f.e. building directly
54 | # on a Raspberry Pi implies that it's the host)
55 | KDIR ?= /lib/modules/$(shell uname -r)/build
56 | endif
57 |
58 | # Compiler
59 | CC := $(CROSS_COMPILE)gcc
60 | #CC := $(CROSS_COMPILE)gcc-10
61 | #CC := clang
62 |
63 | PWD := $(shell pwd)
64 | obj-m += ${FNAME_C}.o
65 |
66 | #--- Debug or production mode?
67 | # Set the MYDEBUG variable accordingly to y/n resp.
68 | # (Actually, debug info is always going to be generated when you build the
69 | # module on a debug kernel, where CONFIG_DEBUG_INFO is defined, making this
70 | # setting of the ccflags-y (or EXTRA_CFLAGS) variable mostly redundant (besides
71 | # the -DDEBUG).
72 | # This simply helps us influence the build on a production kernel, forcing
73 | # generation of debug symbols, if so required. Also, realize that the DEBUG
74 | # macro is turned on by many CONFIG_*DEBUG* options; hence, we use a different
75 | # macro var name, MYDEBUG).
76 | MYDEBUG := n
77 | ifeq (${MYDEBUG}, y)
78 |
79 | # https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
80 | # EXTRA_CFLAGS deprecated; use ccflags-y
81 | ccflags-y += -DDEBUG -g -ggdb -gdwarf-4 -Wall -fno-omit-frame-pointer -fvar-tracking-assignments
82 | else
83 | INSTALL_MOD_STRIP := 1
84 | #ccflags-y += --strip-debug
85 | endif
86 | # We always keep the dynamic debug facility enabled; this allows us to turn
87 | # dynamically turn on/off debug printk's later... To disable it simply comment
88 | # out the following line
89 | ccflags-y += -DDYNAMIC_DEBUG_MODULE
90 |
91 | KMODDIR ?= /lib/modules/$(shell uname -r)
92 | STRIP := ${CROSS_COMPILE}strip
93 |
94 | # gcc-10 issue:
95 | #ccflags-y += $(call cc-option,--allow-store-data-races)
96 |
97 | all:
98 | @echo
99 | @echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y=${ccflags-y} ---'
100 | @${CC} --version|head -n1
101 | @echo
102 | make -C $(KDIR) M=$(PWD) modules
103 | $(shell [ "${MYDEBUG}" != "y" ] && ${STRIP} --strip-debug ./${FNAME_C}.ko)
104 | install:
105 | @echo
106 | @echo "--- installing ---"
107 | @echo " [First, invoking the 'make' ]"
108 | make
109 | @echo
110 | @echo " [Now for the 'sudo make install' ]"
111 | sudo make -C $(KDIR) M=$(PWD) modules_install
112 | @echo " [If !debug, stripping debug info from ${KMODDIR}/extra/${FNAME_C}.ko]"
113 | $(shell if [ "${MYDEBUG}" != "y" ]; then sudo ${STRIP} --strip-debug ${KMODDIR}/extra/${FNAME_C}.ko; fi)
114 | clean:
115 | @echo
116 | @echo "--- cleaning ---"
117 | @echo
118 | make -C $(KDIR) M=$(PWD) clean
119 | # from 'indent'
120 | rm -f *~
121 |
122 | # Any usermode programs to build? Insert the build target(s) here
123 |
124 | #--------------- More (useful) targets! -------------------------------
125 | INDENT := indent
126 |
127 | # code-style : "wrapper" target over the following kernel code style targets
128 | code-style:
129 | make indent
130 | make checkpatch
131 |
132 | # indent- "beautifies" C code - to conform to the the Linux kernel
133 | # coding style guidelines.
134 | # Note! original source file(s) is overwritten, so we back it up.
135 | indent:
136 | @echo
137 | @echo "--- applying kernel code style indentation with indent ---"
138 | @echo
139 | mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
140 | ${INDENT} -linux --line-length95 *.[chsS]
141 | # add source files as required
142 |
143 | # Detailed check on the source code styling / etc
144 | checkpatch:
145 | make clean
146 | @echo
147 | @echo "--- kernel code style check with checkpatch.pl ---"
148 | @echo
149 | $(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
150 | # add source files as required
151 |
152 | #--- Static Analysis
153 | # sa : "wrapper" target over the following kernel static analyzer targets
154 | sa:
155 | make sa_sparse
156 | make sa_gcc
157 | make sa_flawfinder
158 | make sa_cppcheck
159 |
160 | # static analysis with sparse
161 | sa_sparse:
162 | make clean
163 | @echo
164 | @echo "--- static analysis with sparse ---"
165 | @echo
166 | # if you feel it's too much, use C=1 instead
167 | # NOTE: deliberately IGNORING warnings from kernel headers!
168 | make -Wsparse-all C=2 CHECK="/usr/bin/sparse --os=linux --arch=$(ARCH)" -C $(KDIR) M=$(PWD) modules 2>&1 |egrep -v "^\./include/.*\.h|^\./arch/.*\.h"
169 |
170 | # static analysis with gcc
171 | sa_gcc:
172 | make clean
173 | @echo
174 | @echo "--- static analysis with gcc ---"
175 | @echo
176 | make W=1 -C $(KDIR) M=$(PWD) modules
177 |
178 | # static analysis with flawfinder
179 | sa_flawfinder:
180 | make clean
181 | @echo
182 | @echo "--- static analysis with flawfinder ---"
183 | @echo
184 | flawfinder *.[ch]
185 |
186 | # static analysis with cppcheck
187 | sa_cppcheck:
188 | make clean
189 | @echo
190 | @echo "--- static analysis with cppcheck ---"
191 | @echo
192 | cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
193 |
194 | # Packaging; just tar.xz as of now
195 | PKG_NAME := ${FNAME_C}
196 | tarxz-pkg:
197 | rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
198 | make clean
199 | @echo
200 | @echo "--- packaging ---"
201 | @echo
202 | tar caf ../${PKG_NAME}.tar.xz *
203 | ls -l ../${PKG_NAME}.tar.xz
204 | @echo '=== package created: ../$(PKG_NAME).tar.xz ==='
205 | @echo 'Tip: when extracting, to extract into a dir of the same name as the tar file,'
206 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
207 |
208 | help:
209 | @echo '=== Makefile Help : additional targets available ==='
210 | @echo
211 | @echo 'TIP: type make to show all valid targets'
212 | @echo
213 |
214 | @echo '--- 'usual' kernel LKM targets ---'
215 | @echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
216 | @echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default here: /lib/modules/$(shell uname -r)/)'
217 | @echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
218 |
219 | @echo
220 | @echo '--- kernel code style targets ---'
221 | @echo 'code-style : "wrapper" target over the following kernel code style targets'
222 | @echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
223 | @echo ' checkpatch : run the kernel code style checker tool on source file(s)'
224 |
225 | @echo
226 | @echo '--- kernel static analyzer targets ---'
227 | @echo 'sa : "wrapper" target over the following kernel static analyzer targets'
228 | @echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
229 | @echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
230 | @echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
231 | @echo ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
232 | @echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
233 |
234 | @echo
235 | @echo '--- kernel dynamic analysis targets ---'
236 | @echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
237 | @echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
238 | @echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
239 |
240 | @echo
241 | @echo '--- misc targets ---'
242 | @echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
243 | @echo ' Tip: when extracting, to extract into a dir of the same name as the tar file,'
244 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
245 | @echo 'help : this help target'
246 |
--------------------------------------------------------------------------------
/ch7/oops_tryv1/oops_tryv1.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch7/oops_tryv1/oops_tryv1.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 7: Oops! Interpreting the kernel bug diagnostic
12 | ****************************************************************
13 | * Brief Description:
14 | *
15 | * For details, please refer the book, Ch 7.
16 | */
17 | #define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | MODULE_AUTHOR("");
24 | MODULE_DESCRIPTION("LKD book:ch7/oops_tryv1: generates a kernel Oops! a kernel bug");
25 | MODULE_LICENSE("Dual MIT/GPL");
26 | MODULE_VERSION("0.1");
27 |
28 | static bool try_reading;
29 | module_param(try_reading, bool, 0644);
30 | MODULE_PARM_DESC(try_reading,
31 | "Trigger an Oops-generating bug when reading from NULL; else, do so by writing to NULL");
32 |
33 | static int __init try_oops_init(void)
34 | {
35 | size_t val = 0x0;
36 |
37 | pr_info("Lets Oops!\nNow attempting to %s something %s the NULL address 0x%p\n",
38 | !!try_reading ? "read" : "write",
39 | !!try_reading ? "from" : "to", // pedantic, huh
40 | NULL);
41 | if (!!try_reading) {
42 | val = *(int *)0x0;
43 | /* Interesting! If we leave the code at this, the compiler actually optimizes
44 | * it away, as we're not working with the result of the read. This makes it
45 | * appear that the read does NOT cause an Oops; this ISN'T the case, it does,
46 | * of course.
47 | * So, to prove it, we try and printk the variable, thus forcing the compiler
48 | * to generate the code, and voila, we're rewarded with a nice Oops !
49 | */
50 | pr_info("val = 0x%zu\n", val);
51 | /*
52 | * How do we know that the size_t var requires the %zu format specifier?
53 | * These are important things to figure; pl see the kernel documentation
54 | * on this aspect here:
55 | * https://www.kernel.org/doc/Documentation/printk-formats.txt
56 | */
57 | } else // try writing to NULL
58 | *(int *)val = 'x';
59 |
60 | return 0; /* success */
61 | }
62 |
63 | static void __exit try_oops_exit(void)
64 | {
65 | pr_info("Goodbye, from Oops try v1\n");
66 | }
67 |
68 | module_init(try_oops_init);
69 | module_exit(try_oops_exit);
70 |
--------------------------------------------------------------------------------
/ch7/oops_tryv2/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile
2 | # ***************************************************************
3 | # This program is part of the source code released for the book
4 | # "Linux Kernel Debugging"
5 | # (c) Author: Kaiwan N Billimoria
6 | # Publisher: Packt
7 | # GitHub repository:
8 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
9 | #
10 | # ***************************************************************
11 | # Brief Description:
12 | # A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
13 | # the 'usual' targets (the build, install and clean), we incorporate targets to
14 | # do useful (and indeed required) stuff like:
15 | # - adhering to kernel coding style (indent+checkpatch)
16 | # - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
17 | # - two _dummy_ dynamic analysis targets (KASAN, LOCKDEP); just to remind you!
18 | # - a packaging (.tar.xz) target and
19 | # - a help target.
20 | #
21 | # To get started, just type:
22 | # make help
23 | #
24 | # For details on this so-called 'better' Makefile, please refer my earlier book
25 | # 'Linux Kernel Programming', Packt, Mar 2021, Ch 5 section 'A "better" Makefile
26 | # template for your kernel modules'.
27 |
28 | #------------------------------------------------------------------
29 | # Set FNAME_C to the kernel module name source filename (without .c)
30 | # This enables you to use this Makefile as a template; just update this variable!
31 | # As well, the MYDEBUG variable (see it below) can be set to 'y' or 'n' (no being
32 | # the default)
33 | FNAME_C := oops_tryv2
34 | #------------------------------------------------------------------
35 |
36 | # To support cross-compiling for kernel modules:
37 | # For architecture (cpu) 'arch', invoke make as:
38 | # make ARCH= CROSS_COMPILE=
39 | # The KDIR var is set to a sample path below; you're expected to update it on
40 | # your box to the appropriate path to the kernel src tree for that arch.
41 | ifeq ($(ARCH),arm)
42 | # *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
43 | KDIR ?= ~/rpi_work/kernel_rpi/linux
44 | else ifeq ($(ARCH),arm64)
45 | # *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
46 | # tree on your box
47 | KDIR ?= ~/kernel/linux-5.4
48 | else ifeq ($(ARCH),powerpc)
49 | # *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
50 | KDIR ?= ~/kernel/linux-5.0
51 | else
52 | # 'KDIR' is the Linux 'kernel headers' package on your host system; this is
53 | # usually an x86_64, but could be anything, really (f.e. building directly
54 | # on a Raspberry Pi implies that it's the host)
55 | KDIR ?= /lib/modules/$(shell uname -r)/build
56 | endif
57 |
58 | # Compiler
59 | CC := $(CROSS_COMPILE)gcc
60 | #CC := $(CROSS_COMPILE)gcc-10
61 | #CC := clang
62 |
63 | PWD := $(shell pwd)
64 | obj-m += ${FNAME_C}.o
65 |
66 | #--- Debug or production mode?
67 | # Set the MYDEBUG variable accordingly to y/n resp.
68 | # (Actually, debug info is always going to be generated when you build the
69 | # module on a debug kernel, where CONFIG_DEBUG_INFO is defined, making this
70 | # setting of the ccflags-y (or EXTRA_CFLAGS) variable mostly redundant (besides
71 | # the -DDEBUG).
72 | # This simply helps us influence the build on a production kernel, forcing
73 | # generation of debug symbols, if so required. Also, realize that the DEBUG
74 | # macro is turned on by many CONFIG_*DEBUG* options; hence, we use a different
75 | # macro var name, MYDEBUG).
76 | MYDEBUG := y
77 | ifeq (${MYDEBUG}, y)
78 |
79 | # https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
80 | # EXTRA_CFLAGS deprecated; use ccflags-y
81 | ccflags-y += -DDEBUG -g -ggdb -gdwarf-4 -Wall -fno-omit-frame-pointer -fvar-tracking-assignments
82 | else
83 | INSTALL_MOD_STRIP := 1
84 | ccflags-y += -UDEBUG
85 | endif
86 | # We always keep the dynamic debug facility enabled; this allows us to turn
87 | # dynamically turn on/off debug printk's later... To disable it simply comment
88 | # out the following line
89 | ccflags-y += -DDYNAMIC_DEBUG_MODULE
90 |
91 | KMODDIR ?= /lib/modules/$(shell uname -r)
92 | STRIP := ${CROSS_COMPILE}strip
93 |
94 | # gcc-10 issue:
95 | #ccflags-y += $(call cc-option,--allow-store-data-races)
96 |
97 | all:
98 | @echo
99 | @echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y=${ccflags-y} ---'
100 | @${CC} --version|head -n1
101 | @echo
102 | make -C $(KDIR) M=$(PWD) modules
103 | $(shell [ "${MYDEBUG}" != "y" ] && ${STRIP} --strip-debug ./${FNAME_C}.ko)
104 | install:
105 | @echo
106 | @echo "--- installing ---"
107 | @echo " [First, invoking the 'make' ]"
108 | make
109 | @echo
110 | @echo " [Now for the 'sudo make install' ]"
111 | sudo make -C $(KDIR) M=$(PWD) modules_install
112 | @echo " [If !debug, stripping debug info from ${KMODDIR}/extra/${FNAME_C}.ko]"
113 | $(shell if [ "${MYDEBUG}" != "y" ]; then sudo ${STRIP} --strip-debug ${KMODDIR}/extra/${FNAME_C}.ko; fi)
114 | clean:
115 | @echo
116 | @echo "--- cleaning ---"
117 | @echo
118 | make -C $(KDIR) M=$(PWD) clean
119 | # from 'indent'
120 | rm -f *~
121 |
122 | # Any usermode programs to build? Insert the build target(s) here
123 |
124 | #--------------- More (useful) targets! -------------------------------
125 | INDENT := indent
126 |
127 | # code-style : "wrapper" target over the following kernel code style targets
128 | code-style:
129 | make indent
130 | make checkpatch
131 |
132 | # indent- "beautifies" C code - to conform to the the Linux kernel
133 | # coding style guidelines.
134 | # Note! original source file(s) is overwritten, so we back it up.
135 | indent:
136 | @echo
137 | @echo "--- applying kernel code style indentation with indent ---"
138 | @echo
139 | mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
140 | ${INDENT} -linux --line-length95 *.[chsS]
141 | # add source files as required
142 |
143 | # Detailed check on the source code styling / etc
144 | checkpatch:
145 | make clean
146 | @echo
147 | @echo "--- kernel code style check with checkpatch.pl ---"
148 | @echo
149 | $(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
150 | # add source files as required
151 |
152 | #--- Static Analysis
153 | # sa : "wrapper" target over the following kernel static analyzer targets
154 | sa:
155 | make sa_sparse
156 | make sa_gcc
157 | make sa_flawfinder
158 | make sa_cppcheck
159 |
160 | # static analysis with sparse
161 | sa_sparse:
162 | make clean
163 | @echo
164 | @echo "--- static analysis with sparse ---"
165 | @echo
166 | # if you feel it's too much, use C=1 instead
167 | # NOTE: deliberately IGNORING warnings from kernel headers!
168 | make -Wsparse-all C=2 CHECK="/usr/bin/sparse --os=linux --arch=$(ARCH)" -C $(KDIR) M=$(PWD) modules 2>&1 |egrep -v "^\./include/.*\.h|^\./arch/.*\.h"
169 |
170 | # static analysis with gcc
171 | sa_gcc:
172 | make clean
173 | @echo
174 | @echo "--- static analysis with gcc ---"
175 | @echo
176 | make W=1 -C $(KDIR) M=$(PWD) modules
177 |
178 | # static analysis with flawfinder
179 | sa_flawfinder:
180 | make clean
181 | @echo
182 | @echo "--- static analysis with flawfinder ---"
183 | @echo
184 | flawfinder *.[ch]
185 |
186 | # static analysis with cppcheck
187 | sa_cppcheck:
188 | make clean
189 | @echo
190 | @echo "--- static analysis with cppcheck ---"
191 | @echo
192 | cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
193 |
194 | # Packaging; just tar.xz as of now
195 | PKG_NAME := ${FNAME_C}
196 | tarxz-pkg:
197 | rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
198 | make clean
199 | @echo
200 | @echo "--- packaging ---"
201 | @echo
202 | tar caf ../${PKG_NAME}.tar.xz *
203 | ls -l ../${PKG_NAME}.tar.xz
204 | @echo '=== package created: ../$(PKG_NAME).tar.xz ==='
205 | @echo 'Tip: when extracting, to extract into a dir of the same name as the tar file,'
206 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
207 |
208 | help:
209 | @echo '=== Makefile Help : additional targets available ==='
210 | @echo
211 | @echo 'TIP: type make to show all valid targets'
212 | @echo
213 |
214 | @echo '--- 'usual' kernel LKM targets ---'
215 | @echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
216 | @echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default here: /lib/modules/$(shell uname -r)/)'
217 | @echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
218 |
219 | @echo
220 | @echo '--- kernel code style targets ---'
221 | @echo 'code-style : "wrapper" target over the following kernel code style targets'
222 | @echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
223 | @echo ' checkpatch : run the kernel code style checker tool on source file(s)'
224 |
225 | @echo
226 | @echo '--- kernel static analyzer targets ---'
227 | @echo 'sa : "wrapper" target over the following kernel static analyzer targets'
228 | @echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
229 | @echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
230 | @echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
231 | @echo ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
232 | @echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
233 |
234 | @echo
235 | @echo '--- kernel dynamic analysis targets ---'
236 | @echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
237 | @echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
238 | @echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
239 |
240 | @echo
241 | @echo '--- misc targets ---'
242 | @echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
243 | @echo ' Tip: when extracting, to extract into a dir of the same name as the tar file,'
244 | @echo ' do: tar -xvf ${PKG_NAME}.tar.xz --one-top-level'
245 | @echo 'help : this help target'
246 |
--------------------------------------------------------------------------------
/ch7/oops_tryv2/oops_tryv2.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch7/oops_tryv2/oops_tryv2.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 7: Oops! Interpreting the kernel bug diagnostic
12 | ****************************************************************
13 | * Brief Description:
14 | *
15 | * For details, please refer the book, Ch 7.
16 | */
17 | #define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include "../../convenient.h"
27 |
28 | MODULE_AUTHOR("");
29 | MODULE_DESCRIPTION("LKD book:ch7/oops_tryv2: generates a kernel Oops! a kernel bug in different ways");
30 | MODULE_LICENSE("Dual MIT/GPL");
31 | MODULE_VERSION("0.1");
32 |
33 | static unsigned long mp_randaddr;
34 | module_param(mp_randaddr, ulong, 0644);
35 | MODULE_PARM_DESC(mp_randaddr,
36 | "Random non-zero kernel virtual address; deliberately invalid, to cause an Oops!");
37 |
38 | static bool bug_in_workq;
39 | module_param(bug_in_workq, bool, 0644);
40 | MODULE_PARM_DESC(bug_in_workq, "Trigger an Oops-generating bug in our workqueue function");
41 |
42 | static unsigned long bad_kva;
43 | static u64 t1, t2;
44 | static struct st_ctx {
45 | int x, y, z;
46 | struct work_struct work;
47 | u8 data;
48 | } *gctx, *oopsie;
49 |
50 | /*
51 | * Our workqueue callback function
52 | */
53 | static void do_the_work(struct work_struct *work)
54 | {
55 | struct st_ctx *priv = container_of(work, struct st_ctx, work);
56 |
57 | pr_info("In our workq function: data=%d\n", priv->data);
58 | t2 = ktime_get_real_ns();
59 | SHOW_DELTA(t2, t1);
60 | if (!!bug_in_workq) {
61 | pr_info("Generating Oops by attempting to write to an invalid kernel memory pointer\n");
62 | oopsie->data = 'x';
63 | }
64 | kfree(gctx);
65 | }
66 |
67 | static int setup_work(void)
68 | {
69 | gctx = kzalloc(sizeof(struct st_ctx), GFP_KERNEL);
70 | if (!gctx)
71 | return -ENOMEM;
72 | gctx->data = 'C';
73 |
74 | /* Initialize our workqueue */
75 | INIT_WORK(&gctx->work, do_the_work);
76 | // Do it!
77 | schedule_work(&gctx->work);
78 |
79 | return 0;
80 | }
81 |
82 | static int __init try_oops_init(void)
83 | {
84 | unsigned int page0_randptr = 0x0;
85 |
86 | if (!!bug_in_workq) {
87 | pr_info("Generating Oops via kernel bug in workqueue function\n");
88 | t1 = ktime_get_real_ns();
89 | setup_work();
90 | return 0;
91 | } else if (mp_randaddr) {
92 | pr_info("Generating Oops by attempting to write to the invalid kernel address passed\n");
93 | bad_kva = mp_randaddr;
94 | } else {
95 | pr_info("Generating Oops by attempting to write to a random invalid kernel address in NULL trap page\n");
96 | get_random_bytes(&page0_randptr, sizeof(unsigned int));
97 | bad_kva = (page0_randptr %= PAGE_SIZE);
98 | }
99 | pr_info("bad_kva = 0x%lx; now writing to it...\n", bad_kva);
100 | *(unsigned long *)bad_kva = 0xdead;
101 |
102 | return 0; /* success */
103 | }
104 |
105 | static void __exit try_oops_exit(void)
106 | {
107 | pr_info("Goodbye, from Oops try v2\n");
108 | }
109 |
110 | module_init(try_oops_init);
111 | module_exit(try_oops_exit);
112 |
--------------------------------------------------------------------------------
/ch8/kcsan_datarace/kcsan_datarace.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ch8/kcsan_datarace/kcsan_datarace.c
3 | ***************************************************************
4 | * This program is part of the source code released for the book
5 | * "Linux Kernel Debugging"
6 | * (c) Author: Kaiwan N Billimoria
7 | * Publisher: Packt
8 | * GitHub repository:
9 | * https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | *
11 | * From: Ch 8: Lock Debugging
12 | ****************************************************************
13 | * Brief Description:
14 | * Here, we deliberately generate a data race by issuing concurrent plain
15 | * writes on the same address; KCSAN should catch it! So, of course, we assume
16 | * you're running this on a KCSAN-enabled debug kernel.
17 | *
18 | * For details, please refer the book, Ch 8.
19 | */
20 | #define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include "../../convenient.h"
30 |
31 | MODULE_AUTHOR("");
32 | MODULE_DESCRIPTION("LKD book:ch8/kcsan_datarace: deliberately generates a data race; KCSAN should catch it!");
33 | MODULE_LICENSE("Dual MIT/GPL");
34 | MODULE_VERSION("0.1");
35 |
36 | static bool race_2plain_w;
37 | module_param(race_2plain_w, bool, 0644);
38 | MODULE_PARM_DESC(race_2plain_w, "Trigger a data race due to plain writes on same address");
39 |
40 | static int iter1 = 10000;
41 | module_param(iter1, int, 0644);
42 | MODULE_PARM_DESC(iter1, "# of times to loop in workfunc 1");
43 |
44 | static int iter2 = 10000;
45 | module_param(iter2, int, 0644);
46 | MODULE_PARM_DESC(iter2, "# of times to loop in workfunc 2");
47 |
48 | static struct st_ctx {
49 | struct work_struct work1, work2;
50 | u64 x, y, z, data;
51 | } *gctx; /* careful, pointers have no memory! */
52 |
53 | /*
54 | * Our workqueue callback function #1
55 | */
56 | static void do_the_work1(struct work_struct *work1)
57 | {
58 | int i;
59 | u64 bogus = 32000;
60 |
61 | PRINT_CTX();
62 | if (race_2plain_w) {
63 | pr_info("data race: 2 plain writes:\n");
64 | for (i=0; idata = bogus + i; /* unprotected plain write on global */
66 | }
67 | }
68 |
69 | /*
70 | * Our workqueue callback function #2
71 | */
72 | static void do_the_work2(struct work_struct *work2)
73 | {
74 | int i;
75 | u64 bogus = 98000;
76 |
77 | PRINT_CTX();
78 | if (race_2plain_w) {
79 | pr_info("data race: 2 plain writes:\n");
80 | for (i=0; idata = bogus - i; /* unprotected plain write on global */
82 | }
83 | }
84 |
85 | static int setup_work(void)
86 | {
87 | pr_info("global data item address: 0x%px\n", &gctx->data);
88 |
89 | /* Initialize our workqueue #1 */
90 | INIT_WORK(&gctx->work1, do_the_work1);
91 | schedule_work(&gctx->work1);
92 |
93 | /* Initialize our workqueue #2 */
94 | INIT_WORK(&gctx->work2, do_the_work2);
95 | schedule_work(&gctx->work2);
96 |
97 | return 0;
98 | }
99 |
100 | static int __init kcsan_datarace_init(void)
101 | {
102 | if (!race_2plain_w) {
103 | pr_info("nothing to do (you're expected to set the module param race_2plain_w to True!)\n");
104 | return -EINVAL;
105 | }
106 |
107 | gctx = kzalloc(sizeof(struct st_ctx), GFP_KERNEL);
108 | if (!gctx)
109 | return -ENOMEM;
110 |
111 | gctx->data = 1;
112 | pr_info("Setting up a deliberate data race via our workqueue functions:\n");
113 | if (race_2plain_w == 1)
114 | pr_info("2 plain writes; #loops in workfunc1:%d workfunc2:%d\n", iter1, iter2);
115 |
116 | setup_work();
117 | return 0; /* success */
118 | }
119 |
120 | static void __exit kcsan_datarace_exit(void)
121 | {
122 | kfree(gctx);
123 | pr_info("Goodbye\n");
124 | }
125 |
126 | module_init(kcsan_datarace_init);
127 | module_exit(kcsan_datarace_exit);
128 |
--------------------------------------------------------------------------------
/ch8/kcsan_datarace/tester.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # ch8/tester.sh
3 | # ***************************************************************
4 | # This program is part of the source code released for the book
5 | # "Linux Kernel Debugging"
6 | # (c) Author: Kaiwan N Billimoria
7 | # Publisher: Packt
8 | # GitHub repository:
9 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | #
11 | # From: Ch 8: Lock Debugging
12 | # ****************************************************************
13 | # A simple wrapper script to test (approx) how many times to minimally loop in
14 | # the 'racy' code within the kernel module, in order to trigger a data race
15 | # that KCSAN can catch!
16 | # TIP:
17 | # If you find that KCSAN's not catching the data race, try increasing the # of
18 | # loops to large values; f.e., invoke it like this:
19 | # ./tester.sh 1 75000 50000
20 | #
21 | # For details, please refer the book, Ch 8.
22 | name=$(basename $0)
23 | KCONF=/boot/config-$(uname -r)
24 | KMOD=kcsan_datarace
25 | MAX=10
26 | i=0
27 | SLEEP_TM=3.5 # why? This is as CONFIG_KCSAN_REPORT_ONCE_IN_MS=3000 by default
28 |
29 | # chkconf()
30 | # $1 : string decribing CONFIG_FOO to check
31 | # $1 : CONFIG_FOO to check
32 | chkconf()
33 | {
34 | [ $# -lt 2 ] && return
35 | echo -n "$1:"
36 | grep -q "$2=y" ${KCONF} && echo "enabled" || echo "disabled"
37 | }
38 |
39 | # Check for KCSAN support
40 | [ ! -f /sys/kernel/debug/kcsan ] && {
41 | echo "${name}: kernel requires KCSAN support"
42 | exit 1
43 | }
44 |
45 | # Check for KCSAN assuming plain writes are atomic being disabled
46 | tok=$(chkconf "KCSAN assume plain writes are atomic" CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC)
47 | tok2=$(echo ${tok} |awk -F: '{print $2}')
48 | if [ ${tok2} = "enabled" ]; then
49 | echo "${name}: the kernel config CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC is enabled; ideally, you should reconfigure the kernel,
50 | disabling it and then trying this out..."
51 | exit 1
52 | fi
53 |
54 | [ ! -f ${KMOD}.ko ] && {
55 | echo "${name}: module ${KMOD}.ko not built?"
56 | exit 1
57 | }
58 | [ $(id -u) -ne 0 ] && {
59 | echo "${name}: must run as root."
60 | exit 1
61 | }
62 | [ $# -ne 3 ] && {
63 | echo "${name} max-tries loops_in_func1 loops_in_func2"
64 | exit 1
65 | }
66 | MAX=$1
67 | iter1=$2
68 | iter2=$3
69 | rmmod ${KMOD} 2>/dev/null
70 |
71 | dmesg -C
72 | while [ $i -lt ${MAX} ]
73 | do
74 | echo "Trial ${i} -----------------------------------------"
75 | echo "Trial ${i} -----------------------------------------" > /dev/kmsg
76 | insmod ./${KMOD}.ko race_2plain_w=y iter1=${iter1} iter2=${iter2}
77 | rmmod ${KMOD}
78 | [ ${MAX} -gt 1 ] && sleep ${SLEEP_TM}
79 | let i=i+1
80 | done
81 |
--------------------------------------------------------------------------------
/ch9/ftrace/ftrace_common.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # ch9/ftrace/ftrace_common.sh
3 | # ***************************************************************
4 | # This program is part of the source code released for the book
5 | # "Linux Kernel Debugging"
6 | # (c) Author: Kaiwan N Billimoria
7 | # Publisher: Packt
8 | # GitHub repository:
9 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | #
11 | # From: Ch 9: Tracing the kernel flow
12 | #***************************************************************
13 | # Brief Description:
14 | #
15 | # For details, please refer the book, Ch 9.
16 | #------------------------------------------------------------------------------
17 |
18 | die()
19 | {
20 | echo "$@" 1>&2
21 | exit 1
22 | }
23 |
24 | reset_ftrace()
25 | {
26 | local f
27 |
28 | # Check: if trace-cmd is installed, use it to reset
29 | # But it doesn't auto reset everything we want, so let the other stuff also get reset
30 | if which trace-cmd >/dev/null ; then
31 | echo "trace-cmd reset (patience, pl...)"
32 | trace-cmd reset
33 | fi
34 |
35 | echo 1408 > buffer_size_kb # 1408 KB is the default (5.10)
36 |
37 | # wipe out any existing trace data
38 | echo > trace
39 |
40 | # Tip: do NOT attempt to reset tracing_cpumask by 'echo > tracing_cpumask';
41 | # Causes trace to fail... as a value of 0x0 as cpu bitmask effectively disables
42 | # tracing!
43 |
44 | for f in set_ftrace_filter set_ftrace_notrace set_ftrace_notrace_pid set_ftrace_pid
45 | do
46 | echo "resetting $f"
47 | echo > $f
48 | done
49 |
50 | # trace_options to defaults (as of 5.10.60)
51 | echo "resetting trace_options to defaults (as of 5.10.60)"
52 | echo print-parent > trace_options
53 | echo nosym-offset > trace_options
54 | echo nosym-addr > trace_options
55 | echo noverbose > trace_options
56 | echo noraw > trace_options
57 | echo nohex > trace_options
58 | echo nobin > trace_options
59 | echo noblock > trace_options
60 | echo trace_printk > trace_options
61 | echo annotate > trace_options
62 | echo nouserstacktrace > trace_options
63 | echo nosym-userobj > trace_options
64 | echo noprintk-msg-only > trace_options
65 | echo context-info > trace_options
66 | echo nolatency-format > trace_options
67 | echo record-cmd > trace_options
68 | echo norecord-tgid > trace_options
69 | echo overwrite > trace_options
70 | echo nodisable_on_free > trace_options
71 | echo irq-info > trace_options
72 | echo markers > trace_options
73 | echo noevent-fork > trace_options
74 | echo nopause-on-trace > trace_options
75 | echo function-trace > trace_options
76 | echo nofunction-fork > trace_options
77 | echo nodisplay-graph > trace_options
78 | echo nostacktrace > trace_options
79 | #echo notest_nop_accept > trace_options
80 | #echo notest_nop_refuse > trace_options
81 |
82 | # options/funcgraph-* to defaults
83 | echo "resetting options/funcgraph-*"
84 | echo 0 > options/funcgraph-abstime
85 | echo 1 > options/funcgraph-cpu
86 | echo 1 > options/funcgraph-duration
87 | echo 1 > options/funcgraph-irqs
88 | echo 1 > options/funcgraph-overhead
89 | echo 0 > options/funcgraph-overrun
90 | echo 0 > options/funcgraph-proc
91 | echo 0 > options/funcgraph-tail
92 |
93 | echo 0 > max_graph_depth
94 |
95 | # perf-tools ftrace reset script
96 | f=$(which reset-ftrace-perf)
97 | [ ! -z "$f" ] && {
98 | echo "running '$f -q' now..."
99 | $f -q
100 | }
101 |
102 | # IMP / TIP
103 | # Tracing is ON after reset! turn it Off until we're good and ready
104 | #echo "tracing : " ; cat tracing_on
105 | echo 0 > tracing_on
106 | # plus, ensure the trace buffer is empty
107 | echo > trace
108 | }
109 |
110 | runcmd()
111 | {
112 | [ $# -eq 0 ] && return
113 | echo "$@"
114 | eval "$@"
115 | }
116 |
--------------------------------------------------------------------------------
/ch9/ftrace/ftrc_1s.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # ch9/ftrace/ftrc_1s.sh
3 | # ***************************************************************
4 | # This program is part of the source code released for the book
5 | # "Linux Kernel Debugging"
6 | # (c) Author: Kaiwan N Billimoria
7 | # Publisher: Packt
8 | # GitHub repository:
9 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | #
11 | # From: Ch 9: Tracing the kernel flow
12 | #***************************************************************
13 | # Brief Description:
14 | # Very simple (raw) usage of kernel ftrace; traces whatever executes within
15 | # the kernel for 1 second.
16 | #
17 | # For details, please refer the book, Ch 9.
18 | #------------------------------------------------------------------------------
19 | name=$(basename $0)
20 | [ $(id -u) -ne 0 ] && {
21 | echo "${name}: needs root."
22 | exit 1
23 | }
24 | source $(dirname $0)/ftrace_common.sh || {
25 | echo "Couldn't source required file $(dirname $0)/ftrace_common.sh"
26 | exit 1
27 | }
28 | REPDIR=~/ftrace_reports
29 | FTRC_REP=${REPDIR}/${name}_$(date +%Y%m%d_%H%M%S).txt
30 |
31 | cd /sys/kernel/tracing
32 | reset_ftrace
33 |
34 | grep -q -w function_graph available_tracers || die "tracer specified function_graph unavailable"
35 | echo function_graph > current_tracer || die "setting function_graph plugin failed"
36 | echo 1 > options/funcgraph-proc
37 | echo 1 > options/latency-format
38 |
39 | echo "Tracing with function_graph for 1s ..."
40 | echo 1 > tracing_on ; sleep 1 ; echo 0 > tracing_on
41 | mkdir -p ${REPDIR} 2>/dev/null
42 | cp trace ${FTRC_REP}
43 | ls -lh ${FTRC_REP}
44 | exit 0
45 |
--------------------------------------------------------------------------------
/ch9/ftrace/runner:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # runner
3 |
4 | # A wrapper to run a given command. Will exec the given command upon a trigger.
5 | # This is only to ensure we know the PID of the process in advance.
6 | # (Leveraged by our ftrace scripts).
7 | name=$(basename $0)
8 |
9 | # Keep these vars in sync with the 'ping_ftrace.sh' script!
10 | TRIGGER_FILE=/tmp/${name}
11 | CPUMASK=2
12 |
13 | if [ $# -eq 0 ]; then
14 | echo "usage: ${name} command"
15 | exit 1
16 | fi
17 |
18 | while [ ! -f ${TRIGGER_FILE} ]; do
19 | sleep .5
20 | done
21 | echo "> ${name}:$$: triggered"
22 | # Exec the given command on CPU 1
23 | exec taskset ${CPUMASK} "$@"
24 |
--------------------------------------------------------------------------------
/ch9/lttng/lttng_trc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # lttng_trc.sh
3 | #
4 | # A simple frontend to using the powerful kernel-space LTTng tracer.
5 | # Note: this script does only the (auto) kernel-space tracing, usermode tracing
6 | # has to be setup by appropriately instrumenting the app w/ tracepoints.
7 | #
8 | # Once this tracing is done, run the TraceCompass GUI, open and analyze
9 | # the trace.
10 | #
11 | # TODO:
12 | # - event filters
13 | # - time traced
14 | name=$(basename $0)
15 |
16 | die()
17 | {
18 | echo >&2 " *** Fatal ***
19 | $@"
20 | exit 1
21 | }
22 |
23 | SESSION=lttng
24 | TRC_OUT_DIR=/tmp
25 |
26 | trap 'finish_trace' INT QUIT
27 |
28 | finish_trace()
29 | {
30 | echo "[+] cleaning up..."
31 | USR=$(who |head -n1 |awk '{print $1}')
32 | chown -R ${USR}:${USR} ${TRC_OUT_DIR}
33 | echo "${name}: done. Trace files in ${TRC_OUT_DIR} ; size: "
34 | du -ms ${TRC_OUT_DIR}
35 | lttng destroy
36 |
37 | echo " [+] ...generating compressed tar file of trace now, pl wait ..."
38 | TARBALL=${SESSION}.tar
39 | tar -cf ${TARBALL} ${TRC_OUT_DIR}/
40 | gzip -9 ${TARBALL}
41 | ls -lh ${TARBALL}*
42 | }
43 |
44 | #--- "main" here
45 | which lttng >/dev/null || die "lttng not installed?"
46 | [ $(id -u) -ne 0 ] && die "need to be root."
47 |
48 | [ $# -lt 2 ] && {
49 | echo "Usage: ${name} session-name program-to-trace-with-LTTng|0
50 | 1st parameter: name of the session
51 | 2nd parameter, ...:
52 | If '0' is passed, we just do a trace of the entire system (all kevents),
53 | else we do a trace of the particular process (all kevents).
54 | Eg. sudo ./${name} ps_trace ps -LA
55 | [NOTE: other stuff running _also_ gets traced (this is non-exclusive)]."
56 | exit 3
57 | }
58 |
59 | echo "Session name :: \"$1\""
60 | echo -n "[+] (Minimal) Checking for LTTng support ... "
61 | lsmod |grep -q "^lttng_" || die "lttng kernel modules not seen"
62 | echo "[OK]"
63 |
64 | # 1. Set up a recording session
65 | # TODO - put the full name instead of just $1;
66 | # but, will need to replace spaces with an '_'
67 | SESSION=${SESSION}_$1_$(date +%d%b%y_%H%M)
68 | TRC_OUT_DIR=${TRC_OUT_DIR}/${SESSION}
69 | echo "[+] lttng create ${SESSION} --output=${TRC_OUT_DIR}"
70 | lttng create ${SESSION} --output=${TRC_OUT_DIR} || die "lttng create failed"
71 |
72 | # 2. Set up kernel events to record; simplistic: record all
73 | # WARNING! Big trace files with ALL kernel events
74 | echo "[+] lttng enable events ..."
75 | lttng enable-event --kernel --all
76 | # userspace tracef() [??]
77 | lttng enable-event --userspace 'lttng_ust_tracef:*'
78 |
79 | # 3. Perform the trace
80 | if [ "$2" = "0" ]; then
81 | echo "@@@ ${name}: Tracing system now ... press [Enter] or ^C to stop! @@@"
82 | date ; date +%s.%N # timestamp
83 | lttng start
84 | read
85 | else
86 | #echo "params: num=$# val=$@"
87 | shift # $1 is the session name; $2 onward is the program to execute
88 | echo "@@@ ${name}: Tracing \"$@\" now ... @@@"
89 | date ; date +%s.%N # timestamp
90 | lttng start ; eval "$@" ; lttng stop
91 | date ; date +%s.%N # timestamp
92 | fi
93 |
94 | date ; date +%s.%N # timestamp
95 | # 4. Stop recording, destroy the session, ...
96 | finish_trace
97 | exit 0
98 |
--------------------------------------------------------------------------------
/ch9/tracecmd/trace-cmd-wrapper-readme.txt:
--------------------------------------------------------------------------------
1 | A convenience wrapper script over the useful trace-cmd(1) utility can be
2 | obtained by cloning this repo:
3 |
4 | https://github.com/kaiwan/trccmd
5 |
6 | Do try it out!
7 |
--------------------------------------------------------------------------------
/ch9/tracecmd/trc-cmd2-mod.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # ch9/trace-cmd/trc-cmd1.sh
3 | # Simple wrapper over trace-cmd; same as trc-cmd1.sh except that here,
4 | # we trace only the e1000 (network driver) module!
5 | [ $# -ne 1 ] && {
6 | echo "Usage: $0 report-file"
7 | exit 1
8 | }
9 | die() {
10 | echo $@ >&2
11 | exit 1
12 | }
13 | KMOD=e1000
14 |
15 | runcmd()
16 | {
17 | [ $# -eq 0 ] && return
18 | echo "$@"
19 | eval "$@"
20 | }
21 | rep=$1
22 |
23 | lsmod|grep -w ${KMOD} || die "Module ${KMOD} isn't loaded"
24 | runcmd sudo trace-cmd record -q -p function_graph -e net -e sock -e skb -e tcp -e udp \
25 | --module ${KMOD} -F ping -c1 packtpub.com
26 |
27 | [ -f $1 ] && mv -f $1 $1.bkp
28 | runcmd sudo trace-cmd report -q -l > $1
29 | # Typically, the report file is now pretty tiny (~ 4 to 5 KB in my tests)
30 | # as ONLY the $KMOD related module/kernel functions show up in the trace.
31 |
--------------------------------------------------------------------------------
/ch9/tracecmd/trccmd_1ping.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # ch9/trace-cmd/trc-cmd1.sh
3 | # Simple wrapper over trace-cmd : trace doing a single ping
4 | usage() {
5 | echo "Usage: $0 option
6 | option = -f : show in function_graph format
7 | option = -p : show the parameters"
8 | exit 1
9 | }
10 | runcmd()
11 | {
12 | [ $# -eq 0 ] && return
13 | echo "$@"
14 | eval "$@"
15 | }
16 |
17 | #--- 'main'
18 | [ $(id -u) -ne 0 ] && {
19 | echo "$0: needs root."
20 | usage
21 | exit 1
22 | }
23 | [ $# -ne 1 ] && {
24 | usage; exit 1
25 | }
26 |
27 | SHOW_FUNCGRAPH=0
28 | SHOW_PARAMS=0
29 | case "$1" in
30 | -f ) SHOW_FUNCGRAPH=1 ;;
31 | -p ) SHOW_PARAMS=1 ;;
32 | *) usage ; exit 1 ;;
33 | esac
34 | REP=ping_trccmd.txt
35 | cmd="ping -c1 packtpub.com"
36 | events="-e net -e sock -e skb -e tcp -e udp"
37 |
38 | #--- RECORD
39 | if [ ${SHOW_FUNCGRAPH} -eq 1 ] ; then
40 | runcmd trace-cmd record -p function_graph ${events} -F ${cmd}
41 | elif [ ${SHOW_PARAMS} -eq 1 ] ; then
42 | runcmd trace-cmd record ${events} -F ${cmd}
43 | fi
44 |
45 | #--- REPORT
46 | [ -f ${REP} ] && mv -f ${REP} ${REP}.bkp
47 | trace-cmd report -l > ${REP}
48 | # -l : show 'latency-format' columns
49 | # Typically, the report file is still fairly large (~ 9 to 10 MB in my tests)
50 | # as all related kernel functions show up in the trace.
51 |
52 | exit 0
53 |
--------------------------------------------------------------------------------
/dwarves_1.17-1_amd64.deb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Linux-Kernel-Debugging/65c1fff06f879722da1854e47b37c8fe3cfaf65e/dwarves_1.17-1_amd64.deb
--------------------------------------------------------------------------------
/lkm:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # lkm : a silly kernel module dev - build, load, unload - helper wrapper script
3 | # License: MIT
4 | unset ARCH
5 | unset CROSS_COMPILE
6 | name=$(basename "${0}")
7 |
8 | #-------------- r u n c m d -------------------------------------------
9 | # Display and run the provided command.
10 | # Parameter(s): the command to run
11 | runcmd()
12 | {
13 | local SEP="------------------------------"
14 | [ $# -eq 0 ] && return
15 | echo "${SEP}
16 | $*
17 | ${SEP}"
18 | eval "$@"
19 | [ $? -ne 0 ] && echo " ^--[FAILED]"
20 | }
21 |
22 | ### "main" here
23 |
24 | [ $# -ne 1 ] && {
25 | echo "Usage: ${name} name-of-kernel-module-file (without the .c)"
26 | exit 1
27 | }
28 | [[ "${1}" = *"."* ]] && {
29 | echo "Usage: ${name} name-of-kernel-module-file ONLY (do NOT put any extension)."
30 | exit 1
31 | }
32 |
33 | echo "Version info:"
34 | which lsb_release >/dev/null 2>&1 && {
35 | echo -n "Distro: "
36 | lsb_release -a 2>/dev/null |grep "Description" |awk -F':' '{print $2}'
37 | }
38 | echo -n "Kernel: " ; uname -r
39 |
40 | runcmd "sudo rmmod $1 2> /dev/null"
41 | #runcmd "make clean"
42 | runcmd "sudo dmesg -C"
43 | runcmd "make || exit 1"
44 | # TODO - the '|| exit 1' does not seem to work
45 |
46 | [ ! -f "$1".ko ] && {
47 | echo "[!] ${name}: $1.ko has not been built, aborting..."
48 | exit 1
49 | }
50 |
51 | runcmd "sudo insmod ./$1.ko && lsmod|grep $1"
52 | # Ubuntu 20.10 onward has enabled CONFIG_SECURITY_DMESG_RESTRICT ! That's good for security
53 | # So we need to 'sudo' dmesg; thanks to @gregbuchholz for pointing this out
54 | runcmd "sudo dmesg"
55 | exit 0
56 |
--------------------------------------------------------------------------------
/pkg_install4ubuntu_lkd.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # pkg_install4ubuntu_lkd.sh
3 | # ***************************************************************
4 | # This program is part of the source code released for the book
5 | # "Linux Kernel Debugging"
6 | # (c) Author: Kaiwan N Billimoria
7 | # Publisher: Packt
8 | # GitHub repository:
9 | # https://github.com/PacktPublishing/Linux-Kernel-Debugging
10 | #***************************************************************
11 | # Brief Description:
12 | # Helper script to install all required packages (as well as a few more
13 | # possibly) for the Linux Kernel Debugging book.
14 |
15 | function die
16 | {
17 | echo >&2 "$@"
18 | exit 1
19 | }
20 | runcmd()
21 | {
22 | [ $# -eq 0 ] && return
23 | echo "$@"
24 | eval "$@" || die "failed"
25 | }
26 |
27 | runcmd sudo apt update
28 |
29 | # packages typically required for kernel build
30 | runcmd sudo apt install -y bison flex libncurses5-dev ncurses-dev xz-utils libssl-dev libelf-dev util-linux tar
31 |
32 | # other packages...
33 | runcmd sudo apt install -y bc bpfcc-tools bsdmainutils clang cmake cppcheck cscope curl \
34 | dwarves exuberant-ctags fakeroot flawfinder git gnome-system-monitor gnuplot \
35 | hwloc indent kernelshark libnuma-dev libjson-c-dev linux-tools-$(uname -r) \
36 | net-tools numactl openjdk-16-jre openssh-server perf-tools-unstable psmisc \
37 | python3-distutils rt-tests smem sparse stress sysfsutils tldr-py trace-cmd \
38 | tree tuna virt-what
39 |
40 | # Add yourself to the vboxsf group (to gain access to VirtualBox shared folders);
41 | # will require you to log out and back in (or even reboot) to take effect
42 | groups |grep -q -w vboxsf || runcmd sudo usermod -G vboxsf -a ${USER}
43 |
44 | exit 0
45 |
--------------------------------------------------------------------------------
/updtMakefile2better:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # updtMakefile2better
3 | SRC=~/Linux-Kernel-Debugging
4 | ORIG=${SRC}/Makefile.better
5 |
6 | [ $# -ne 1 ] && {
7 | echo "usage: $0 "
8 | exit 1
9 | }
10 | make clean
11 | mv -f Makefile Makefile.old 2>/dev/null
12 | cp -f ${ORIG} Makefile
13 |
14 | fname=$(echo "${1}" | sed -e 's/[]$.*[\^]/\\&/g' )
15 | sed -i -e "s/FNAME_C := .*/FNAME_C := ${fname}/" Makefile
16 | grep "^FNAME_C" Makefile
17 |
18 | TEST1_BUILDIT=1
19 | if [ ${TEST1_BUILDIT} -eq 1 ] ; then
20 | echo "--------------------------------------------------------"
21 | make
22 | ls -lh
23 | make clean
24 | fi
25 | TEST2_RUNIT=0
26 | if [ ${TEST2_RUNIT} -eq 1 ] ; then
27 | echo "--------------------------------------------------------"
28 | ${SRC}/lkm ${fname}
29 | sudo rmmod ${fname}
30 | sleep 1
31 | make clean
32 | ls -lh
33 | fi
34 |
--------------------------------------------------------------------------------
/updtMakefile2better_ALL:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Recurse through all src dirs, cleanup, set the correct 'better' Makefile!
3 |
4 | TOP=$(pwd)
5 | i=1
6 | for lkmdir in $(find . -type d)
7 | do
8 | cd ${TOP}
9 | nm=$(dirname ${lkmdir} |cut -d/ -f2)
10 |
11 | # skip these...
12 | [ "${nm}" = "tmp" -o "${nm}" = ".git" -o "${nm}" = "./.git" -o "${nm}" = "bkp" ] && continue
13 | #echo "lkmdir=${lkmdir}"
14 |
15 | # ok, process
16 | cd ${lkmdir} || {
17 | echo "cd failed! ??"
18 | continue
19 | }
20 | [ ! -f Makefile ] && continue
21 | echo "$i. ============================================================================="
22 | pwd
23 | echo "================================================================================"
24 |
25 | # get C source filename
26 | numc=$(ls *.c|wc -w)
27 | [ ${numc} -le 0 -o ${numc} -gt 1 ] && {
28 | echo "None -or- >1 C source files here, skipping dir..."
29 | continue
30 | }
31 | csrc=$(ls *.c)
32 | #echo "csrc=${csrc} nm=${csrc::-2}"
33 | ${TOP}/updtMakefile2better ${csrc::-2}
34 |
35 | let i=i+1
36 | done
37 | exit 0
38 |
--------------------------------------------------------------------------------
/xplore_fs:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # xplore_fs.sh
3 | #
4 | # This is a simple script to help you explore the content of any filesystem!
5 | # Typical use case: to recursively read the ASCII text content of the (pseudo)files
6 | # under pseudo-filesystems like procfs and sysfs!
7 | #
8 | # Tune the MAXDEPTH variable below to control your descent into fs purgatory :)
9 | #
10 | # Author: Kaiwan N Billimoria, kaiwanTECH.
11 | # License: MIT
12 |
13 | name=$(basename $0)
14 | #PFX=$(dirname `which $0`)
15 | #source ${PFX}/common.sh || {
16 | # echo "${name}: fatal: could not source ${PFX}/common.sh , aborting..."
17 | # exit 1
18 | #}
19 |
20 | MAX_SZ=3000000 # 3 MB #100
21 | MAX_LINES=25000 #500
22 | FANCY_MODE=0 #1
23 |
24 | #-------------Color stuff--------------------------------------
25 | #--- Foreground Colors
26 | fg_black() { tput setaf 0
27 | }
28 | fg_red() { tput setaf 1
29 | }
30 | fg_green() { tput setaf 2
31 | }
32 | fg_yellow() { tput setaf 3
33 | }
34 | fg_blue() { tput setaf 4
35 | }
36 | fg_magenta() { tput setaf 5
37 | }
38 | fg_cyan() { tput setaf 6
39 | }
40 | fg_white() { tput setaf 7
41 | }
42 | fg_grey() { tput setaf 8
43 | }
44 |
45 | #--- Background Colors
46 | bg_white() { tput setab 7
47 | }
48 | bg_red() { tput setab 1
49 | }
50 | bg_green() { tput setab 2
51 | }
52 | bg_yellow() { tput setab 3
53 | }
54 | bg_blue() { tput setab 4
55 | }
56 | bg_cyan() { tput setab 6
57 | }
58 |
59 | # Reset text attributes to normal without clearing screen.
60 | color_reset()
61 | {
62 | tput sgr0
63 | }
64 | #--------------------------------------------------------------
65 |
66 | # Parameters
67 | # $1 : regular text file's pathname - to display contents of
68 | display_file()
69 | {
70 | [ $# -eq 0 ] && return
71 |
72 | # Check limits
73 | local sz=$(ls -l $1|awk '{print $5}')
74 | #pr_sz_human "sz" ${sz}
75 |
76 | [ ${FANCY_MODE} -eq 1 ] && tput bold
77 | ls -lh $1
78 |
79 | [ ${sz} -gt ${MAX_SZ} ] && {
80 | printf "\n *** ***\n" ${MAX_SZ}
81 | return
82 | }
83 | local numln=$(wc -l $1|awk '{print $1}')
84 | [ ${numln} -gt 1 ] && printf "${numln} lines\n"
85 | [ ${FANCY_MODE} -eq 1 ] && color_reset
86 | [ ${numln} -gt ${MAX_LINES} ] && {
87 | printf "\n *** ***\n" ${MAX_LINES}
88 | return
89 | }
90 |
91 | printf "%s\n" "${SEP}"
92 | [ -f $1 -a -r $1 ] && cat $1 # display file content
93 | } # end display_file()
94 |
95 | # TODO : add an option [--no-fancy] to turn off the 'fancy colors' stuff
96 | # Useful for saving o/p to a regular file.
97 | # use getopt
98 | usage()
99 | {
100 | echo "Usage: ${name} start-dir [max-folder-depth-to-traverse]
101 |
102 | Displays the names of all files starting from the folder 'start-dir';
103 | if a regular file And it's readable And it's size and number if lines lies
104 | within the preconfigured 'max', it's content is displayed.
105 |
106 | The 'max-folder-depth-to-traverse' parameter is optional; passing it
107 | constrains the folder depth of the search [default=4]."
108 | exit 1
109 | } # end usage()
110 |
111 |
112 | ###--- "main" here
113 | STARTDIR=/sys/devices
114 | name=$(basename $0)
115 | SEP="--------------------------------------------------------------------------"
116 | MAXDEPTH=4
117 |
118 | [ $# -lt 1 -o "$1" = "-h" -o "$1" = "--help" ] && usage
119 | [ ! -d $1 ] && {
120 | echo "${name}: '$1' invalid folder. Aborting..."
121 | exit 1
122 | }
123 | [ ! -r $1 ] && {
124 | echo "${name}: '$1' not read-able, re-run this utility as root (with sudo)? Aborting..."
125 | exit 1
126 | }
127 | STARTDIR=$1
128 | regex='^[0-9]+$'
129 | [ $# -eq 2 ] && {
130 | if [[ ! $2 =~ ${regex} ]] ; then
131 | echo "${name}: 2nd parameter 'maxdepth' must be a positive integer"
132 | exit 1
133 | fi
134 | MAXDEPTH=$2
135 | }
136 |
137 | SHOW_SUMMARY=1
138 | if [ ${SHOW_SUMMARY} -eq 1 ]; then
139 | echo "===================== SUMMARY LIST of Files =========================="
140 | echo
141 | echo "Note: Max Depth is ${MAXDEPTH}."
142 | echo
143 | find -L ${STARTDIR} -xdev -maxdepth ${MAXDEPTH} # multi-level find; more info..
144 | echo
145 | echo
146 | fi
147 | printf "%s\n" ${SEP}
148 |
149 | ######### the main loop ################
150 | #for sysfile in $(find ${STARTDIR}) # 1-level find; simpler..
151 | for sysfile in $(find -L ${STARTDIR} -xdev -maxdepth ${MAXDEPTH}) # multi-level find; more info..
152 | do
153 | #tput bold 2>/dev/null
154 | #fg_white ; bg_blue
155 | printf "%-60s " ${sysfile}
156 | case $(file --mime-type --brief ${sysfile}) in
157 | inode/symlink) #techo ": " ; continue ;;
158 | printf ": \n" ; continue ;;
159 | inode/directory) printf ": \n" ; continue ;;
160 | inode/socket) printf ": \n" ; continue ;;
161 | inode/chardevice) printf ": \n" ; continue ;;
162 | inode/blockdevice) printf ": \n" ; continue ;;
163 | application/x-sharedlib|*zlib) printf ": \n" ; continue ;;
164 | application/zip|application/x-xz) printf ": \n" ; continue ;;
165 | # Text files
166 | text/plain|text/*)
167 | [ ${FANCY_MODE} -eq 1 ] && {
168 | color_reset ; tput bold 2>/dev/null ; fg_red #; bg_white
169 | }
170 | printf ": "
171 | [ ${FANCY_MODE} -eq 1 ] && color_reset
172 | printf "\n"
173 | [ -f ${sysfile} -a -r ${sysfile} ] && display_file ${sysfile}
174 | ;;
175 | # procfs files
176 | inode/x-empty) # usually the case for procfs (pseudo)'files'
177 | [ ${FANCY_MODE} -eq 1 ] && {
178 | color_reset ; tput bold 2>/dev/null
179 | }
180 | firstdir="/$(echo "${sysfile}" |cut -d"/" -f2)"
181 | #echo "firstdir=${firstdir}"
182 | [ "${firstdir}"="/proc" ] && {
183 | [ ${FANCY_MODE} -eq 1 ] && fg_magenta #; bg_white
184 | printf "\n"
185 | [ -f ${sysfile} -a -r ${sysfile} ] && display_file ${sysfile}
186 | #display_file ${sysfile}
187 | }
188 | [ ${FANCY_MODE} -eq 1 ] && color_reset
189 | ;;
190 | *) #fg_white ; bg_blue
191 | printf ": <-other->\n"
192 | ls -l ${sysfile} 2>/dev/null
193 | #color_reset
194 | continue ;;
195 | esac
196 | printf "%s\n" ${SEP}
197 | [ ${FANCY_MODE} -eq 1 ] && color_reset
198 | done
199 | ################
200 | exit 0
201 |
--------------------------------------------------------------------------------