SafeDrive : Safe and Recoverable Extensions Using Language-Based Techniques
Symposium A Quarterly Journal In Modern Foreign Literatures (2006)
- ISBN: 1931971471
Available from portal.acm.org
or
Available from portal.acm.org
Page 1
SafeDrive : Safe and Recoverable Extensions Using Language-Based Techniques
SafeDrive: Safe and Recoverable Extensions
Using Language-Based Techniques
Feng Zhou, Jeremy Condit, Zachary Anderson, Ilya Bagrak
University of California, Berkeley
{zf,jcondit,zra,ibagrak}@cs.berkeley.edu
Rob Ennals
Intel Research Berkeley
robert.ennals@intel.com
Matthew Harren, George Necula, Eric Brewer
University of California, Berkeley
{matth,necula,brewer}@cs.berkeley.edu
We present SafeDrive, a system for detecting and re-
covering from type safety violations in software exten-
sions. SafeDrive has low overhead and requires minimal
changes to existing source code. To achieve this result,
SafeDrive uses a novel type system that provides fine-
grained isolation for existing extensions written in C. In
addition, SafeDrive tracks invariants using simple wrap-
pers for the host system API and restores them when re-
covering from a violation. This approach achieves fine-
grained memory error detection and recovery with few
code changes and at a significantly lower performance
cost than existing solutions based on hardware-enforced
domains, such as Nooks [33], L4 [21], and Xen [13],
or software-enforced domains, such as SFI [35]. The
principles used in SafeDrive can be applied to any large
system with loadable, error-prone extension modules.
In this paper we describe our experience using
SafeDrive for protection and recovery of a variety of
Linux device drivers. In order to apply SafeDrive to these
device drivers, we had to change less than 4% of the
source code. SafeDrive recovered from all 44 crashes
due to injected faults in a network card driver. In ex-
periments with 6 different drivers, we observed increases
in kernel CPU utilization of 4–23% with no noticeable
degradation in end-to-end performance.
1 Introduction
Large systems such as operating systems and web servers
often provide an extensibility mechanism that allows the
behavior of the system to be customized for a particu-
lar usage scenario. For example, device drivers adapt the
behavior of an operating system to a particular hardware
configuration, and web server modules adapt the behav-
ior of the web server to the content or performance needs
This material is based upon work supported by the National
Science Foundation under Grant No. CNS-0509544.
of a particular web site. However, such extensions are
often responsible for a disproportionately large number
of bugs in the system [9, 33], and bugs in an extension
can often cause the entire system to fail. Our goal is
to improve the reliability of extensible systems without
requiring significant changes to the core of the system.
To do so, we must isolate existing extensions, preferably
with little modification, restore system invariants when
they fail, restart them automatically for availability, and
(ideally) restore active sessions.
In this paper, we focus on the specific problem of im-
proving device driver reliability. Previous systems have
attempted to address this problem using some form of
lightweight protection domain for extensions. For exam-
ple, the Nooks project [32, 33] runs Linux device drivers
in an isolated portion of the kernel address space, mod-
ifying kernel API calls to move data into and out of the
extension. This approach prevents drivers from over-
writing kernel memory at the cost of relatively expensive
driver/kernel boundary crossings.
Our system, SafeDrive, takes a different approach
to improving extension reliability. Instead of using
hardware to enforce isolation, SafeDrive uses language-
based techniques similar to those used in type-safe lan-
guages such as Java. Specifically, SafeDrive adds type-
based checking and restart capabilities to existing device
drivers written in C without hardware support or ma-
jor OS changes (i.e., without adding a new protection
domain mechanism). We have four primary goals for
SafeDrive:
• Fine-grained type-based isolation: We detect
memory and type errors on a per-pointer basis,
whereas previous work has only attempted to pro-
vide per-extension memory safety. SafeDrive en-
sures that data of the correct type is used in
kernel API calls and in shared data structures.
This advantage is critical, because it means that
SafeDrive can catch memory and type violations be-
Using Language-Based Techniques
Feng Zhou, Jeremy Condit, Zachary Anderson, Ilya Bagrak
University of California, Berkeley
{zf,jcondit,zra,ibagrak}@cs.berkeley.edu
Rob Ennals
Intel Research Berkeley
robert.ennals@intel.com
Matthew Harren, George Necula, Eric Brewer
University of California, Berkeley
{matth,necula,brewer}@cs.berkeley.edu
We present SafeDrive, a system for detecting and re-
covering from type safety violations in software exten-
sions. SafeDrive has low overhead and requires minimal
changes to existing source code. To achieve this result,
SafeDrive uses a novel type system that provides fine-
grained isolation for existing extensions written in C. In
addition, SafeDrive tracks invariants using simple wrap-
pers for the host system API and restores them when re-
covering from a violation. This approach achieves fine-
grained memory error detection and recovery with few
code changes and at a significantly lower performance
cost than existing solutions based on hardware-enforced
domains, such as Nooks [33], L4 [21], and Xen [13],
or software-enforced domains, such as SFI [35]. The
principles used in SafeDrive can be applied to any large
system with loadable, error-prone extension modules.
In this paper we describe our experience using
SafeDrive for protection and recovery of a variety of
Linux device drivers. In order to apply SafeDrive to these
device drivers, we had to change less than 4% of the
source code. SafeDrive recovered from all 44 crashes
due to injected faults in a network card driver. In ex-
periments with 6 different drivers, we observed increases
in kernel CPU utilization of 4–23% with no noticeable
degradation in end-to-end performance.
1 Introduction
Large systems such as operating systems and web servers
often provide an extensibility mechanism that allows the
behavior of the system to be customized for a particu-
lar usage scenario. For example, device drivers adapt the
behavior of an operating system to a particular hardware
configuration, and web server modules adapt the behav-
ior of the web server to the content or performance needs
This material is based upon work supported by the National
Science Foundation under Grant No. CNS-0509544.
of a particular web site. However, such extensions are
often responsible for a disproportionately large number
of bugs in the system [9, 33], and bugs in an extension
can often cause the entire system to fail. Our goal is
to improve the reliability of extensible systems without
requiring significant changes to the core of the system.
To do so, we must isolate existing extensions, preferably
with little modification, restore system invariants when
they fail, restart them automatically for availability, and
(ideally) restore active sessions.
In this paper, we focus on the specific problem of im-
proving device driver reliability. Previous systems have
attempted to address this problem using some form of
lightweight protection domain for extensions. For exam-
ple, the Nooks project [32, 33] runs Linux device drivers
in an isolated portion of the kernel address space, mod-
ifying kernel API calls to move data into and out of the
extension. This approach prevents drivers from over-
writing kernel memory at the cost of relatively expensive
driver/kernel boundary crossings.
Our system, SafeDrive, takes a different approach
to improving extension reliability. Instead of using
hardware to enforce isolation, SafeDrive uses language-
based techniques similar to those used in type-safe lan-
guages such as Java. Specifically, SafeDrive adds type-
based checking and restart capabilities to existing device
drivers written in C without hardware support or ma-
jor OS changes (i.e., without adding a new protection
domain mechanism). We have four primary goals for
SafeDrive:
• Fine-grained type-based isolation: We detect
memory and type errors on a per-pointer basis,
whereas previous work has only attempted to pro-
vide per-extension memory safety. SafeDrive en-
sures that data of the correct type is used in
kernel API calls and in shared data structures.
This advantage is critical, because it means that
SafeDrive can catch memory and type violations be-
Page 2
fore they corrupt data, even for violations that oc-
cur entirely within the driver. Thus, we can pre-
vent the kernel or devices from receiving incor-
rect data for these cases. SafeDrive can also catch
more memory-related bugs than hardware-based ap-
proaches; specifically, SafeDrive can catch errors
that violate type safety but do not trigger VM faults.
In addition, because errors are caught as they occur,
SafeDrive can provide fine-grained error reports for
debugging.
• Lower overhead for isolation: SafeDrive exhibits
lower overhead in general, particularly for exten-
sions with many crossings (for which Nooks ad-
mits it is a poor fit [33, Sec. 6.4]). Compared with
SafeDrive, hardware-enforced isolation incurs addi-
tional overhead due to domain changes, page table
updates, and data copying. Also, the stronger type
invariants that SafeDrive maintains makes it possi-
ble to check many pointer operations statically.
• Non-intrusive evolutionary design. SafeDrive
provides type safety without changing the structure
of the host system (e.g., the OS kernel) significantly
and without rewriting extensions to use a new lan-
guage or API.
• Protection against buggy (but not malicious) ex-
tensions. In rare cases where true type safety would
require significant changes to the extension or to
the host API, we prefer to trust individual oper-
ations whose safety we cannot verify and gradu-
ally migrate to more complete isolation over time.
For example, our current implementation does not
yet attempt to verify memory allocation, dealloca-
tion, and mapping operations. In addition, we make
no attempt to protect the system from extensions
that abuse CPU, memory, or other resources (unlike
OKE [5] and Singularity [27]). As a consequence,
SafeDrive is able to guard against mistakes made
by the author of the extension but does not attempt
to protect against a malicious adversary capable of
exploiting specific behaviors of our system.
C and its variants have a number of important con-
structs that can be used to cause violations. In addition to
the most obvious issue of out-of-bounds array accesses,
C also has fundamental problems due to unions, null-
terminated strings, and other constructs. To transform a
driver written in C (which includes all Linux drivers) into
one that obeys stricter type safety requirements, we must
fix all of these flaws without requiring extensive rewrites
and ideally without requiring modifications to the kernel.
The existing approaches to type safety for C involve
the use of “fat” pointers, which contain both the pointer
and its bounds information. CCured [24], for example,
can make a legacy C program memory-safe by convert-
ing most of its pointers into fat pointers and then insert-
ing run-time checks to enforce bounds constraints. How-
ever, this approach is not realistic for drivers or for the
kernel, since it modifies the layout of every structure con-
taining pointers as well as every kernel API function that
uses pointers. To use CCured effectively in this context,
we would need to “cure” the entire kernel and all of its
drivers together, which is impractical.
Instead, SafeDrive uses a novel type system for point-
ers, called Deputy, that can enforce memory safety for
most programs without resorting to the use of fat point-
ers and thus without requiring changes to the layout of
data or the driver API. The key insight is that most of the
required pointer bounds information is already present in
the driver code or in the API—just not in a form that the
compiler currently understands. Deputy uses type anno-
tations, particularly in header files for APIs and shared
structures, to identify this information in places where it
already exists.
Although adding annotations to kernel headers or
driver code may seem like a burden, there are many rea-
sons why this approach is a practical one. First, the re-
quired annotations are typically very simple, allowing
programmers to easily express known relationships be-
tween variables and fields (e.g., “p points to an array of
length n”). Second, the cost of annotating kernel headers
is a one-time cost; once the headers are annotated, the
marginal cost of annotating additional drivers is much
smaller. Third, annotations are only mandatory for the
driver-kernel interface, while the unannotated data struc-
tures internal to the driver can use fat pointers. About
600 Deputy annotations were added to kernel headers for
the 6 drivers we tested (Section 5.2).
Any solution based on run-time enforcement of iso-
lation must provide a mechanism for dealing with vi-
olations. In SafeDrive we assume that extensions are
restartable provided that certain system invariants are re-
stored. In the case of Linux device drivers, invariant
restoration consists of releasing a number of resources
allocated by the driver and unregistering any name space
entries registered by the driver (e.g., new device entries
or file system entries), both of which we will refer to as
updates. In order to undo updates during fault recovery,
we track them using wrappers for the relevant API calls.
Because SafeDrive allows an extension to operate safely
in the kernel address space without the use of a hardware-
enforced protection domain, the task of managing and
recovering kernel resources is greatly simplified.
As with Nooks, SafeDrive cannot prevent every
extension-related crash, nor can it guarantee that mali-
cious code cannot abuse the machine or the device. How-
ever, because SafeDrive can catch errors that corrupt the
driver itself without corrupting other parts of the system,
we expect it to catch more errors, and we expect it to
cur entirely within the driver. Thus, we can pre-
vent the kernel or devices from receiving incor-
rect data for these cases. SafeDrive can also catch
more memory-related bugs than hardware-based ap-
proaches; specifically, SafeDrive can catch errors
that violate type safety but do not trigger VM faults.
In addition, because errors are caught as they occur,
SafeDrive can provide fine-grained error reports for
debugging.
• Lower overhead for isolation: SafeDrive exhibits
lower overhead in general, particularly for exten-
sions with many crossings (for which Nooks ad-
mits it is a poor fit [33, Sec. 6.4]). Compared with
SafeDrive, hardware-enforced isolation incurs addi-
tional overhead due to domain changes, page table
updates, and data copying. Also, the stronger type
invariants that SafeDrive maintains makes it possi-
ble to check many pointer operations statically.
• Non-intrusive evolutionary design. SafeDrive
provides type safety without changing the structure
of the host system (e.g., the OS kernel) significantly
and without rewriting extensions to use a new lan-
guage or API.
• Protection against buggy (but not malicious) ex-
tensions. In rare cases where true type safety would
require significant changes to the extension or to
the host API, we prefer to trust individual oper-
ations whose safety we cannot verify and gradu-
ally migrate to more complete isolation over time.
For example, our current implementation does not
yet attempt to verify memory allocation, dealloca-
tion, and mapping operations. In addition, we make
no attempt to protect the system from extensions
that abuse CPU, memory, or other resources (unlike
OKE [5] and Singularity [27]). As a consequence,
SafeDrive is able to guard against mistakes made
by the author of the extension but does not attempt
to protect against a malicious adversary capable of
exploiting specific behaviors of our system.
C and its variants have a number of important con-
structs that can be used to cause violations. In addition to
the most obvious issue of out-of-bounds array accesses,
C also has fundamental problems due to unions, null-
terminated strings, and other constructs. To transform a
driver written in C (which includes all Linux drivers) into
one that obeys stricter type safety requirements, we must
fix all of these flaws without requiring extensive rewrites
and ideally without requiring modifications to the kernel.
The existing approaches to type safety for C involve
the use of “fat” pointers, which contain both the pointer
and its bounds information. CCured [24], for example,
can make a legacy C program memory-safe by convert-
ing most of its pointers into fat pointers and then insert-
ing run-time checks to enforce bounds constraints. How-
ever, this approach is not realistic for drivers or for the
kernel, since it modifies the layout of every structure con-
taining pointers as well as every kernel API function that
uses pointers. To use CCured effectively in this context,
we would need to “cure” the entire kernel and all of its
drivers together, which is impractical.
Instead, SafeDrive uses a novel type system for point-
ers, called Deputy, that can enforce memory safety for
most programs without resorting to the use of fat point-
ers and thus without requiring changes to the layout of
data or the driver API. The key insight is that most of the
required pointer bounds information is already present in
the driver code or in the API—just not in a form that the
compiler currently understands. Deputy uses type anno-
tations, particularly in header files for APIs and shared
structures, to identify this information in places where it
already exists.
Although adding annotations to kernel headers or
driver code may seem like a burden, there are many rea-
sons why this approach is a practical one. First, the re-
quired annotations are typically very simple, allowing
programmers to easily express known relationships be-
tween variables and fields (e.g., “p points to an array of
length n”). Second, the cost of annotating kernel headers
is a one-time cost; once the headers are annotated, the
marginal cost of annotating additional drivers is much
smaller. Third, annotations are only mandatory for the
driver-kernel interface, while the unannotated data struc-
tures internal to the driver can use fat pointers. About
600 Deputy annotations were added to kernel headers for
the 6 drivers we tested (Section 5.2).
Any solution based on run-time enforcement of iso-
lation must provide a mechanism for dealing with vi-
olations. In SafeDrive we assume that extensions are
restartable provided that certain system invariants are re-
stored. In the case of Linux device drivers, invariant
restoration consists of releasing a number of resources
allocated by the driver and unregistering any name space
entries registered by the driver (e.g., new device entries
or file system entries), both of which we will refer to as
updates. In order to undo updates during fault recovery,
we track them using wrappers for the relevant API calls.
Because SafeDrive allows an extension to operate safely
in the kernel address space without the use of a hardware-
enforced protection domain, the task of managing and
recovering kernel resources is greatly simplified.
As with Nooks, SafeDrive cannot prevent every
extension-related crash, nor can it guarantee that mali-
cious code cannot abuse the machine or the device. How-
ever, because SafeDrive can catch errors that corrupt the
driver itself without corrupting other parts of the system,
we expect it to catch more errors, and we expect it to
Sign up today - FREE
Mendeley saves you time finding and organizing research. Learn more
- All your research in one place
- Add and import papers easily
- Access it anywhere, anytime
Start using Mendeley in seconds!
Readership Statistics
13 Readers on Mendeley
by Discipline
8% Mathematics
by Academic Status
62% Ph.D. Student
15% Assistant Professor
8% Student (Master)
by Country
38% China
23% United States
8% Bangladesh


