Operating Systems 2022F: Assignment 4

From Soma-notes

Please submit the answers to the following questions via Brightspace by December 7, 2022 by 11:59 PM. There are 20 points in 9 questions.

Submit your answers as a plain text file following this template. Name your answer file "comp3000-assign4-<username>.txt" (where username is your MyCarletonOne username). Please make sure your submission passes the assignment validator.

Don't forget to include what outside resources you used to complete each of your answers, including other students, man pages, and web resources. You do not need to list help from the instructor, TA, or information found in the textbook.


Questions

  1. [1] Download, build, and install the c3000procreport module. What is reported in the kernel log when the module is installed?
  2. [2] When you run cat /dev/procreport, what does it report? Give sample output and explain what each part means briefly.
  3. [2] Run bpftrace watch_procreport.bt in one terminal and then run dd if=/dev/procreport bs=1024 count=1. What does the bpftrace script report? Why are we getting this output? Explain what you see.
  4. [2] If you run the command dd if=/dev/procreport seek=1 bs=1024 count=1, what happens? Does bpftrace watch_procreport.bt give you any insight as to what is happening?
  5. [2] What is the purpose of line 127, a call to put_user()? Why can't this line be replaced with a simple array assignment?
  6. [1] What is the purpose of line 111, a call to put_user()? Remove this line and report how the behavior of the module changes.
  7. [2] What is the purpose of the calls to get_zeroed_page() (line 104) and free_page() (line 132)? What happens if either is missing?
  8. [4] Change c3000procreport so that it reports not just the calling process but also lists all of the process's ancestors until there are no more to report. You should support up to 10 levels of ancestor processes. Each report should be on a line saying "PID's parent is PID", where the PID's are process IDs.
  9. [4] Change c3000procreport so that it reports the address of all the intermediate lookups in when determining the physical address corresponding to the read buffer's virtual address.
    • Make sure you do so by changing how procreport_get_physical() works (rather than just expanding procreport_read()). How did you change the interface to procreport_get_physical()?
    • Are the addresses being reported virtual or physical addresses? How do you know?

Source

c3000procreport.zip

c3000procreport.c

/* 
   c3000procreport.c
   Anil Somayaji
   November 21, 2022

   creates a device /dev/procreport that, when read,
   reports on the process reading the device

   based on newgetpid and 3000physicalview
   Carleton University COMP 3000, Fall 2022
*/

#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/pgtable_types.h>

#define dbg(format, arg...) do { if (debug) pr_info(CLASS_NAME ": %s: " format, __FUNCTION__, ## arg); } while (0)
#define err(format, arg...) pr_err(CLASS_NAME ": " format, ## arg)
#define info(format, arg...) pr_info(CLASS_NAME ": " format, ## arg)
#define warn(format, arg...) pr_warn(CLASS_NAME ": " format, ## arg)
#define alert(format, arg...) pr_alert(CLASS_NAME ": " format, ## arg)

#define DEVICE_NAME "procreport"
#define CLASS_NAME "comp3000"

static struct class* procreport_class = NULL;
static struct device* procreport_device = NULL;
static int procreport_major;

unsigned long procreport_get_physical(unsigned long addr)
{
        pgd_t *pgd;
        p4d_t *p4d;
        pud_t *pud;
        pmd_t *pmd;
        pte_t *pte;
        
        unsigned long pfn = 0;
        unsigned long phys = 0;

        pgd = pgd_offset(current->mm, addr);
        if (!pgd || pgd_none(*pgd) || pgd_bad(*pgd)) {
                err("Invalid pgd for address 0x%016lx\n", addr);
                return phys;
        }
        
        p4d = p4d_offset(pgd, addr);
        if (!p4d || p4d_none(*p4d) || p4d_bad(*p4d)) {
                err("Invalid p4d for address 0x%016lx\n", addr);
                return phys;
        }

        pud = pud_offset(p4d, addr);
        if (!pud || pud_none(*pud) || pud_bad(*pud)) {
                err("Invalid pud for address 0x%016lx\n", addr);
                return phys;
        }

        pmd = pmd_offset(pud, addr);
        if (!pmd || pmd_none(*pmd) || pmd_bad(*pmd)) {
                err("Invalid pmd for address 0x%016lx\n", addr);
                return phys;
        }

        pte = pte_offset_map(pmd, addr);
        if (!pte || pte_none(*pte)) {
                err("Invalid pte for address 0x%016lx\n", addr);
                return phys;
        }
    
        pfn = pte_pfn(*pte);
        phys = (pfn << PAGE_SHIFT) + (addr % PAGE_SIZE);

        return phys;
}
EXPORT_SYMBOL(procreport_get_physical);


static int procreport_open(struct inode *the_inode, struct file *f)
{
        return 0;
}

static ssize_t procreport_read(struct file *f, char *buf, size_t len,
                                    loff_t *offset)
{
        size_t i, msglen;
        pid_t thepid;
        unsigned long buf_phys;
        char *message;
        
        if (*offset > 0) {
                return 0;
        }

        message = (char *) get_zeroed_page(GFP_KERNEL);
        if (!message) {
                return -ENOMEM;
        }
        
        thepid = task_tgid_vnr(current);

        put_user('0', buf);
        buf_phys = procreport_get_physical((unsigned long) buf);

        snprintf(message, PAGE_SIZE,
                 "Your PID is %d!\n"
                 "Buffer at 0x%016lx virtual is at 0x%016lx physical.\n",
                 thepid, (unsigned long) buf, buf_phys);
        
        msglen = strlen(message);
        msglen++;
        
        if (len < msglen) {
                msglen = len;
        }
        
        for (i = 0; i < msglen; i++) {
                put_user(message[i], buf++);
        }
        
        *offset = i;

        free_page((unsigned long) message);
        
        return i;
}
                           
static int procreport_release(struct inode *the_inode, struct file *f)
{
        return 0;
}


static struct file_operations procreport_fops = {
        .open = procreport_open,
        .read = procreport_read,
        .release = procreport_release,
};


static char *procreport_devnode(struct device *dev, umode_t *mode)
{
        if (mode)
                *mode = 0444;
        return NULL;
}

static int __init procreport_init(void)
{
        int retval;
  
        procreport_major = register_chrdev(0, DEVICE_NAME,
                                                &procreport_fops);
        if (procreport_major < 0) {
                err("failed to register device: error %d\n", 
                    procreport_major);
                retval = procreport_major;
                goto failed_chrdevreg;
        }
 
        procreport_class = class_create(THIS_MODULE, CLASS_NAME);
        if (IS_ERR(procreport_class)) {
                err("failed to register device class '%s'\n", CLASS_NAME);
                retval = PTR_ERR(procreport_class);
                goto failed_classreg;
        }
 
        procreport_class->devnode = procreport_devnode;

        procreport_device = device_create(procreport_class, NULL,
                                               MKDEV(procreport_major, 0),
                                               NULL, DEVICE_NAME);

        if (IS_ERR(procreport_device)) {
                err("failed to create device '%s'\n", DEVICE_NAME);
                retval = PTR_ERR(procreport_device);
                goto failed_devreg;
        }
        
        info("procreport device registered using major %d.\n",
             procreport_major);
        
        return 0;
        
 failed_devreg:
        class_unregister(procreport_class);
 failed_classreg:
        unregister_chrdev(procreport_major, DEVICE_NAME);
 failed_chrdevreg:
        return -1;
}

static void __exit procreport_exit(void)
{
        device_destroy(procreport_class, MKDEV(procreport_major, 0));
        class_unregister(procreport_class);
        unregister_chrdev(procreport_major, "procreport");
        info("Unloading procreport module.\n");
        return;
}

module_init(procreport_init);
module_exit(procreport_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Somayaji <soma@scs.carleton.ca>");
MODULE_DESCRIPTION("c3000procreport character device module");

watch_procreport.bt

kprobe:procreport_devnode
{
	printf("devnote called.\n");
}

kprobe:procreport_get_physical
{
	printf("get_physical called.\n");
}

kprobe:procreport_open
{
	printf("open called.\n");
}

kprobe:procreport_read
{
	printf("read called.\n");
}

kretprobe:procreport_read
{
	printf("read returned %d.\n", retval);
}

kprobe:procreport_release
{
	printf("release called.\n");
}

Solutions

Assignment 4 Solutions