57
Static Analysis and Verification of Drivers Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Embed Size (px)

Citation preview

Page 1: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Static Analysis and Verification of Drivers

Jakob LichtenbergSoftware Development Engineer SDV

Adam ShapiroProgram ManagerDonn TerrySoftware Development Engineer PFD

Page 2: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Session OutlineStatic analysis tools:

What they areBenefits

PREfast for Drivers (PFD)Static Driver Verifier (SDV)Summary

Page 3: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

What Is Static Analysis?

Compile-time analysis of the source program:

Like code inspection, but performed by a tool.

Compile-time type-checking a simple example.

Looks for violations of well-defined constraints:

Procedure contracts or API contracts .

Examples of bugs found by Static Analysis:

p = NULL;…f(p);

f() requires p to be non-NULL

Completing the same IRP twice:

IoCompleteRequest (IRP);

...

IoCompleteRequest (IRP);

Page 4: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Why Static Analysis?

Cheap bugs!Rule of thumb

“A defect that costs $1 to fix on the programmer’s desktop costs $100 tofix once it is incorporated into a complete program and many thousands of dollarsif it is identified only after the softwarehas been deployed in the field.”“Building a Better Bug Trap” – The Economist, June

2003

Page 5: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Tools That Make You More Effective

Push-button technology.

100% path coverage:

At little cost (let a computer do it)

Quickly (minutes or hours versus weeks)

Defects are discovered early:

Even before device hardware is available

Before designing test cases

Often while still coding

Defect reports are easy to use:

A direct reference to the defective path (or point)in the source code reduces cost of debugging

Page 6: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Static Analysis – How Does It Work?

The tool builds an abstract model of a driver and exhaustively inspects execution along all paths:

The abstract model is simpler: it’s reduced...

It’s so much simpler that it’s possible to have it inspected (“simulated”) exhaustively.

Over-approximation of the driver:The control part remains the same .

All paths are preserved and treated equally .

The data state is over-approximated.

if argument x is not constrained, assume any value .

if (x>0) guards the point of interest, keep track of Boolean (x>0),but not integer value of x: Boolean is simpler than integer.

if (x > 0) { IoCompleteRequest (Irp); }

Page 7: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Static Analysis – Not a Silver Bullet

Does not replace functional testing.

Targets violations of a given set of well-defined constraints.

Principal limitations:It doesn’t know about every possible error.

Algorithms are based on source code abstraction and heuristics, which results in both false positives and false negatives.

It is a useful tool.

Page 8: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Our Static Tools for Drivers

PREfast For Drivers (PFD):Lightweight and fast (runs in minutes).

Easy to use early in development – start early:

Use on any code that compiles.

Limited to procedure scope.

Works on any code, C and C++.

Finds many local violations.

Static Driver Verifier (SDV):Extremely deep analysis (runs in hours).

More useful in the later stages of development:

Requires complete driver.

Works over the whole driver (inter-procedural).

Limited to WDM and KMDF and to C (more planned).

Think: Compile –Time Driver Verifier.

Finds deep bugs.

Page 9: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Driver Tools Relationship

Easy Reproducibility Hard

Depth

DriverVerifier Static Driver

Verifier

PREfast for drivers

HardEase of Use

Complex

A problem has been detected and Windows has been shut down to prevent damage to your computer.

DRIVER_IRQL_NOT_LESS_OR_EQUAL

If this is the first time you've seen this stop error screen,restart your computer. If this screen appears again, followthese steps:

Check to make sure any new hardware or software is properly installed.If this is a new installation, ask your hardware or softwareManufacturer for any Windows updates you might need.

If problems continue, disable or remove any newly installed hardwareor software. Disable BIOS memory options such as caching or shadowing.If you need to use Safe Mode to remove or disable components, restartyour computer, press F8 to select Advanced Startup Options, and thenselect Safe Mode

Technical information:

*** STOP: 0x00000001 (0x0000000,00000002,0x00000000,0x00000000)

Page 10: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

PFD: PREfast For DriversFast (2 to 5 times compile time, usually).

Finds many “inadvertent” errors and some “hard” ones.

Works on code that compiles; doesn’t need to run.

Some things it can find:Null pointer and uninitialized variable (along an unusual path)

Local leaks (memory and resource)

Mismatched parameters

Forgot to check result

Format/list mismatch

Misuse of IRQLs (some)

Various special cases that are easily missed (such as Cancel IRQL)

Proper use of callback/function pointers

Page 11: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

PFD: Driver-Specific Resource Leakvoid LeakSample(BOOLEAN Option1){ NTSTATUS Status; KIRQL OldIrql; BufInfo *pBufInfo; KeAcquireSpinLock(MyLock,&OldIrql); //... if (Option1) { pBufInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(BufInfo), 'fuB_'); if (NULL==pBufInfo) { return STATUS_NO_MEMORY; } //...

KeReleaseSpinLock(MyLock, OldIrql); return STATUS_SUCCESS;}//...

Page 12: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

void LeakSample(BOOLEAN Option1){ NTSTATUS Status; KIRQL OldIrql; BufInfo *pBufInfo; KeAcquireSpinLock(MyLock,&OldIrql); //... if (Option1) { pBufInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(BufInfo), 'fuB_'); if (NULL==pBufInfo) { return STATUS_NO_MEMORY; } //...

KeReleaseSpinLock(MyLock, OldIrql); return STATUS_SUCCESS;}//...

PFD: Driver-Specific Resource Leak

warning 28103: Leaking the resource stored in 'SpinLock:MyLock'.

Page 13: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

void LeakSample(BOOLEAN Option1){ NTSTATUS Status; KIRQL OldIrql; BufInfo *pBufInfo; KeAcquireSpinLock(MyLock,&OldIrql); //... if (Option1) { pBufInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(BufInfo), 'fuB_'); if (NULL==pBufInfo) { KeReleaseSpinLock(MyLock, OldIrql); return STATUS_NO_MEMORY; } //...

KeReleaseSpinLock(MyLock, OldIrql); return STATUS_SUCCESS;}//…

PFD: Driver-Specific Resource Leak

Page 14: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD
Page 15: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD
Page 16: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Why Annotate?Good Engineering Practice

Precisely describe the “part” you’re building and the contract that represents.Enable automatic checking. Tells tools things they can’t infer.Effective (and checked) documentation:

Programmers don’t have to guess/experiment.

Code and documentation don’t drift apart.

Comments are nice, but…

Page 17: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Annotation BenefitsRecord and express the contract:

Developers know what the contract is.

Is the contract a good one?

Automatic checking: that is, “cheap” bugs:

The sooner a bug is found, the less expensive it is to fix.

Annotated code finds many more bugs (with less noise) than un-annotated code.

Code that enters test with fewer “easy” bugs makes testing far more efficient – less wasted time for finding and fixing easy bugs.

Page 18: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

PFD: Annotations – Example

wchar_t * wmemset( __out_ecount(s) wchar_t *p, __in wchar_t v, __in size_t s);

__in: the parameter is input to the function

__out: the parameter is output from the function

__out_ecount(s): the parameter is a buffer with s elements

If the parameter p doesn’t contain at least s elements, PFD will yield a warning.

Page 19: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

PFD - Callback Annotations

typedefVOIDDRIVER_CANCEL ( __in struct _DEVICE_OBJECT *DeviceObject, __in struct _IRP *Irp );

typedef DRIVER_CANCEL *PDRIVER_CANCEL;

wdm.h

Driver.h DRIVER_CANCEL MyCancel;

Driver.c VOIDMyCancel ( struct _DEVICE_OBJECT *DeviceObject, struct _IRP *Irp ) { … }

Page 20: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Problem: Floating Point

If your driver uses floating point, you must be very careful to protect the hardware.

It’s easy to forget that you used it.

It’s very hard to find in test, typically not repeatable, and blue-screen is the preferable symptom.

It can span multiple functions.

Page 21: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

longintSqrt(long i){ return (long) sqrt((double)i);}

Example: Floating Point

Page 22: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Example: Floating Point

longintSqrt(long i){ return (long) sqrt((double)i);}

…if (KeSaveFloatingPointState(b)){ … intSqrt(…) … KeRestoreFloatingPointState(b);}else // deal with error

… intSqrt(…) ……

Page 23: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Example: Floating Point

__drv_floatUsedlongintSqrt(long i){ return (long) sqrt((double)i);}

…if (KeSaveFloatingPointState(b)){ … intSqrt(…) … KeRestoreFloatingPointState(b);}else // deal with error

… intSqrt(…) ……

Page 24: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Static Driver Verifier

Jakob LichtenbergSoftware Development EngineerMicrosoft Corporation

Page 25: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Static Analysis Tools for Drivers

Two complementary technologies provided in the WDK:

PREfast for Drivers: Look inside every procedure for possible violations.

Static Driver Verifier: Look along paths, cross inter-procedural boundaries.ReadFoo ( PIRP Irp )

{ PIRP p = NULL; ... if (status) { IoCompleteRequest(p); }}

ReadFoo ( PIRP Irp ) { ... status = Bar (Irp); if (status) { IoCompleteRequest(Irp); } }

Bar ( PIRP Irp ) { ... IoCompleteRequest(Irp); return STATUS_SUCCESS;}

XX

XX

Page 26: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Static Analysis Tools for DriversPREfast for Drivers (PFD):

Lightweight and fast (runs in minutes).

Easy to use early in development – start early.

Use on any code that compiles.

Limited to a procedure scope; each procedure analyzed independently.

Annotations improve precision.

Works on any code, C and C++.

Finds many local violations.

Static Driver Verifier (SDV):

Extremely deep analysis (runs in hours).

More useful in the later stages of development.

Requires complete driver.

Works over the whole driver, along every path, crossing inter-procedural boundaries.

Annotations not necessary.

Limited to WDM or KMDF and to C.

Finds a few deep bugs.

Page 27: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Static Driver Verifier

What Static Driver Verifier does:Global analysis of entire driver.

Looks for violations of DDI constraints.

SDV for WDM:68 rules covering aspects such as IRPs, IRQL, Plug and Play, and synchronization rules.

New: SDV for KMDF:52 rules covering aspects such as DDI ordering, device initialization, request completion, and request cancellation.

Availability: from Windows Longhorn Server WDK.

Limitations:Only pure C drivers.

Up to 50 K lines of code.

Page 28: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Example

Device DriverInterface

IoCompleteRequest

Driver

Dispatch Routine

I/O System

Irp

Irp Irp

Page 29: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Example

Device DriverInterface

IoCompleteRequest

Driver

Dispatch Routine

I/O System

Irp

Irp Irp

XX

Page 30: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Example

Parport driver sample in Win XP SP1 DDK

Page 31: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Example

One defect foundXX

Page 32: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

First CompletionDouble Completion

Page 33: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Example

Rule Passesü

Server 2003 SP1 DDK

Page 34: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Example

PptDispatchClose ( Irp ) P4CompleteRequest ( Irp )

IoCompleteRequest( Irp );XX

PptFdoClose( Irp )

P4CompleteRequestReleaseRemLoc ( Irp )

IOMngr

Page 35: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Concepts

library.c

more_code.c

driver.c

üXXSDV

Page 36: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Concepts

SDVRules

Verification Engine

OS Model

library.c

more_code.c

driver.c

üXX

Page 37: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Rules

SDVVerificatio

n Engine

OS Model

library.c

more_code.c

driver.c

Rules

üXX

Page 38: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

A DDI Constraint Is…A Rule

Rule 1

Page 39: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

…One More Rule…

Rule 2

Page 40: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

…And Yet Another Rule

Rule 3

Page 41: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Rules

SDV comes with:68 WDM rules52 KMDF rules

Each rule captures an aspect of an interface requirements.Rules are written in a C-like language and define:

State declarations in form of C-style variables.Events that are associated with DDI functions.

Page 42: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

SpinLock Rule

Device DriverInterface

KeAcquire SpinLock

KeRelease

SpinLock

Driver

Entry Point

I/O System

Page 43: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

SpinLock Rule

Device DriverInterface

KeAcquire SpinLock

KeRelease

SpinLock

Driver

Entry Point

I/O System

state { enum {unlocked, locked} s = unlocked;}

Abort

Acq

uir

e Rele

ase

Dri

ver

calle

d

Driv

er re

turn

s

Unlocked

Acquire

Driver r

eturn

s

Locked

Release

Page 44: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

SpinLock Rule

Device DriverInterface

KeAcquire SpinLock

KeRelease

SpinLock

Driver

Entry Point

I/O System

RunDispatchFunction.exit{ if (s != unlocked) abort;}

KeAcquireSpinLock.entry{ if (s != unlocked) abort; else s = locked;}

KeReleaseSpinLock.entry{ if (s != locked) abort; else s = unlocked;}

state { enum {unlocked, locked} s = unlocked;}

Page 45: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Operating System Model

SDVVerificatio

n Engine

library.c

more_code.c

driver.c

Rules

OS Model

üXX

Page 46: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Operating System Model

Exercises the driver:Calls DriverEntryCalls Dispatch functionsCalls ISRs

Models certain aspects of the operating system state:

Like the current interrupt request level (IRQL)

Models device driver interface functions:

Like the IoCallDriver DDI

Device DriverInterface

Driver

I/O System

Page 47: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Operating System Model

Device DriverInterface

Driver

void main() { int choice, status; … status = DriverEntry(…); … status = AddDevice(…); … … switch (choice) { case 0: status = DriverRead(…); break; case 1: status = DriverWrite(…); break; … case 28: status = DriverCreate(…); break; default: status = DriverClose(…); break; }}

I/O System

Clos

e

Crea

te

Writ

e

Read

Dri

verE

ntr

y

AddD

evic

e

Page 48: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Operating System Model

Device DriverInterface

Driver

NTSTATUS IoCallDriver( …, PIRP Irp ) { int choice; … switch (choice) { case 0: … Irp->PendingReturned = 0; return STATUS_SUCCESS;

case 1: … Irp->PendingReturned = 0; return STATUS_UNSUCCESSFUL;

default: … Irp->PendingReturned = 1; return STATUS_PENDING;;

}}

I/O System

IoCallDriver

SUCC

ESS

UNSUCCE

SSFU

L

PENDIN

G

Page 49: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Verification Engine

SDVlibrary.c

more_code.c

driver.c

Rules

OS Model

Verification Engine

üXX

Page 50: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Verification Engine: Example

Executes:Your driver in the context of the operating system model.

While keeping track of the rule state.

While looking for rule violations.

Checks each and every path of the driver.

Implements worst-case behavior.

Symbolic model checker:Strategy: Throw away many irrelevant details through aggressive, but correct, abstraction.

Device DriverInterface

Driver

I/O System

IoCallDriver

SUCC

ESS

UNSUCCE

SSFU

L

PENDIN

G

Clos

e

Crea

te

Writ

e

Read

Dri

verE

ntr

y

AddD

evic

e

Page 51: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Verification Engine: Example

Device DriverInterface

Driver

I/O System

IoCallDriver

SUCC

ESS

UNSUCCE

SSFU

L

PENDIN

G

Clos

e

Crea

te

Writ

e

Read

Dri

verE

ntr

y

AddD

evic

e

… Read

Write

Create Close

SUCCESS ü ü ü ü …

UNSUCCESFUL ü ü ü …

PENDING ü ü ü ü …

… … … … … …

XX

Page 52: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Quality

Comprehensive path coverage:

Driver is exercised in a hostile environment.

Verifies all possible paths in the drivers.

Verifies cross-function calls.

Verifies driver together with supporting libraries.

But there are places where SDV is imprecise:

Only one driver (not the entire driver stack).

Operating system model imperfect.

The verification engine is not precise.

Page 53: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

ExperienceFinds deep bugs not found by testing:

1 bug on average for a sample driver in Server 2003 DDK-3677.

Well-tested drivers are often clean.

A dozen true bugs in a fresh driver.

Low noise level:Less than 1 false bug reported per driver.

2 real bugs for 1 false bug on DDK-3677 samples.

Performance:WDM: Runs in a few hours, but may need to run overnight.

KMDF: Much, much faster.

New: SDV will take advantage of all available CPU cores.

Page 54: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Static Analysis Can…

The business case:Reduce risk of expensive after-deployment bugs.

Reduce time to market.

Reduce cost of code review and testing.

Improve code quality.

Achieve higher

test coverage.

The development case:

Find/prevent bugs earlier.

More directly and obviously.

Find/prevent “hard-to-test” bugs.

Make you more efficient.

Page 55: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Use Static Analysis... Wisely

It doesn’t know about all possible errors.It doesn’t make testing unnecessary.Both false positives and false negatives can be misleading.Static Analysis Tools complement testing.

Page 56: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Static Analysis Tools for Drivers

PREfast for Drivers

Static Driver Verifier

Driver Models

Any WDMKMDF

Applicability

C and C++ C only

Issues found

Local defectsEasy to fixHigh volume

Global defectsHarder to fixLow volume

Development Cycle

Apply early: “When the driver compiles”

Run often…

Apply later:“When the basic structure of the driver is in place”

Run ad hoc or overnight…

Page 57: Jakob Lichtenberg Software Development Engineer SDV Adam Shapiro Program Manager Donn Terry Software Development Engineer PFD

Disclaimer© 2007 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.

The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.