COMP3000 Operating Systems F23: Tutorial 7: Difference between revisions

From Soma-notes
Abdou (talk | contribs)
Abdou (talk | contribs)
 
(5 intermediate revisions by the same user not shown)
Line 15: Line 15:


===Tasks part A: Understanding special files===
===Tasks part A: Understanding special files===
# Try the following commands as a non-privileged user. What does each do? How do the files f1-f3 compare? How do they compare to <tt>/dev/urandom</tt>? Remember you can get output from <tt>/dev/urandom</tt> using <tt>cat</tt> or <tt>dd</tt>. If you use <tt>cat</tt>, make sure to pipe it to <tt>less</tt>!
# Try the following commands as a non-privileged user. What does each do? How do the files f1-f3 compare? How do they compare to <code>/dev/urandom</code>? Remember you can get output from <code>/dev/urandom</code> using <code>cat</code> or <code>dd</code>. If you use <code>cat</code>, make sure to pipe it to <code>less</code>!
#* <tt>cp /dev/urandom f1</tt> (hit Ctrl+C right away to avoid a huge file)
#* <code>cp /dev/urandom f1</code> (hit Ctrl+C right away to avoid a huge file)
#* <tt>sudo cp -a /dev/urandom f2</tt>
#* <code>sudo cp -a /dev/urandom f2</code>
#* <tt>sudo mknod f3 c 1 9</tt>
#* <code>sudo mknod f3 c 1 9</code>
#: Note: If you see any command running for more than a few seconds, hit Ctrl+C right away, check the produced file (often it’s very large) and delete it if not needed.
#: Note: If you see any command running for more than a few seconds, hit Ctrl+C right away, check the produced file (often it’s very large) and delete it if not needed.
# Make named pipes using <tt>mknod</tt> and <tt>mkfifo</tt>. Use them to simulate <tt>ls | wc</tt> using just the '>' and '<' operators.
# Make named pipes using <code>mknod</code> and <code>mkfifo</code>. Use them to simulate <code>ls | wc</code> using just the '>' and '<' operators.
# Use <tt>mknod</tt> to make a copy of your current terminal's tty - <tt>/dev/tty</tt> (Hint: consider how you did something similar with <tt>/dev/urandom</tt> above). You can name it <tt>mytty</tt>. Examine its characteristics using <tt>stty --file=mytty</tt>. Do the same for the original tty.  
# Use <code>mknod</code> to make a copy of your current terminal's tty - <code>/dev/tty</code> (Hint: consider how you did something similar with <code>/dev/urandom</code> above). You can name it <code>mytty</code>. Examine its characteristics using <code>stty --file=mytty</code>. Do the same for the original tty.  
# Run <tt>stty --help</tt> to see what you can do with stty. Try disabling local echo. How does the shell behave with echo disabled? How can you restore echo without logging out and back in? Recall we did this in the class.
# Run <code>stty --help</code> to see what you can do with stty. Try disabling local echo. How does the shell behave with echo disabled? How can you restore echo without logging out and back in? Recall we did this in the class.


==Kernel Modules==
==Kernel Modules==
Line 31: Line 31:
Before you start, review what you have learned about kernel modules and device drivers, and think about the following questions (no need to answer them): 1) How does the source of kernel modules differ from C programs? 2) How does building kernel modules differ from building userspace C programs? 3) How does invoking the file operations (e.g., read/write) of a special file trigger corresponding functions in a device driver?
Before you start, review what you have learned about kernel modules and device drivers, and think about the following questions (no need to answer them): 1) How does the source of kernel modules differ from C programs? 2) How does building kernel modules differ from building userspace C programs? 3) How does invoking the file operations (e.g., read/write) of a special file trigger corresponding functions in a device driver?


You will learn how you can access and manipulate kernel data structures and call kernel functions in a kernel module. You will also understand how processes are represented by <tt>task_struct</tt>'s in the Linux kernel, what kind of information is stored in a task struct, and how to access the <tt>task_struct</tt> of a process.
You will learn how you can access and manipulate kernel data structures and call kernel functions in a kernel module. You will also understand how processes are represented by <code>task_struct</code>'s in the Linux kernel, what kind of information is stored in a task struct, and how to access the <code>task_struct</code> of a process.


===Tasks part B: A simple kernel module===
===Tasks part B: A simple kernel module===
Download the source for this [https://people.scs.carleton.ca/~lianyingzhao/comp3000/w23/tut7/simple.tar.gz simple module], unpack, and build it by typing "<tt>make</tt>". You can safely ignore the warning of "Skipping BTF generation...".
Download the source for this [https://people.scs.carleton.ca/~abdou/comp3000/f23/tut7/simple.tar.gz simple module], unpack, and build it by typing "<code>make</code>". You can safely ignore the warning of "Skipping BTF generation...".
# Install the module using "<tt>sudo insmod simple.ko</tt>". The hello message is recorded in the kernel logs. How do you view the kernel logs? How many ways are there to view them?
# Install the module using "<code>sudo insmod simple.ko</code>". The hello message is recorded in the kernel logs. How do you view the kernel logs? How many ways are there to view them?
# Check to see that the module has been loaded. How do you do this?
# Check to see that the module has been loaded. How do you do this?
# Remove the module from the kernel. What did you do?
# Remove the module from the kernel. What did you do?


===Tasks part C: A character device kernel module (driver)===
===Tasks part C: A character device kernel module (driver)===
Download the source for [https://people.scs.carleton.ca/~lianyingzhao/comp3000/w23/tut7/ones.tar.gz ones], a kernel module implementing a character device that outputs an unbounded string of "1"'s. Build, compile, and run it as before.
Download the source for [https://people.scs.carleton.ca/~abdou/comp3000/f23/tut7/ones.tar.gz ones], a kernel module implementing a character device that outputs an unbounded string of "1"'s. Build, compile, and run it as before.
# What kernel messages does the module generate? Does it create any new files (other than <tt>/dev/ones</tt>)? If so, where? (Hint: search in <tt>/sys</tt>)
# What kernel messages does the module generate? Does it create any new files (other than <code>/dev/ones</code>)? If so, where? (Hint: search in <code>/sys</code>)
# What happens when you "cat" the device <tt>/dev/ones</tt>? How can you limit the output in more than one way?
# What happens when you "cat" the device <code>/dev/ones</code>? How can you limit the output in more than one way?
# Add more <tt>printk()</tt>’s (where you see needed) to find out at which point which functions in ones.c are called and for which purpose.
# Add more <code>printk()</code>’s (where you see needed) to find out at which point which functions in ones.c are called and for which purpose.


===Tasks part D: Getting process information from a module===
===Tasks part D: Getting process information from a module===
Download the source [https://people.scs.carleton.ca/~lianyingzhao/comp3000/w23/tut7/newgetpid.tar.gz newgetpid.c]. Build and run it as before. Hints for the questions below:  
Download the source [https://people.scs.carleton.ca/~abdou/comp3000/f23/tut7/newgetpid.tar.gz newgetpid.c]. Build and run it as before. Hints for the questions below:  


https://elixir.bootlin.com/linux/latest/source/include/linux/sched.h,  
https://elixir.bootlin.com/linux/latest/source/include/linux/sched.h,  
https://elixir.bootlin.com/linux/latest/source/arch/x86/include/asm/current.h,  
https://elixir.bootlin.com/linux/latest/source/arch/x86/include/asm/current.h,  
https://elixir.bootlin.com/linux/latest/source/include/linux/cred.h
https://elixir.bootlin.com/linux/latest/source/include/linux/cred.h
# What type is "<tt>current</tt>"? <u>How</u> can you figure this out?
# What type is "<code>current</code>"? <u>How</u> can you figure this out?
# Modify newgetpid.c so that it creates a device file <tt>/dev/describe</tt> rather than <tt>/dev/newgetpid</tt>.
# Modify newgetpid.c so that it creates a device file <code>/dev/describe</code> rather than <code>/dev/newgetpid</code>.
# Make <tt>/dev/describe</tt> output the calling process's parent ID (ppid), user ID (uid), group ID (gid), effective user ID (euid), and effective group ID (egid).
# Make <code>/dev/describe</code> output the calling process's parent ID (ppid), user ID (uid), group ID (gid), effective user ID (euid), and effective group ID (egid).


==Using <tt>bpftrace</tt> to monitor kernel module events==
==Using <code>bpftrace</code> to monitor kernel module events==
Note: you are not required to write about your experience for this part in the submitted work, but you should still do it, which can help you understand eBPF better (see how simple but still powerful an eBPF program can be).
Note: the following is optional but you are encouraged to do it, as it can help you understand eBPF better (see how simple but still powerful an eBPF program can be).


If you see an error like "<tt>Could not resolve symbol</tt>" when using <tt>bpftrace</tt>, you need to apply a fix by installing the dbgsym package of <tt>bpftrace</tt>: Just <tt>wget</tt> [https://people.scs.carleton.ca/~lianyingzhao/comp3000/w23/tut7/install-bpftrace-dbgsym this file], and run it with
If you see an error like "<code>Could not resolve symbol</code>" when using <code>bpftrace</code>, you need to apply a fix by installing the dbgsym package of <code>bpftrace</code>: Just <code>wget</code> [https://people.scs.carleton.ca/~abdou/comp3000/f23/tut7/install-bpftrace-dbgsym this file], and run it with


   sudo apt update
   sudo apt update
   source install-bpftrace-dbgsym
   source install-bpftrace-dbgsym


Download this tiny code [https://people.scs.carleton.ca/~lianyingzhao/comp3000/w23/tut7/kmsnoop.bt kmsnoop.bt] and make it executable with <tt>chmod 755 kmsnoop.bt</tt>.
Download this tiny code [https://people.scs.carleton.ca/~abdou/comp3000/f23/tut7/kmsnoop.bt kmsnoop.bt] and make it executable with <code>chmod 755 kmsnoop.bt</code>.


Now, you can open a separate SSH session and run it with <tt>sudo ./kmsnoop.bt</tt>. It will wait for events. When you do any "insmod" or "rmmod" in the other session, you will see it outputs a line of message.
Now, you can open a separate SSH session and run it with <code>sudo ./kmsnoop.bt</code>. It will wait for events. When you do any "insmod" or "rmmod" in the other session, you will see it outputs a line of message.


If you want to avoid opening another SSH window, you can use tmux (a terminal multiplexer) by just typing tmux and hit enter.
If you want to avoid opening another SSH window, you can use tmux (a terminal multiplexer) by just typing tmux and hit enter.
Line 73: Line 73:
To move between sessions: press Ctrl-B and N (or P)
To move between sessions: press Ctrl-B and N (or P)


How could this save you from doing any <tt>printk</tt>'s in your kernel module code?
How could this save you from doing any <code>printk</code>'s in your kernel module code?
You can also <tt>sudo bpftrace -l | grep</tt> for your needed kernel functions (e.g.,<tt>printk</tt>) to hook to.
You can also <code>sudo bpftrace -l | grep</code> for your needed kernel functions (e.g.,<code>printk</code>) to hook to.

Latest revision as of 01:44, 25 October 2023

In this tutorial you'll be learning about special files and Linux kernel modules, part of which are device drivers. You’ll create several kernel modules and see how to interact with device drivers using special files (e.g., character device) and their file operations as an interface.

Important Tips

  • There is a chance your VM will crash or your ssh session will become unresponsive when messing with the kernel. To recover, you can reboot your VM from the OpenStack web console.
  • Definitely do not under any circumstances attempt this on your own system. This is your last warning.
  • Remember to recompile and insert your module after making changes.
  • Work incrementally and be prepared for your system to crash.
  • It might be wise to code on your own computer and use scp or sshfs to transfer the files to your VM.
  • https://elixir.bootlin.com/linux/latest/source is a great resource for learning about kernel functions and data structures.

Note: although it's highly recommended that you use the openstack instance, if you cannot for a reason, or if you encounter other issues, check out these instructions to see if they help.

Special Files

Before you start, review what you have learned about special files and think about the following questions (no need to answer them): 1) What does a special file represent and what backs it up? 2) Can you have multiple special files that are somehow "the same"? What does it mean to "copy" a special file? 3) How are named pipes different from unnamed pipes and what are their similarities? Take this opportunity to deepen your understanding of special files to prepare you for working with device drivers that support such special files.

Tasks part A: Understanding special files

  1. Try the following commands as a non-privileged user. What does each do? How do the files f1-f3 compare? How do they compare to /dev/urandom? Remember you can get output from /dev/urandom using cat or dd. If you use cat, make sure to pipe it to less!
    • cp /dev/urandom f1 (hit Ctrl+C right away to avoid a huge file)
    • sudo cp -a /dev/urandom f2
    • sudo mknod f3 c 1 9
    Note: If you see any command running for more than a few seconds, hit Ctrl+C right away, check the produced file (often it’s very large) and delete it if not needed.
  2. Make named pipes using mknod and mkfifo. Use them to simulate ls | wc using just the '>' and '<' operators.
  3. Use mknod to make a copy of your current terminal's tty - /dev/tty (Hint: consider how you did something similar with /dev/urandom above). You can name it mytty. Examine its characteristics using stty --file=mytty. Do the same for the original tty.
  4. Run stty --help to see what you can do with stty. Try disabling local echo. How does the shell behave with echo disabled? How can you restore echo without logging out and back in? Recall we did this in the class.

Kernel Modules

In this part of the tutorial you will be building and installing kernel modules. You will need root access to install kernel modules.

It is highly recommended that you use a comp3000 openstack instance for the exercises below for two reasons. First, you may have difficulties compiling kernel modules on other systems. Second, these operations are potentially dangerous, and mistakes could destroy all data on the Linux system. Consider yourself warned!

Before you start, review what you have learned about kernel modules and device drivers, and think about the following questions (no need to answer them): 1) How does the source of kernel modules differ from C programs? 2) How does building kernel modules differ from building userspace C programs? 3) How does invoking the file operations (e.g., read/write) of a special file trigger corresponding functions in a device driver?

You will learn how you can access and manipulate kernel data structures and call kernel functions in a kernel module. You will also understand how processes are represented by task_struct's in the Linux kernel, what kind of information is stored in a task struct, and how to access the task_struct of a process.

Tasks part B: A simple kernel module

Download the source for this simple module, unpack, and build it by typing "make". You can safely ignore the warning of "Skipping BTF generation...".

  1. Install the module using "sudo insmod simple.ko". The hello message is recorded in the kernel logs. How do you view the kernel logs? How many ways are there to view them?
  2. Check to see that the module has been loaded. How do you do this?
  3. Remove the module from the kernel. What did you do?

Tasks part C: A character device kernel module (driver)

Download the source for ones, a kernel module implementing a character device that outputs an unbounded string of "1"'s. Build, compile, and run it as before.

  1. What kernel messages does the module generate? Does it create any new files (other than /dev/ones)? If so, where? (Hint: search in /sys)
  2. What happens when you "cat" the device /dev/ones? How can you limit the output in more than one way?
  3. Add more printk()’s (where you see needed) to find out at which point which functions in ones.c are called and for which purpose.

Tasks part D: Getting process information from a module

Download the source newgetpid.c. Build and run it as before. Hints for the questions below:

https://elixir.bootlin.com/linux/latest/source/include/linux/sched.h, https://elixir.bootlin.com/linux/latest/source/arch/x86/include/asm/current.h, https://elixir.bootlin.com/linux/latest/source/include/linux/cred.h

  1. What type is "current"? How can you figure this out?
  2. Modify newgetpid.c so that it creates a device file /dev/describe rather than /dev/newgetpid.
  3. Make /dev/describe output the calling process's parent ID (ppid), user ID (uid), group ID (gid), effective user ID (euid), and effective group ID (egid).

Using bpftrace to monitor kernel module events

Note: the following is optional but you are encouraged to do it, as it can help you understand eBPF better (see how simple but still powerful an eBPF program can be).

If you see an error like "Could not resolve symbol" when using bpftrace, you need to apply a fix by installing the dbgsym package of bpftrace: Just wget this file, and run it with

 sudo apt update
 source install-bpftrace-dbgsym

Download this tiny code kmsnoop.bt and make it executable with chmod 755 kmsnoop.bt.

Now, you can open a separate SSH session and run it with sudo ./kmsnoop.bt. It will wait for events. When you do any "insmod" or "rmmod" in the other session, you will see it outputs a line of message.

If you want to avoid opening another SSH window, you can use tmux (a terminal multiplexer) by just typing tmux and hit enter.

To create a new session: press Ctrl-B and C

To move between sessions: press Ctrl-B and N (or P)

How could this save you from doing any printk's in your kernel module code? You can also sudo bpftrace -l | grep for your needed kernel functions (e.g.,printk) to hook to.