diff --git a/.checkpatch-camelcase.git. b/.checkpatch-camelcase.git. new file mode 100644 index 0000000..e69de29 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..961acec --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +# clang-format configuration for Linux kernel code +# Based on Linux kernel coding style +--- +BasedOnStyle: LLVM +IndentWidth: 8 +UseTab: Always +TabWidth: 8 +BreakBeforeBraces: Linux +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +IndentCaseLabels: false +SortIncludes: false +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: false +AlignTrailingComments: false +ColumnLimit: 80 +MaxEmptyLinesToKeep: 1 +KeepEmptyLinesAtTheStartOfBlocks: false +SpaceAfterCStyleCast: false +PointerAlignment: Right diff --git a/.cppcheck-suppressions b/.cppcheck-suppressions new file mode 100644 index 0000000..64cd289 --- /dev/null +++ b/.cppcheck-suppressions @@ -0,0 +1,13 @@ +# Cppcheck suppressions for kernel module code +# Suppress warnings that are false positives or not applicable to kernel code + +# Kernel-specific suppressions +missingIncludeSystem +unusedFunction:*test*.c +unmatchedSuppression + +# Suppress preprocessor errors in kernel headers (configuration issues) +preprocessorErrorDirective:*/linux/bitops.h +preprocessorErrorDirective:*/linux/*.h +noValidConfiguration:src/elf_det.c +noValidConfiguration:src/elf_det.mod.c diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index cf641fc..481e573 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -25,6 +25,9 @@ RUN apt-get update && apt-get install -y \ bc \ libelf-dev \ libssl-dev \ + clang-format \ + sparse \ + cppcheck \ && rm -rf /var/lib/apt/lists/* # Ensure ubuntu user exists and has sudo privileges diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 46f84a3..6d0ca4c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,7 +13,7 @@ "source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/ubuntu/.ssh,type=bind,consistency=cached" ], "features": {}, - "postCreateCommand": "bash -lc 'make --version && gcc --version'", + "postCreateCommand": "bash -c 'echo \"Setting up development environment...\" && make --version && gcc --version && clang-format --version && echo \"Installing Git hooks...\" && mkdir -p .git/hooks && if [ -f scripts/pre-commit.sh ]; then cp scripts/pre-commit.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit && echo \"✓ Git pre-commit hook installed\"; fi && echo \"✓ Dev environment ready!\"'", "customizations": { "vscode": { "extensions": [ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ed4d96a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,31 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +# Kernel C source files +[*.{c,h}] +indent_style = tab +indent_size = 8 +trim_trailing_whitespace = true + +# Makefile +[Makefile] +indent_style = tab + +# YAML files +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +# Markdown files +[*.md] +trim_trailing_whitespace = false +indent_style = space +indent_size = 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d4472d..fa8773f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,10 +45,51 @@ jobs: name: kernel-module-build path: build/ retention-days: 30 + + static-analysis: + name: Static Analysis and Code Quality + runs-on: ubuntu-24.04 + + steps: + - name: Checkout code + uses: actions/checkout@v4 - - name: Static analysis with sparse (if available) + - name: Install analysis tools run: | - sudo apt-get install -y sparse || true - make clean - make module C=1 || echo "Sparse analysis completed with warnings" + sudo apt-get update + sudo apt-get install -y \ + clang-format \ + cppcheck \ + sparse \ + linux-headers-$(uname -r) + + - name: Check code formatting + run: make format-check + + - name: Run checkpatch (kernel coding style) + run: make checkpatch continue-on-error: true + + - name: Run cppcheck + run: make cppcheck + continue-on-error: true + + - name: Run sparse analyzer + run: make sparse + continue-on-error: true + + unit-tests: + name: Unit Tests + runs-on: ubuntu-24.04 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y build-essential gcc + + - name: Run unit tests + run: make unit diff --git a/.gitignore b/.gitignore index cfb1f31..cc8b1ad 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,9 @@ core.* # QEMU testing environment scripts/qemu-env/ *~ + +# Static analysis output +*.log +cppcheck-build/ +scan-build-*/ +compile_commands.json diff --git a/Makefile b/Makefile index 0677ccb..c087153 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ SRC_DIR := src # Build directory for user program BUILD_DIR := build -.PHONY: all clean module user install uninstall test help unit +.PHONY: all clean module user install uninstall test help unit check format checkpatch sparse cppcheck # Default target all: module user @@ -77,17 +77,114 @@ clean: rm -rf $(SRC_DIR)/.tmp_versions @echo "Clean complete!" +# Static Analysis and Code Quality Checks + +# Run all code quality checks +check: checkpatch sparse cppcheck + @echo "" + @echo "================================================" + @echo "All static analysis checks completed!" + @echo "================================================" + +# Check kernel coding style with checkpatch.pl +checkpatch: + @echo "Running checkpatch.pl (kernel coding style)..." + @if [ -f /lib/modules/$(shell uname -r)/build/scripts/checkpatch.pl ]; then \ + for file in $(SRC_DIR)/*.c $(SRC_DIR)/*.h; do \ + case "$$file" in \ + *.mod.c) ;; \ + *) if [ -f "$$file" ]; then \ + echo "Checking $$file..."; \ + /lib/modules/$(shell uname -r)/build/scripts/checkpatch.pl --no-tree --strict --file $$file || true; \ + fi ;; \ + esac; \ + done; \ + else \ + echo "checkpatch.pl not found. Install kernel sources."; \ + fi + +# Run sparse static analyzer +sparse: + @echo "Running sparse static analyzer..." + @if command -v sparse >/dev/null 2>&1; then \ + $(MAKE) -C $(KDIR) M=$(PWD)/$(SRC_DIR) C=2 CF="-D__CHECK_ENDIAN__" modules || true; \ + else \ + echo "sparse not found. Install with: sudo apt-get install sparse"; \ + fi + +# Run cppcheck static analyzer +cppcheck: + @echo "Running cppcheck static analyzer..." + @if command -v cppcheck >/dev/null 2>&1; then \ + cppcheck --enable=all --inconclusive --force \ + --suppressions-list=.cppcheck-suppressions \ + --inline-suppr \ + -I$(SRC_DIR) \ + -I/lib/modules/$(shell uname -r)/build/include \ + --error-exitcode=0 \ + $(SRC_DIR)/*.c 2>&1 | grep -v "Cppcheck cannot find" || true; \ + else \ + echo "cppcheck not found. Install with: sudo apt-get install cppcheck"; \ + fi + +# Format code with clang-format +format: + @echo "Formatting code with clang-format..." + @if command -v clang-format >/dev/null 2>&1; then \ + for file in $(SRC_DIR)/*.c $(SRC_DIR)/*.h; do \ + if [ -f "$$file" ]; then \ + echo "Formatting $$file..."; \ + clang-format -i $$file; \ + fi \ + done; \ + echo "Code formatting complete!"; \ + else \ + echo "clang-format not found. Install with: sudo apt-get install clang-format"; \ + fi + +# Check if code is properly formatted (CI-friendly) +format-check: + @echo "Checking code formatting..." + @if command -v clang-format >/dev/null 2>&1; then \ + UNFORMATTED=$$(for file in $(SRC_DIR)/*.c $(SRC_DIR)/*.h; do \ + if [ -f "$$file" ]; then \ + clang-format -output-replacements-xml $$file | grep -q " 0) { + /* Comment style: C89 */ + return param * 2; + } + return 0; +} +``` + +**Development Workflow:** + +```bash +# Everything is ready - just start coding! + +# Format code before committing +make format + +# Run all checks +make check + +# Commit (hooks run automatically) +git commit -m "Your message" +``` ## Troubleshooting @@ -324,6 +570,13 @@ Contributions, issues, and feature requests are welcome! ## Changelog +### Version 1.1 +- Integrated static analysis tools (clang-format, sparse, cppcheck, checkpatch) +- Automated Git pre-commit hooks for code quality +- Dev container with zero-configuration setup +- Enhanced CI/CD pipeline with static analysis checks +- Comprehensive code quality documentation + ### Version 1.0 - Initial release with basic process information extraction - Support for CPU usage, memory layout, and ELF sections diff --git a/scripts/README.md b/scripts/README.md index 08ddc4b..e52699d 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,13 +1,18 @@ -# QEMU Testing Scripts +# Testing Scripts -Scripts for safely testing the Linux Process Information Kernel Module in an isolated QEMU virtual machine. +Scripts for testing the Linux Process Information Kernel Module in isolated QEMU virtual machines. ## Overview -These scripts provide a complete QEMU-based testing environment that isolates kernel module testing from your host system. This is the recommended approach for testing kernel modules as it prevents potential system crashes or instability from affecting your development machine. +This directory contains scripts for: +- **QEMU Testing**: Safely test kernel modules in isolated virtual machines + +**Note**: All development should be done in the dev container, which includes pre-configured static analysis tools and Git hooks. ## Quick Start +### QEMU Testing + ```bash # 1. Setup QEMU environment (one-time setup) ./scripts/qemu-setup.sh @@ -21,7 +26,9 @@ These scripts provide a complete QEMU-based testing environment that isolates ke ## Script Descriptions -### `qemu-setup.sh` +### QEMU Testing Scripts + +#### `qemu-setup.sh` - Downloads Ubuntu 24.04 cloud image - Creates cloud-init configuration - Sets up VM with kernel headers pre-installed @@ -32,7 +39,7 @@ These scripts provide a complete QEMU-based testing environment that isolates ke - `qemu-img` - `cloud-localds` (from cloud-image-utils package) -### `qemu-run.sh` +#### `qemu-run.sh` - Starts the QEMU VM with appropriate settings - Forwards SSH port 2222 to VM's port 22 - Allocates 2GB RAM and 2 CPU cores @@ -43,7 +50,7 @@ These scripts provide a complete QEMU-based testing environment that isolates ke - Password: `ubuntu` - Exit QEMU: Press `Ctrl+A` then `X` -### `qemu-test.sh` +#### `qemu-test.sh` - Automated end-to-end testing script (run from host) - Builds kernel module and user program locally - Copies files to VM via SCP @@ -55,11 +62,59 @@ These scripts provide a complete QEMU-based testing environment that isolates ke - VM must be running (`qemu-run.sh`) - SSH must be accessible on port 2222 -### `quick-reference.sh` +#### `quick-reference.sh` - Quick reference guide for common QEMU commands - Display usage: `./scripts/quick-reference.sh` - Includes setup, testing, and troubleshooting commands +### Static Analysis + +**Note**: All tools and hooks are automatically installed and configured in the dev container. + +#### `pre-commit.sh` +- Git pre-commit hook script (automatically installed in dev container) +- Runs formatting, cppcheck, and checkpatch on staged files +- Can be bypassed with `git commit --no-verify` if needed + +**Checks Performed:** +- Code formatting compliance (clang-format) +- Cppcheck static analysis +- Kernel coding style (checkpatch) + +## Code Quality Workflow + +All tools are pre-configured in the dev container: + +```bash +make format # Format code +make check # Run all checks +git commit # Hooks run automatically +``` + +### During Development +```bash +# Format code before committing +make format + +# Run all static analysis checks +make check + +# Individual checks +make checkpatch # Kernel coding style +make sparse # Kernel static analysis +make cppcheck # General C/C++ analysis +``` + +### Pre-Commit +If Git hooks are installed, checks run automatically: +```bash +git commit -m "Your commit message" +# Hooks run automatically and will fail commit if issues found + +# To skip hooks (use sparingly) +git commit --no-verify -m "Your commit message" +``` + ## Manual Testing If you prefer manual testing: diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh new file mode 100755 index 0000000..865b312 --- /dev/null +++ b/scripts/pre-commit.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# Pre-commit hook to run code quality checks +# Automatically installed in dev container on startup + +set -e + +echo "Running pre-commit checks..." +echo "" + +# Check if analysis tools are installed +check_tool() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Warning: $1 not found. Please use the dev container for development." + return 1 + fi + return 0 +} + +# Array to track failures +FAILED=0 + +# Run clang-format check +if check_tool clang-format; then + echo "Checking code formatting..." + if ! make format-check >/dev/null 2>&1; then + echo "❌ Code formatting check failed!" + echo " Run 'make format' to fix formatting issues" + FAILED=1 + else + echo "✓ Code formatting check passed" + fi +else + echo "⚠ Skipping format check (clang-format not installed)" +fi + +echo "" + +# Run cppcheck +if check_tool cppcheck; then + echo "Running cppcheck..." + if make cppcheck 2>&1 | grep -q "error:"; then + echo "⚠ Cppcheck found potential issues" + FAILED=1 + else + echo "✓ Cppcheck passed" + fi +else + echo "⚠ Skipping cppcheck (not installed)" +fi + +echo "" + +# Run checkpatch if available +if [ -f /lib/modules/$(uname -r)/build/scripts/checkpatch.pl ]; then + echo "Running checkpatch..." + # Get list of staged .c and .h files + STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(c|h)$' || true) + + if [ -n "$STAGED_FILES" ]; then + for file in $STAGED_FILES; do + if [ -f "$file" ]; then + if /lib/modules/$(uname -r)/build/scripts/checkpatch.pl --no-tree --file "$file" 2>&1 | grep -q "ERROR:"; then + echo "⚠ Checkpatch found errors in $file" + FAILED=1 + fi + fi + done + if [ $FAILED -eq 0 ]; then + echo "✓ Checkpatch passed" + fi + fi +else + echo "⚠ Skipping checkpatch (kernel sources not found)" +fi + +echo "" +echo "================================================" + +if [ $FAILED -eq 1 ]; then + echo "❌ Pre-commit checks failed!" + echo "" + echo "Fix the issues or use 'git commit --no-verify' to skip checks" + exit 1 +else + echo "✓ All pre-commit checks passed!" +fi + +exit 0 diff --git a/src/elf_det.c b/src/elf_det.c index 7cf921d..82cfb01 100644 --- a/src/elf_det.c +++ b/src/elf_det.c @@ -1,4 +1,5 @@ -#include +// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +#include #include //for module programming #include //for task_struct #include //for file_operations write and read @@ -6,173 +7,200 @@ #include #include //for using proc #include // for using seq operations -#include //for using file_operations +#include //for using file_operations #include //for using vm_area struct -#include //for mm_struct and VMA access -#include //for user to kernel and vice versa access +#include //for mm_struct and VMA access +#include //for user to kernel and vice versa access #include //for string libs #include //for task iteration #include //for task_cputime #include "elf_helpers.h" -MODULE_LICENSE("Dual BSD/GPL"); //module license +MODULE_LICENSE("Dual BSD/GPL"); // module license +static char buff[20] = + "1"; // the common(global) buffer between kernel and user space +static int user_pid; // the desired pid that we get from user +static int number_opens; // number of opens(writes) to the pid file -static char buff[20]="1"; //the common(global) buffer between kernel and user space -static int user_pid; //the desired pid that we get from user -static int numberOpens = 0; //number of opens(writes) to the pid file - -//skip these instances (will be described bellow) +// skip these instances (will be described bellow) static struct proc_dir_entry *elfdet_dir, *elfdet_det_entry, *elfdet_pid_entry; static int procfile_open(struct inode *inode, struct file *file); -static ssize_t procfile_read(struct file*, char*, size_t, loff_t*); -static ssize_t procfile_write(struct file*, const char*, size_t, loff_t*); - +static ssize_t procfile_read(struct file *, char __user *, size_t, loff_t *); +static ssize_t procfile_write(struct file *, const char __user *, size_t, + loff_t *); -//det proc file_operations starts +// det proc file_operations starts -//this function is the base function to gather information from kernel -static int elfdet_show(struct seq_file *m, void *v) { +// this function is the base function to gather information from kernel +static int elfdet_show(struct seq_file *m, void *v) +{ struct task_struct *task; unsigned long bss_start = 0, bss_end = 0; unsigned long elf_header = 0; u64 delta_ns, total_ns; u64 usage_permyriad; // CPU usage in hundredths of a percent (X.XX%) + int ret; + + ret = kstrtoint(buff, 10, &user_pid); + if (ret != 0) { + seq_puts(m, "Failed to parse PID\n"); + return 0; + } - - sscanf(buff, "%d", &user_pid); //type cast pid from user(buff) to integer - task = pid_task(find_vpid(user_pid), PIDTYPE_PID); //get the task from pid + task = pid_task(find_vpid(user_pid), PIDTYPE_PID); if (!task || !task->mm) { - seq_printf(m, "Invalid PID or process has no memory context\n"); + seq_puts(m, "Invalid PID or process has no memory context\n"); return 0; } - /* CPU usage: total CPU time of task since start divided by elapsed wall time */ + /* CPU usage: total CPU time of task since start divided by elapsed wall + * time + */ total_ns = (u64)task->utime + (u64)task->stime; delta_ns = ktime_get_ns() - task->start_time; usage_permyriad = compute_usage_permyriad(total_ns, delta_ns); - + // Access VMA using VMA iterator for kernel 6.8+ if (mmap_read_lock_killable(task->mm)) { - seq_printf(m, "Failed to lock mm\n"); + seq_puts(m, "Failed to lock mm\n"); return 0; } /* Use mm fields directly for ELF and BSS */ elf_header = task->mm->start_code; - compute_bss_range(task->mm->end_data, task->mm->start_brk, &bss_start, &bss_end); + compute_bss_range(task->mm->end_data, task->mm->start_brk, &bss_start, + &bss_end); mmap_read_unlock(task->mm); - //now print the information we want to the det file - seq_printf(m, "PID \tNAME \tCPU(%%) \tSTART_CODE \tEND_CODE \tSTART_DATA\tEND_DATA \tBSS_START\tBSS_END\tELF\n"); - seq_printf(m, "%.5d\t%.7s\t%llu.%02llu\t0x%.13lx\t0x%.13lx\t0x%.13lx\t0x%.13lx\t0x%.13lx\t0x%.13lx\t0x%.13lx\n", - task->pid, task->comm, (usage_permyriad / 100), (usage_permyriad % 100), - task->mm->start_code, task->mm->end_code, - task->mm->start_data, task->mm->end_data, - bss_start, bss_end, - elf_header); + // now print the information we want to the det file + seq_puts(m, "PID \tNAME \tCPU(%) \tSTART_CODE \tEND_CODE " + "\tSTART_DATA\tEND_DATA \tBSS_START\tBSS_END\tELF\n"); + seq_printf(m, + "%.5d\t%.7s\t%llu.%02llu\t0x%.13lx\t0x%.13lx\t0x%.13lx\t0x%." + "13lx\t0x%.13lx\t0x%.13lx\t0x%.13lx\n", + task->pid, task->comm, (usage_permyriad / 100), + (usage_permyriad % 100), task->mm->start_code, + task->mm->end_code, task->mm->start_data, task->mm->end_data, + bss_start, bss_end, elf_header); return 0; } -//runs when openning file -static int elfdet_open(struct inode *inode, struct file *file) { - return single_open(file, elfdet_show, NULL); //calling elfdet_show +// runs when opening file +static int elfdet_open(struct inode *inode, struct file *file) +{ + return single_open(file, elfdet_show, NULL); // calling elfdet_show } -//file operations of det proc (using proc_ops for kernel 5.6+) +// file operations of det proc (using proc_ops for kernel 5.6+) static const struct proc_ops elfdet_det_ops = { - .proc_open = elfdet_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, + .proc_open = elfdet_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, }; +// elf proc file_operations starts - - -//elf proc file_operations starts - -//runs when elf opens -//will be called every time this file is accessed shows number of accessed times -static int procfile_open(struct inode *inode, struct file *file) +// runs when elf opens +// will be called every time this file is accessed shows number of accessed +// times +static int procfile_open(struct inode *inode, struct file *file) { - numberOpens++; - printk(KERN_INFO "procfile opened %d times", numberOpens); + number_opens++; + pr_info("procfile opened %d times\n", number_opens); return 0; } -//when we cat elf file this function will be runned (this is useless here) because our info is in det file not here! -static ssize_t procfile_read(struct file *file, char *buffer, size_t length, loff_t *offset) +// when we cat elf file this function will be run (this is useless here) +// because our info is in det file not here! +static ssize_t procfile_read(struct file *file, char __user *buffer, + size_t length, loff_t *offset) { - static int finished = 0; //normal return value other than '0' will cause loop - int ret = 0; + static int finished; + char tmp[64]; + int len; - printk(KERN_INFO "procfile read called\n"); + // normal return value other than '0' will cause loop - if (finished) { - printk(KERN_INFO "procfs read: END\n"); - finished = 0; - return 0; - } + pr_info("procfile read called\n"); - finished = 1; - ret = sprintf(buffer, "buff variable : %s\n", buff); - return ret; + if (finished) { + pr_info("procfs read: END\n"); + finished = 0; + return 0; + } + + finished = 1; + len = snprintf(tmp, sizeof(tmp), "buff variable : %s\n", buff); + if (len < 0) + return -EFAULT; + if (len > length) + len = length; + if (copy_to_user(buffer, tmp, len)) + return -EFAULT; + return len; } -//most important function of elf! called when we write some charachters into it -static ssize_t procfile_write(struct file *file, const char *buffer, size_t length, loff_t *offset) +// most important function of elf! called when we write some characters into it +static ssize_t procfile_write(struct file *file, const char __user *buffer, + size_t length, loff_t *offset) { long ret; - ret = strncpy_from_user(buff, buffer, sizeof(buff) - 1); //copy the charachters to buff (global buffer, inorder to use it in kernel) + + ret = strncpy_from_user(buff, buffer, sizeof(buff) - 1); + // copy the characters to buff (global buffer, in order to use it in + // kernel) if (ret < 0) return ret; buff[ret] = '\0'; // Null terminate - printk(KERN_INFO "procfs_write called\n"); + pr_info("procfs_write called\n"); return length; } - -static const struct proc_ops write_pops = { +static const struct proc_ops write_pops = { .proc_open = procfile_open, .proc_read = procfile_read, - .proc_write = procfile_write,//this is the important part + .proc_write = procfile_write, // this is the important part }; +static int elfdet_init(void) +{ + elfdet_dir = proc_mkdir("elf_det", NULL); + // creating the directory: elf_det in proc -static int elfdet_init(void) { - elfdet_dir = proc_mkdir("elf_det", NULL); //creating the directory: elf_det in proc - - if (!elfdet_dir) { + if (!elfdet_dir) return -ENOMEM; - } - //0777 means full premmisions for the file - elfdet_det_entry = proc_create("det", 0777, elfdet_dir, &elfdet_det_ops); //create proc file det with elfdet_det_ops - printk("det initiated; /proc/elf_det/det created\n"); - elfdet_pid_entry = proc_create("pid", 0777, elfdet_dir, &write_pops); //create proc file pid with write_pops - printk("pid initiated; /proc/elf_det/pid created\n"); - - if (!elfdet_det_entry) { + + // 0644 means owner read/write, others read-only + elfdet_det_entry = + proc_create("det", 0644, elfdet_dir, &elfdet_det_ops); + // create proc file det with elfdet_det_ops + pr_info("det initiated; /proc/elf_det/det created\n"); + elfdet_pid_entry = proc_create("pid", 0644, elfdet_dir, &write_pops); + // create proc file pid with write_pops + pr_info("pid initiated; /proc/elf_det/pid created\n"); + + if (!elfdet_det_entry) return -ENOMEM; - } - return 0; } -//the remove operations done by module(cleaning up) -static void elfdet_exit(void) { +// the remove operations done by module(cleaning up) +static void elfdet_exit(void) +{ proc_remove(elfdet_det_entry); - printk("elf_det exited; /proc/elf_det/det deleted\n"); + pr_info("elf_det exited; /proc/elf_det/det deleted\n"); proc_remove(elfdet_pid_entry); - printk("elf_det exited; /proc/elf_det/pid deleted\n"); + pr_info("elf_det exited; /proc/elf_det/pid deleted\n"); proc_remove(elfdet_dir); } -//macros for init and exit +// macros for init and exit module_init(elfdet_init); module_exit(elfdet_exit); diff --git a/src/elf_det_tests.c b/src/elf_det_tests.c index 101de92..575b86f 100644 --- a/src/elf_det_tests.c +++ b/src/elf_det_tests.c @@ -1,23 +1,29 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) #include "elf_helpers.h" #include #include -int main(void) { - /* compute_usage_permyriad tests */ - assert(compute_usage_permyriad(0, 1000000ULL) == 0); - assert(compute_usage_permyriad(500000ULL, 1000000ULL) == 5000ULL); - assert(compute_usage_permyriad(250000ULL, 1000000ULL) == 2500ULL); - assert(compute_usage_permyriad(1000000ULL, 1000000ULL) == 10000ULL); - assert(compute_usage_permyriad(0, 0) == 0); +int main(void) +{ + /* compute_usage_permyriad tests */ + assert(compute_usage_permyriad(0, 1000000ULL) == 0); + assert(compute_usage_permyriad(500000ULL, 1000000ULL) == 5000ULL); + assert(compute_usage_permyriad(250000ULL, 1000000ULL) == 2500ULL); + assert(compute_usage_permyriad(1000000ULL, 1000000ULL) == 10000ULL); + assert(compute_usage_permyriad(0, 0) == 0); - /* compute_bss_range tests */ - unsigned long s = 0, e = 0; - assert(compute_bss_range(1000UL, 2000UL, &s, &e) == 1); - assert(s == 1000UL && e == 2000UL); + /* compute_bss_range tests */ + unsigned long s = 0, e = 0; + int ret1, ret2; - assert(compute_bss_range(3000UL, 2000UL, &s, &e) == 0); - assert(s == 0UL && e == 0UL); + ret1 = compute_bss_range(1000UL, 2000UL, &s, &e); + assert(ret1 == 1); + assert(s == 1000UL && e == 2000UL); - puts("elf_helpers tests passed"); - return 0; -} \ No newline at end of file + ret2 = compute_bss_range(3000UL, 2000UL, &s, &e); + assert(ret2 == 0); + assert(s == 0UL && e == 0UL); + + puts("elf_helpers tests passed"); + return 0; +} diff --git a/src/elf_helpers.h b/src/elf_helpers.h index 2cdbd57..ac65bfa 100644 --- a/src/elf_helpers.h +++ b/src/elf_helpers.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ #pragma once #ifdef __KERNEL__ @@ -11,22 +12,23 @@ typedef uint64_t eh_u64; /* Compute CPU usage permyriad (percent * 100) from total_ns and delta_ns */ static inline eh_u64 compute_usage_permyriad(eh_u64 total_ns, eh_u64 delta_ns) { - if (delta_ns == 0) return 0; - return (10000ULL * total_ns) / delta_ns; + if (delta_ns == 0) + return 0; + return (10000ULL * total_ns) / delta_ns; } /* Compute BSS range from end_data and start_brk; returns 0 on invalid */ static inline int compute_bss_range(unsigned long end_data, - unsigned long start_brk, - unsigned long *out_start, - unsigned long *out_end) + unsigned long start_brk, + unsigned long *out_start, + unsigned long *out_end) { - if (start_brk < end_data) { - *out_start = 0; - *out_end = 0; - return 0; - } - *out_start = end_data; - *out_end = start_brk; - return 1; + if (start_brk < end_data) { + *out_start = 0; + *out_end = 0; + return 0; + } + *out_start = end_data; + *out_end = start_brk; + return 1; } diff --git a/src/proc_elf_ctrl.c b/src/proc_elf_ctrl.c index b6167fd..7d4986f 100644 --- a/src/proc_elf_ctrl.c +++ b/src/proc_elf_ctrl.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) #include #include #include @@ -6,77 +7,92 @@ int main(int argc, char **argv) { - FILE *fp; - char pid_user[20]; - char buff[2048]; + FILE *fp; + char pid_user[20]; + char buff[2048]; - if (argc > 1) { - strncpy(pid_user, argv[1], sizeof(pid_user) - 1); - pid_user[sizeof(pid_user) - 1] = '\0'; + if (argc > 1) { + char *pid_path; + char *det_path; + size_t len; - char *pid_path = build_proc_path("pid"); - fp = fopen(pid_path, "w"); - if (!fp) { - perror("open pid"); - free(pid_path); - return 1; - } - fprintf(fp,"%s", pid_user); - fclose(fp); - free(pid_path); + /* Safe string copy with explicit bounds checking */ + len = strlen(argv[1]); + if (len >= sizeof(pid_user)) + len = sizeof(pid_user) - 1; + memcpy(pid_user, argv[1], len); + pid_user[len] = '\0'; - char *det_path = build_proc_path("det"); - fp = fopen(det_path, "r"); - if (!fp) { - perror("open det"); - free(det_path); - return 1; - } - if (fgets(buff, sizeof(buff), fp)) - printf("%s\n", buff); - if (fgets(buff, sizeof(buff), fp)) - printf("%s\n", buff); - fclose(fp); - free(det_path); - return 0; - } + pid_path = build_proc_path("pid"); + fp = fopen(pid_path, "w"); + if (!fp) { + perror("open pid"); + free(pid_path); + return 1; + } + fprintf(fp, "%s", pid_user); + fclose(fp); + free(pid_path); - printf("***************************************************************************\n"); - printf("******Navid user program for gathering memory info on desired process******\n"); - printf("***************************************************************************\n"); - printf("***************************************************************************\n"); - while (1) { - printf("************enter the process id:"); - if (scanf("%19s", pid_user) != 1) { - fprintf(stderr, "invalid input\n"); - break; - } + det_path = build_proc_path("det"); + fp = fopen(det_path, "r"); + if (!fp) { + perror("open det"); + free(det_path); + return 1; + } + if (fgets(buff, sizeof(buff), fp)) + printf("%s\n", buff); + if (fgets(buff, sizeof(buff), fp)) + printf("%s\n", buff); + fclose(fp); + free(det_path); + return 0; + } - char *pid_path2 = build_proc_path("pid"); - fp = fopen(pid_path2, "w"); - if (!fp) { - perror("open pid"); - free(pid_path2); - return 1; - } - fprintf(fp,"%s", pid_user); - fclose(fp); - free(pid_path2); + printf("***************************************************************" + "********\n"); + printf("******Navid user program for gathering memory info on desired " + "process******\n"); + printf("***************************************************************" + "********\n"); + printf("***************************************************************" + "********\n"); + while (1) { + char *pid_path2; + char *det_path2; - printf("the process info is here:\n"); - char *det_path2 = build_proc_path("det"); - fp = fopen(det_path2, "r"); - if (!fp) { - perror("open det"); - free(det_path2); - return 1; - } - if (fgets(buff, sizeof(buff), fp)) - printf("%s\n", buff); - if (fgets(buff, sizeof(buff), fp)) - printf("%s\n", buff); - fclose(fp); - free(det_path2); - } - return 0; -} \ No newline at end of file + printf("************enter the process id:"); + if (scanf("%19s", pid_user) != 1) { + fprintf(stderr, "invalid input\n"); + break; + } + + pid_path2 = build_proc_path("pid"); + fp = fopen(pid_path2, "w"); + if (!fp) { + perror("open pid"); + free(pid_path2); + return 1; + } + fprintf(fp, "%s", pid_user); + fclose(fp); + free(pid_path2); + + printf("the process info is here:\n"); + det_path2 = build_proc_path("det"); + fp = fopen(det_path2, "r"); + if (!fp) { + perror("open det"); + free(det_path2); + return 1; + } + if (fgets(buff, sizeof(buff), fp)) + printf("%s\n", buff); + if (fgets(buff, sizeof(buff), fp)) + printf("%s\n", buff); + fclose(fp); + free(det_path2); + } + return 0; +} diff --git a/src/proc_elf_ctrl_tests.c b/src/proc_elf_ctrl_tests.c index bfe46e1..1a34fa5 100644 --- a/src/proc_elf_ctrl_tests.c +++ b/src/proc_elf_ctrl_tests.c @@ -1,21 +1,26 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) #include "user_helpers.h" #include #include #include -int main(void) { - /* Without env override */ - unsetenv("ELF_DET_PROC_DIR"); - char *p1 = build_proc_path("pid"); - assert(p1 && strcmp(p1, "/proc/elf_det/pid") == 0); - free(p1); +int main(void) +{ + char *p1; + char *p2; - /* With env override */ - setenv("ELF_DET_PROC_DIR", "/tmp/fakeproc", 1); - char *p2 = build_proc_path("det"); - assert(p2 && strcmp(p2, "/tmp/fakeproc/det") == 0); - free(p2); + /* Without env override */ + unsetenv("ELF_DET_PROC_DIR"); + p1 = build_proc_path("pid"); + assert(p1 && strcmp(p1, "/proc/elf_det/pid") == 0); + free(p1); - puts("user_helpers tests passed"); - return 0; -} \ No newline at end of file + /* With env override */ + setenv("ELF_DET_PROC_DIR", "/tmp/fakeproc", 1); + p2 = build_proc_path("det"); + assert(p2 && strcmp(p2, "/tmp/fakeproc/det") == 0); + free(p2); + + puts("user_helpers tests passed"); + return 0; +} diff --git a/src/user_helpers.h b/src/user_helpers.h index 43c37ac..eadef5a 100644 --- a/src/user_helpers.h +++ b/src/user_helpers.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ #pragma once #include @@ -7,12 +8,16 @@ /* Build path to /proc/elf_det/ with optional override via env. */ static inline char *build_proc_path(const char *name) { - const char *base = getenv("ELF_DET_PROC_DIR"); - if (!base || !*base) base = "/proc/elf_det"; + const char *base = getenv("ELF_DET_PROC_DIR"); - size_t len = strlen(base) + 1 + strlen(name) + 1; - char *p = (char *)malloc(len); - if (!p) return NULL; - snprintf(p, len, "%s/%s", base, name); - return p; + if (!base || !*base) + base = "/proc/elf_det"; + + size_t len = strlen(base) + 1 + strlen(name) + 1; + char *p = (char *)malloc(len); + + if (!p) + return NULL; + snprintf(p, len, "%s/%s", base, name); + return p; }