CSE451 Spring 2008 Project
#2
Out: April 17 2008
Due: May 2 2008
Objectives
In the first project you learned
how to build and debug the windows kernel, and how to enhance a system
call. This second project builds on the first one by requiring
more information be safely managed and returned via the query call.
The principal objectives of this second project are:
- Learn how to use
synchronization primitives to ensure your code is properly synchronized
for a multi-processor and preemptive kernel environment.
- Learn how to manage
dynamic data in a kernel.
- Learn about buffering
data and dealing with unexpected overflow conditions
Getting Started
Some of the new tools that
you will need for the project are:
- Mutex - Defined
in base\ntos\inc\ex.h are a set of routines to manipulate a structured
called a FastMutex. A FastMutex is a basic synchronization mechanism
used throughout the kernel. It is essentially a binary semaphore
but optimized for single ownership.
- Pool - Dynamically
allocated memory in the kernel is called pool. There are two types
of pool “PagedPool” and “NonPagedPool.” PagedPool is memory
that is allowed to be paged out to disk, while NonPagedPool must remain
in memory at all times. As a rule of thumb allocation should be
from PagedPool unless the code accessing the data must not take a page
fault. For example, the paging code itself can never take a page
fault, otherwise the data can never be read. Another rule of thumb
in Windows is to never take a page fault holding a spinlock or with
interrupts disabled. The routines to allocate and free memory
is called ExAllocatePool and ExFreePool. They are declared in
base\ntos\ex.h and their usage is pretty straightforward.
- Time - Time
in NT is stored in the kernel as a 64 bit integer. The resolution
is 100ns. Kernel mode code calls KeQuerySystemTime to get the
current time. There are also routines in base\ntos\rtl\time.c
to help manipulate times.
This is just a partial list
to help you get started, there are definitely more things in the kernel
that you can use to help complete this project.
Your assignment:
Start by copying your own solution
for Project 1 to a new directory called Project 2. Alternatively
you can start with my nominal solution to project 1 which will be available
on or after April 17.
- Most likely your
solution for project 1 did not fully synchronize access to your kernel
data structures and therefore the code will not always yield the correct
answer on a preemptive kernel or on an MP system. The first part
of this assignment is to correct this deficiency by protecting access
to all the data structures that you added to project 1. You should
use this using the Mutex provided in NT. Granularity (or scope)
of the lock is up to you.
Alternately for extra credit
you can skip using the kernel supplied Mutex, and instead write you
own synchronization package using only spinlocks and events.
Your implementation must allow touched paged pool while holding your
lock.
- We want to enhance
the NT API counters logic from project 1 to include the order in which
the actual calls and returns take place. To do this you will need
to have the system keep a history buffer of these events that will be
returned to the user on the query call. Each event needs to have
a timestamp and specify an action. Actions are either “Call”,
“Return”, or “Buffer Overflow”. Each Call and Return action
must also specify its appropriate NT API classification. In addition
each return event must specify its returns status classification.
The “Buffer Overflow” will denote holes in the timeline where the
system was unable to keep a full history because of memory limitations.
Extend the SystemCSE451Information
class to enable the user to retrieve the history of events. The
user can supply any sized buffer and the system will need to return
as much data as the buffer can hold or until there are no more events
to return. If the buffer is empty the system returns STATUS_NO_MORE_ENTRIES.
Use PagedPool to store
the event history in 4KB buffers that you will dynamically allocate
and free as necessary. Do not consume more than 64KB worth of
buffers. This limit does not include any ancillary data that you
need to manage the buffers. It is probably convenient to visualize
each NT API call as producing events and the query as consuming events.
If an event occurs and all 64KB is already in use then system will insert
a buffer overflow event. The overflow condition will exist until
a query is used to drain some of the buffer. The system must only
keep as many 4KB buffers as necessary to keep the history. That
is, it needs to allocate and free buffers as needed.
- Modify your test
program to take as input a buffer size. The buffer size is the
number of bytes to use when calling NtQuerySystemInformation.
The new output can now look like:
API
Total
Class
Calls Success Information Warning
Error
-----
-------- ------- --------
------- ------
NtOpen
56 50
2 3
1
NtRead
123 100
0 0
23
History
-------
yyyy/mm/dd HH:MM:SS.sss
NtOpen Call
yyyy/mm/dd HH:MM:SS.sss
NtOpen Return Success
yyyy/mm/dd HH:MM:SS.sss
NtRead Call
yyyy/mm/dd HH:MM:SS.sss
NtQuery Call
Buffer Overflow
yyyy/mm/dd HH:MM:SS.sss
NtWrite Return Warning
Turn-in:
Be prepared to turn in the
following
- Executables images
of your test program, and the modified kernel.
- Source code for
your test program and the modules that you have modified and added to
your kernel.
You'll be submitting the source
code and executables to Catalyst. Please seperately submit test.exe,
wrkx86.exe, and a zip of your entire source directory (After running
'nmake clean').
Grading Criteria:
Your project will be graded
based on the following criteria:
- [50%] Does it accomplish
the assigned task, split evenly between synchronization and history
buffering?
- [15%] Are your modifications
to the kernel clear and easy to understand?
- [15%] Are your modifications
to the kernel correct?
- [10%] Is the output
of the test program clear and easy to understand?
- [10%] Is the coding
of the test program clear and easy to understand?
- [10% Extra Credit]
Wrote Own Synchronization.