├── .gitignore ├── LICENSE ├── README.md ├── ch1 ├── kconfig_dbg01 └── kconfig_prod01 ├── ch10 ├── cause_oops_panic.sh ├── kthread_stuck │ ├── Makefile │ └── kthread_stuck.c ├── letspanic │ ├── Makefile │ └── letspanic.c ├── netcon ├── panic_notifier │ ├── Makefile │ └── panic_notifier_lkm.c └── workq_stall │ ├── Makefile │ └── workq_stall.c ├── ch11 ├── .gitattributes ├── README.txt ├── gdbline.sh ├── kconfig_x86-64_target ├── kgdb_try │ ├── Makefile │ └── kgdb_try.c ├── rootfs_deb.img.7z └── run_target.sh ├── ch3 ├── dyndbg.sh ├── miscdrv_rdwr │ ├── Makefile │ ├── miscdrv_rdwr.c │ └── rdwr_test_secret.c ├── printk_loglevels │ ├── Makefile │ └── printk_loglevels.c └── ratelimit_test │ ├── Makefile │ └── ratelimit_test.c ├── ch4 └── kprobes │ ├── 1_kprobe │ ├── 1_kprobe.c │ ├── Makefile │ ├── run │ └── test.sh │ ├── 2_kprobe │ ├── 2_kprobe.c │ ├── Makefile │ ├── run │ └── test.sh │ ├── 3_kprobe │ ├── 3_kprobe.c │ ├── Makefile │ ├── run │ └── test.sh │ └── 4_kprobe_helper │ ├── Readme.txt │ ├── common.sh │ ├── err_common.sh │ ├── helper_kp.c │ └── kp_load.sh ├── ch5 └── kmembugs_test │ ├── Makefile │ ├── debugfs_kmembugs.c │ ├── kmembugs_test.c │ ├── load_testmod │ └── run_tests ├── ch7 ├── oops_inirqv3 │ ├── Makefile │ └── oops_inirqv3.c ├── oops_tryv1 │ ├── Makefile │ └── oops_tryv1.c └── oops_tryv2 │ ├── Makefile │ └── oops_tryv2.c ├── ch8 └── kcsan_datarace │ ├── Makefile │ ├── kcsan_datarace.c │ └── tester.sh ├── ch9 ├── ftrace │ ├── ftrace_common.sh │ ├── ftrc_1s.sh │ ├── ping_ftrace.sh │ ├── ping_ftrace_report.txt │ ├── ping_ftrace_set_event_report.txt │ └── runner ├── lttng │ └── lttng_trc.sh └── tracecmd │ ├── trace-cmd-wrapper-readme.txt │ ├── trc-cmd2-mod.sh │ └── trccmd_1ping.sh ├── convenient.h ├── dwarves_1.17-1_amd64.deb ├── lkm ├── pkg_install4ubuntu_lkd.sh ├── updtMakefile2better ├── updtMakefile2better_ALL └── xplore_fs /.gitignore: -------------------------------------------------------------------------------- 1 | # LKD book GitHub repo 2 | rootfs_deb.img.7z 3 | .*.swp 4 | *.bkp 5 | bkp/ 6 | *.patch 7 | *.dat 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Linux Kernel Debugging 5 | 6 | Book Name 7 | 8 | This is the code repository for [Linux Kernel Debugging](https://www.packtpub.com/product/linux-kernel-debugging/9781801075039), published by Packt. 9 | 10 | **Leverage proven tools and advanced techniques to effectively debug Linux kernels and kernel modules** 11 | 12 | ## What is this book about? 13 | Linux Kernel Debugging is a comprehensive guide to learning all about advanced kernel debugging. This book covers many areas in depth, such as instrumentation-based debugging techniques (printk and the dynamic debug framework), and shows you how to use Kprobes. Memory-related bugs tend to be a nightmare – two chapters are packed with tools and techniques devoted to debugging them. When the kernel gifts you an Oops, how exactly do you interpret it to be able to debug the underlying issue? We’ve got you covered. Concurrency tends to be an inherently complex topic, so a chapter on lock debugging will help you to learn precisely what data races are, including using KCSAN to detect them. Some thorny issues, both debug- and performance-wise, require detailed kernel-level tracing; you’ll learn to wield the impressive power of Ftrace and its frontends. You’ll also discover how to handle kernel lockups, hangs, and the dreaded kernel panic, as well as leverage the venerable GDB tool within the kernel (KGDB), along with much more. 14 | 15 | This book covers the following exciting features: 16 | * Explore instrumentation-based printk along with the powerful dynamic debug framework 17 | * Use static and dynamic Kprobes to trap into kernel/module functions 18 | * Catch kernel memory defects with KASAN, UBSAN, SLUB debug, and kmemleak 19 | * Understand data races and use KCSAN to catch evasive concurrency defects 20 | * Leverage Ftrace and trace-cmd to trace the kernel flow in great detail 21 | * Use KGDB to single-step and debug kernel/module source code 22 | 23 | If you feel this book is for you, get your [copy](https://www.amazon.com/Linux-Kernel-Debugging-techniques-effectively-ebook/dp/B09TTD3358) today! 24 | 25 | https://www.packtpub.com/ 26 | 27 | ## Instructions and Navigations 28 | All of the code is organized into folders. For example, ch5. 29 | 30 | The code will look like the following: 31 | ``` 32 | static int handler_pre(struct kprobe *p, struct pt_regs *regs) 33 | { 34 | PRINT_CTX(); // uses pr_debug() 35 | 36 | spin_lock(&lock); 37 | tm_start = ktime_get_real_ns(); 38 | spin_unlock(&lock); 39 | 40 | return 0; 41 | } 42 | ``` 43 | 44 | **Following is what you need for this book:** 45 | This book is for Linux kernel developers, module/driver authors, and testers interested in debugging and enhancing their Linux systems at the level of the kernel. System administrators who want to understand and debug the internal infrastructure of their Linux kernels will also find this book useful. A good grasp on C programming and the Linux command line is necessary. Some experience with kernel (module) development will help you follow along. 46 | 47 | With the following software and hardware list you can run all code files present in the book (Chapter 1-12). 48 | 49 | ### Software and Hardware List 50 | 51 | | Chapter | Software required | OS required | 52 | | -------- | ----------------------------------| -----------------------------------| 53 | | 1-12 | Oracle VirtualBox | Windows, Mac OS X, and Linux (Any) | 54 | | 1-12 | Ubuntu 21.04 or 21.10 LTS | Windows, Mac OS X, and Linux (Any) | 55 | | 1-12 | Visual Studio Code | Windows, Mac OS X, and Linux (Any) | 56 | 57 | 58 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://packt.link/2zUIX). 59 | 60 | ### Known Errata 61 | - PDF pg 74: 62 | "For the size_t and ssize_t typedefs (which represent signed and unsigned integers respectively)..." should be: 63 | "For the size_t and ssize_t typedefs (which represent unsigned and signed integers respectively)..." 64 | 65 | - PDF pg 127: 66 | It says: "... We need to interpret the PINT_CTX() 67 | macro's output." 68 | 69 | It should be: "... We need to interpret the PRINT_CTX() 70 | macro's output." 71 | 72 | - PDF pg 137: 73 | The URL 74 | https://elixir.bootlin.com/linux/v5.10.60/source/arch/arm/include/asm/ptrace.h#L135 75 | 76 | should be 77 | https://elixir.bootlin.com/linux/v5.10.60/source/arch/arm/include/asm/ptrace.h#L15 78 | 79 | (same goes for the code comments) 80 | 81 | - PDF pg 235: 82 | Broken link: fixed updated link is: 83 | https://www.kernel.org/doc/html/latest/mm/slub.html 84 | 85 | - PDF pg 236: 86 | Broken link: fixed updated link is: 87 | https://www.kernel.org/doc/html/latest/mm/slub.html#emergency-operations 88 | 89 | - PDF pg 262: 90 | Broken link: fixed updated link is: 91 | https://elixir.bootlin.com/linux/v5.10.60/source/drivers/net/ethernet/cadence/macb_main.c#L3578 92 | 93 | - PDF pg 283: 94 | Broken link: fixed updated link is: 95 | https://lore.kernel.org/lkml/1420845382-25815-1-git-send-email-khoroshilov@ispras.ru/ 96 | 97 | - PDF pg 287: 98 | Broken link: fixed updated link is: 99 | https://docs.kernel.org/mm/slub.html#short-users-guide-for-slub 100 | 101 | - PDF pg 384: 102 | Broken link: fixed updated link is: 103 | https://www.mail-archive.com/git-commits-head@vger.kernel.org/msg18392.html 104 | (removed the incorrect mailto: prefix) 105 | 106 | - PDF pg 509: 107 | It says: "... In other words, automatically." 108 | It should say: "... In other words, atomically." 109 | 110 | - (PDF) pg 585 (as well as other pages): 111 | We refer to "syzkaller" and "syzbot" as the same thing. 112 | A reader, Javier Carrasco Cruz, points out that they aren't the same: "... syzbot is a higher-level automation on top of syzkaller that runs multiple instances of syzkaller, keeps bug tracking, and provides a webUI... The link https://groups.google.com/g/syzkaller/c/O9rWGc824eU/m/2DKWhBLkCgAJ goes into more details." Many thanks to you Javier for pointing this out! 113 | 114 | 115 | ### UPDATES / Observations 116 | 117 | - PDF pg 43: 118 | An update to the Ch 01, *Further Reading* section: 119 | One more good article on the Boeing 737 MAX disaster: 120 | [The Boeing 737 MAX Saga: Lessons for Software Organizations, PHILLIP JOHNSTON AND ROZI HARRIS, Embedded Artistry, 2019](https://c2y6x2t8.rocketcdn.me/wp-content/uploads/2019/09/the-boeing-737-max-saga-lessons-for-software-organizations.pdf) 121 | 122 | - PDF pg 126: 123 | 124 | As of now (early 2023), attempting to trace file open's via the `do_sys_open()` doesn't seem to cut it... 125 | I find that instead using the `do_sys_openat2()` works! 126 | So, substitute this function in place of the `do_sys_open()` being used and you may get better results... 127 | (In fact, our Figure 4.13 shows the `do_sys_openat2()` being invoked!). 128 | 129 | 130 | 131 | ### Related products 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 | --------------------------------------------------------------------------------