Thursday, May 21, 2009

. NET Type Safety

Introduction

Before going any further, let us first have a look at what is type safe code and why is it relevant to secure programming.Type safety is certainly one of the most confusing aspects for somebody learning .Net. When .Net was released, it had a tag attached claiming that the programs developed in .Net are more stable when compared to programs developed using VB or VC++. This just adds to the confusion if you do not understand type safety very well.

Type safety
Type safe code can access only the memory locations that it has permission to execute. Type safe code can never access any private members of an object. Type safe code ensures that objects are isolated from each other and are therefore safe for inadvertent or malicious corruption.CLR performs a mandatory type safety check, called verification, during JIT compilation. This is done by a tool called peverify.exe, which examines the Microsoft Intermediate Language and the metadata included in the assembly. If an assembly (or code) is successfully verified as type safe, it is called verifiably type safe code.The code is said to be verifiably type safe when any references to the types inside that code are strictly compatible with the types they are referring to.Code need no always be verifiably type safe. This is for the situations where you have both managed as well as un-safe code in the same assembly. Remember, un-safe code need no always be un-verifiable code.Though this is a mandatory process performed by the CLR, it can be skipped by providing necessary permissions to that assemblyCLR ensures that the type safe code does not end up in any undesirable situations like calling native or unmanaged code or performing any malicious operations.

What is Type Safety?
Type safe means preventing programs from accessing memory outside the bounds of an object's public properties A programming language is type safe when the language defines the behavior for when the programmer treats a value as a type to which it does not belong. Type safety requires that well-typed programs have no unspecified behavior (i.e., their semantics are complete). Languages such as C and C++, which allow programmers to access arbitrary memory locations, are not type safe. An unsafe programming language supports operations (such as accessing arbitrary memory locations), which are not defined in terms of the language semantics. Type safety is a property of the programming language, and not of the programs themselves. What is Type Safety?Type safe means preventing programs from accessing memory outside the bounds of an object's public properties A programming language is type safe when the language defines the behavior for when the programmer treats a value as a type to which it does not belong. Type safety requires that well-typed programs have no unspecified behavior (i.e., their semantics are complete). Languages such as C and C++, which allow programmers to access arbitrary memory locations, are not type safe. An unsafe programming language supports operations (such as accessing arbitrary memory locations), which are not defined in terms of the language semantics. Type safety is a property of the programming language, and not of the programs themselves.

How does Type Safety affect us?
1) Type-safe code accesses only the memory locations it is authorized to access.
2) For example, type-safe code cannot directly read values from another object's private fields or code areas.
3) It accesses types only in well-defined, allowable ways, thereby preventing overrun security breaches.
4) Type safety helps isolate objects from each other and therefore helps protect them from inadvertent or malicious corruption. 5) It also provides assurance that security restrictions on code can be reliably enforced.

Why is Type Safety Important?
1) Type-safety is important for assembly isolation and security enforcement.
2) When code is type- safe, the common language runtime can completely isolate assemblies from each other.
3) This isolation helps ensure that assemblies cannot adversely affect each other and it increases application reliability.
4) Type-safe components can execute safely in the same process even if they are trusted at different levels.

Writing Type-Safe Code
Type-safe code is code that accesses types only in well-defined, allowable ways. Given a valid object reference, type-safe code can access memory at fixed offsets corresponding to actual field members. However, if the code accesses memory at arbitrary offsets outside the range of memory that belongs to that object's publicly exposed fields, it is not type-safe.

How is Type Safety ensured?
The Common Language runtime manages code at execution time. It provides core services such as ‘memory management’, ‘thread management’ and remoting. It also enforces strict type safety and promotes security and robustness.
1) During just-in-time compilation, an optional verification process examines the metadata and intermediate language of a method to verify that they are type-safe.
2) If the code has permission to bypass verification, then this process is skipped.
3) The common language runtime manages code at execution time, providing core services such as memory management, thread management, and remoting, and enforces strict type safety and other forms of code accuracy that promote security and robustness.
Example:
double dblX=10.59;
int intY=5;
intY = dblX; / / this causes an Error

What is Verifiable Type Safe Code?
JIT compilation performs a process called verification that examines code and attempts to determine whether the code is type-safe.
Code that is proven during verification to be type-safe is called verifiably type- safe code.
Code can be type-safe, yet not be verifiably type-safe, due to the limitations of the verification process or of the compiler. Not all languages are type-safe, and some language compilers cannot generate verifiably type-safe managed code.

Verification process:
As part of compiling MSIL to native code, code must pass a verification process unless an administrator has established a security policy that allows code to bypass verification. Verification examines MSIL and metadata to find out whether the code is type safe, which means that it only accesses the memory locations it is authorized to access. The runtime relies on the fact that the following statements are true for code that is verifiably type safe:
a) A reference to a type is strictly compatible with the type being referenced
b) Only appropriately defined operations are invoked on an object
c) Identities are what they claim to be.
During the verification process, MSIL code is examined in an attempt to confirm that the code can access memory locations and call methods only through properly defined types.

Additionally, verification inspects code to determine whether the MSIL has been correctly generated, because incorrect MSIL can lead to a violation of the type safety rules. The verification process passes a well-defined set of type-safe code, and it passes only code that is type safe. However, some type-safe code might not pass verification because of limitations of the verification process, and some languages, by design, do not produce verifiably type-safe code. If type-safe code is required by security policy and the code does not pass verification, an exception is thrown when the code is run.

What if the Code is not verifiably type safe?
1) Code that is not verifiably type-safe can attempt to execute if security policy allows the code to bypass verification.
2) But because type-safety is an essential part of the runtime's mechanism for isolating assemblies, security cannot be reliably enforced if code violates the rules of type- safety.
3) By default, code that is not type-safe is only allowed to run if it originates from the local machine.
4) All code that is not type-safe must have been granted ‘SecurityPermission’ with the passed ‘enum’ member ‘SkipVerification’ to run.

Tools:
How to determine if the code is type safe? Some language compilers generate verifiably type-safe code only when you avoid certain language constructs. The .NET Framework SDK PEVerify tool can be used to determine whether the code is verifiably type-safe.

Usage of PEVerify.exe:
C:\>PEverify.exe
Microsoft (R) .NET Framework PE Verifier. Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.Usage:

PEverify [Options]

Options:
/IL Verify only the PE structure and IL
/MD Verify only the PE structure and MetaData
/UNIQUE Disregard repeating error codes
/HRESULT Display error codes in hex format
/CLOCK Measure and report verification times
/IGNORE=[,...] Ignore specified error codes
/IGNORE=@ Ignore error codes specified in
/QUIET Display only file and Status. Do not display all errors.
/VERBOSE Display additional info in IL verification error messages.
/NOLOGO Don't display product version and copyright info.

Note: By default, MD is verified and then if there were no errors, IL isverified. If /MD /IL options are specified, IL is verified even ifthere were MD verification errors.

Example:
C:\Test\bin\Debug>peverify Test.exe

Microsoft (R) .NET Framework PE Verifier. Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.

All Classes and Methods in test.exe Verified.

Code Sample:
//This code fails the verification
unsafe
{
int* ptr = (int*) 0;
*ptr = 1000;
}
C:\>peverify C:\Test\bin\Debug\Test.exe

Microsoft (R) .NET Framework PE Verifier. Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.
[IL]: Error: [C:\Test\bin\Debug\Test.exe : Test.Program::Main][offset 0x00000004][found Native Int][expected unmanaged pointer] Unexpected type on the stack
.[IL]: Error: [C:\Test\bin\Debug\Test.exe : Test.Program::Main][offset 0x00000005] Unmanaged pointers are not a verifiable type.2 Errors Verifying Program.exe

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.