Category: Debugging

Using and Debugging Environment Variables

By , February 28, 2022 6:31 pm

Environment variables can be used to configure the various Validator tools that we provide. Configuration provides flexibility, but it also provides avenues for failure. In this article I’m going to show you how you can configure our tools using environment variables, and how you can debug them if that configuration goes wrong.

In this article I’m going to use Coverage Validator, but these topics also apply to Bug Validator, Memory Validator, Performance Validator and Thread Validator.

Should I use % or $?

Note that you can use % to delimit env vars, as in %PATH%, or you can use $ to delimit env vars, as $PATH$. If you use % the command shell (or batch file processor) will do the substitution before the Validator gets to see the command line. If you use $ the command shell will not do the substitution for you, the Validator will do the substitution.

Using environment variables

Any Validator command line option that also takes an argument can have that argument supplied by an environment variable. For example:

  • -program e:\test\test.exe
  • -directory e:\testArea
  • -arg 1
  • -arg “strawberry ice cream”

Using environment variables the above could become

  • -program $TEST_PROGRAM$
  • -directory $TEST_AREA$
  • -arg $TEST_COUNT$
  • -arg $FAVOURITE_ICE_CREAM$

A slightly more complicated command line could look like this:

coverageValidator.exe -program $BUILD_DIR$\test.exe -directory $TEST_DIR$ -fileLocations $TEST_DIR$\fileLocations.cvxfl

This command line starts test.exe located in directory identified in the BUILD_DIR environment variable, with the current directory set to the directory identified in the TEST_DIR environment variable, and sets the source code file locations to the list of files specified in the fileLocations.cvxfl file in the directory identified in the TEST_DIR environment variable.

The fileLocations.cvxfl file can also contain environment variable definitions:

[Files]
$SRC_DIR$\model
$SRC_DIR$\view
$SRC_DIR$\controller
$SRC_DIR$\test 
[Third]
[PDB]
[MAP]

The environment variables in the fileLocations file (or any other file where environment variables are used) are substituted for the environment variable definitions when that file is loaded. In the example above, if the SRC_DIR environment variable had the value “e:\abcMusicTutor” the file contents lines would evaluate to:

[Files]
e:\abcMusicTutor\model
e:\abcMusicTutor\view
e:\abcMusicTutor\controller
e:\abcMusicTutor\test 
[Third]
[PDB]
[MAP]

As you can see the use of environment variables allows you to setup a general framework for controlling Coverage Validator from the command line, but then customise that usage by setting the environment variables appropriately.

What happens when things go wrong?

When using environment variables there are a few things that can go wrong:

  • The environment variable isn’t defined
  • The value in the environment variable is wrong
  • The value in the environment variable doesn’t exist

You can view the environment variables in the Validator, the target application, and any environment variable substitution errors using the Env Vars tab in the Diagnostic part of the Validator.

To view the environment variable substitution errors choose “Environment variable errors” in the combo box on the Env Vars tab.

Environment variable not defined

All the Validator tools check if an environment is defined or not. Environment variables that are not defined are logged and reported on the Diagnostic Environment Variables display. In addition, any locations where the substituted value is meant to be used as a directory or filename (the fileLocations example) will show the non-substituted path ($SRC_DIR$\model) and will be highlighted in an error colour indicating an invalid path.

Example: These are values loaded via -fileLocations. The environment variable SRC_DIR does not exist. The file locations dialog lists the invalid paths in red.

Example: These are values loaded via -fileLocations. The environment variable SRC_DIR does not exist. The columns show the environment variable name, the original string value being parsed, a comment indicating where this option was encountered and a timestamp.

Environment variable is wrong

Each Validator can’t tell if a value is wrong. This is a subjective matter. For example: -numSession $NUM_SESSION$.

If NUM_SESSIONS is set to 1 when you’d prefer it to be 2, that’s an error will stop you loading multiple sessions and comparing them, but it’s not an error that will prevent Coverage Validator from performing code coverage.

As such debugging this error is something that requirement your judgement. Examining the Diagnostic Environment Variables displays will help you identify what the problem is.

Value in the environment variable doesn’t exist

A common error case is where the environment variable specifies a directory but the directory does not exist. A related error case is when a filename is specified but does not exist. For cases like fileLocations where the values loaded can be inspected in the settings displays you will see the specified folder location listed in an error colour indicating an invalid path.

The best way to debug this class of error is to open the settings dialog (Settings->Edit Settings…) and examine all settings looking for paths in the error code (the default for this is red).

Example: Here environment variable SRC_DIR did exist and was substituted for e:\abcMusicTutor, but that directory does not exist.

How do I view the Validator’s environment variables?

To view the Validator’s environment variables, on the Diagnostic Env Vars tab, choose “??? Validator” in the combo box, in this example “Coverage Validator”. The environment variables will be shown below.

How do I view the target application’s environment variables?

To view the target application’s variables, on the Diagnostic Env Vars tab, choose “Application being monitored” in the combo box. The environment variables will be shown below.

If you launched the target application from the Validator the target application’s environment variables will be similar to those in the Validator, but with some additional env vars to control .Net profilers and and some other SVL_ prefixed env vars to communicate various data to Software Verify components that are loaded.

If you launched the target application as a standalone application, or service and used one of our APIs to connect to the Validator, the environment variables shown will reflect those in force at the time the application/service was started, and the account that application/service is running on.

Exceptions Codes you’ve never heard of

By , January 28, 2022 3:33 pm

If you write software for any length of time you’ll have seen your software fail in a variety of ways.

Exceptions are a common cause of application failure.

Exceptions are also used to handle unusual error cases, with the intention that the calling code is aware of this exception and will handle it, ensuring that the application doesn’t fail.

For this article I’m going to be discussing Win32 structured exception handling (SEH) exception codes. I’m also going to assume that you’re using Visual Studio, a Windows SDK or another IDE, such as C++ Builder, or Delphi.

Most of the exception codes you’ve likely encountered in your career can be found declared in ntstatus.h

Depending on which version of Visual Studio you’re using some exception codes may not present.

I’m going to list exception codes that we’ve found during the process of developing software tools at Software Verify.

Many of these exception codes are not documented, or are only present in recent versions of SDKs, which you may not be using because you’re still using an older Visual Studio.

Where possible we’ll indicate additional sources of information about a given exception code. We’ll indicate the actual exception code and what name we use to reference it. We’ll also include a downloadable header file so that you can use these definitions without needing to type them in yourself.

Visual Studio Specific

These exceptions are specific to Microsoft Visual Studio and Microsoft compilers and linkers.

Microsoft Thread Naming Exception

0x406D1388 SVL_THREAD_NAMING_EXCEPTION

This exception is used to communicate to a debugger (or any other monitoring tool) a suggested name to represent the thread. This is technique is discussed in this blog post. It has been replaced in Windows 10 by SetThreadDescription().

Visual Studio C++ Exception

0xE06D7363 SVL_MSVC_EXCEPTION

This exception is used to implement C++’s exception handling.

This is discussed in this blog post.

.Net specific

These exceptions are specific to the Common Language Runtime (CLR), used to implement .Net. Most of these exception codes we found while inspecting .Net Core open source code. A few of them we have found elsewhere.

CLR Fatal JIT Exception

0x02345678 SVL_CLR_NET_FATAL_JIT_EXCEPTION

This definition comes from .Net Core open source code.

CLR Data Checksum Exception

0x31415927 SVL_CLR_NET_CLRDBG_DATA_CHECKSUM_EXCEPTION

This definition comes from .Net Core open source code.

CLR First Chance Exception

0x04242420 SVL_CLRDBG_NOTIFICATION_EXCEPTION_CODE

CLR Bootup COM+ Exception

0xC0020001 SVL_CLR_NET_BOOTUP_COMPLUS_EXCEPTION

This definition comes from .Net Core open source code.

CLR Exception

0xE0434F4D SVL_CLR_NET_EXCEPTION

CLR COM+ Exception

0xE0434352 SVL_CLR_NET_COMPLUS_EXCEPTION

This definition comes from .Net Core open source code.

Many causes of this exception are discussed here.

CLR HIJACK Exception

0xE0434F4E SVL_CLR_NET_HIJACK_EXCEPTION

This definition comes from .Net Core open source code.

CLR Notify Exception

0xE0444143 SVL_CLR_NET_CLRDATA_NOTIFY_EXCEPTION

This definition comes from .Net Core open source code.

CLR EXX Exception

0xE0455858 SVL_CLR_NET_EXX_EXCEPTION

This definition comes from .Net Core open source code.

CLR SEH Verification Exception

0xE0564552 SVL_CLR_NET_SEH_VERIFICATION_EXCEPTION

This definition comes from .Net Core open source code.

CLR Internal ASSERT Exception

0xE0584D4E SVL_CLR_NET_INTERNAL_ASSERT_EXCEPTION

This definition comes from .Net Core open source code.

Embarcadero specific

The exceptions are thrown by software made with tools from Embarcadero (formerly Borland).

Delphi Exception 1

0x0EEDFADE SVL_DELPHI_EXCEPTION_1

This is an exception thrown by Delphi.

Delphi Exception 2

0x0EEDFACE SVL_DELPHI_EXCEPTION_2

This is an exception thrown by Delphi.

C++ Builder Exception

0x0EEFFACE SVL_CPP_BUILDER_EXCEPTION

This is an exception thrown by C++ Builder.

Windows SDK

These exceptions are Windows exceptions that may or may not be defined in the IDE/SDK that you are using.

For more information you can look up error codes and exceptions here and here.

Fatal Memory Exhaustion

0xC00001AD STATUS_FATAL_MEMORY_EXHAUSTION

The process cannot allocate any more memory.

Fail Fast Exception

0xC0000602 STATUS_FAIL_FAST_EXCEPTION

This is used by the Kernel to handle fail fast exceptions. See the list of fail fast codes.

Stack Buffer Overrun

0xC0000409 STATUS_STACK_BUFFER_OVERRUN

This used to be used to indicate that the user mode callstack had had a buffer overrun, corrupting the stack and potentially opening up the application to the a malicious attack.

This exception code has been repurposed. It is now used to handle user mode fail fast exceptions. The first parameter passed with the exception is the fast fail code.

Invalid C Runtime Parameter

0xC0000417 STATUS_INVALID_CRUNTIME_PARAMETER

This is used to indicate that invalid parameters have been passed to a C runtime function. An example of this would be passing too small a string buffer to a _stprintf_s();

Heap Corruption

0xC0000374 STATUS_HEAP_CORRUPTION

A Win32 heap (HeapCreate, HeapDestroy, HeapAlloc, HeapReAlloc, HeapFree) has had it’s internal structures corrupted.

Invalid Exception Handler

0xC00001A5 STATUS_INVALID_EXCEPTION_HANDLER

An exception handler was expected but not found.

You can often see this in 32 bit programs where the stack (containing the stack based exception handler) has been corrupted.

Unwind

0xC0000027 STATUS_UNWIND

Bad Stack

0xC0000028 STATUS_BAD_STACK

During an unwind operation an invalid stack location, or a misaligned stack location was found.

Invalid Unwind Target

0xC0000029 STATUS_INVALID_UNWIND_TARGET

During an unwind operation and invalid unwind target was found.

Fatal User Callback Exception

0xC000041D STATUS_FATAL_USER_CALLBACK_EXCEPTION

An unhandled exception was found while handling a user callback.

Invalid Image Format

0xC000007B STATUS_INVALID_IMAGE_FORMAT

The specified file is not a PE format file, or it is a PE format file for a different processor architecture than the version that Windows is running.

To inspect PE files you can use PE File Browser.

Debugger Inactive

0xC0000354 STATUS_DEBUGGER_INACTIVE

Windows may have been started without Kernel debugging enabled.

Certificate Expired

0x800B0101 CERT_E_EXPIRED

The certificate is not within its validity period when verifying against the current system clock or the timestamp in the signed file.

x86 Breakpoint

0x4000001F STATUS_WX86_BREAKPOINT

Exception code used by the Win32 x86 emulation subsystem.

Debug Reply Later

0x40010001 DBG_REPLY_LATER

Debugger will reply later.

Debug Unable to provide handle

0x40010002 DBG_UNABLE_TO_PROVIDE_HANDLE

Debugger cannot provide the handle requested.

Debug Terminate Thread

0x40010003 DBG_TERMINATE_THREAD

Thread terminated by debugger.

Debug Terminate Process

0x40010004 DBG_TERMINATE_PROCESS

Process terminated by debugger.

Debug Control-C

0x40010005 DBG_CONTROL_C

Debugger received Ctrl-C.

Debug Print Exception Char

0x40010006 DBG_PRINTEXCEPTION_C

Debugger received message from OutputDebugStringA();

Debug RIP Exception

0x40010007 DBG_RIPEXCEPTION

Debugger received RIP exception.

Debug Control Break

0x40010008 DBG_CONTROL_BREAK

Debugger received Ctrl-Break.

Debug Command Exception

0x40010009 DBG_COMMAND_EXCEPTION

Debugger communicating a command.

Debug Print Exception Wide Char

0x4001000A DBG_PRINTEXCEPTION_WIDE_C

Debugger received message from OutputDebugStringW();

Debug Exception Not Handled

0x80010001 DBG_EXCEPTION_NOT_HANDLED

Debugger did not handle the exception.

Assertion Failure

0xC0000420 STATUS_ASSERTION_FAILURE

An assertion failed. See NT_ASSERT() and NT_ASSERTMSG().

Fatal Application Exit

0x40000015 STATUS_FATAL_APP_EXIT

Application exited via a call to abort(); or as a result of a fast fail exception.

Application Hang

0xCFFFFFFF STATUS_APPLICATION_HANG

Application Verifier Stop

0xC0000421 STATUS_VERIFIER_STOP

Application verifier has found an error in the current process

Procedure Not Found

0xC06D007F STATUS_PROCEDURE_NOT_FOUND

This is a Software Verify name for this exception, we can’t find an official name.

Header File

I hope you found the above list useful.

You can download the exceptionCodes.h header file.

Why does GetProcAddress() sometimes return an address outside of the DLL?

By , April 14, 2021 5:43 pm

For most uses of GetProcAddress() the address returned will be an address inside the DLL that you’re using to lookup the function.

But sometimes the address returned is not in the DLL. That’s odd! Is this a bug?

It’s not a bug. This intended behaviour. This article explains why it happens.

Before we get into what’s going on, let’s recap what GetProcAddress() does.

What is GetProcAddress()?

GetProcAddress() is a function exported from kernel32.dll.

It’s used for looking up the address of a function exported from a DLL.

The function can be exported by name (ASCII strings only) or by ordinal (an integer between 0 and 65535). Examples of DLLs that export by ordinal are the MFC dlls. Most DLLs export by name.

GetProcAddress() examines the export address table in the DLL looking for a function with the name (or ordinal) specified. If the function is found the address is returned, otherwise NULL is returned.

Here’s a demonstration looking up the address of the StarCrossedLovers(); function in the theatre.dll. This DLL handles all functionality related to the theatre plays needed by the application.

If the function is found it is called with the names of the two lovers, Romeo and Juliet.


HMODULE hModTheatre;

hModTheatre = GetModuleHandle(_T("theatre.dll")); // could be a call to LoadLibrary instead
if (hModTheatre != NULL)
{
    LOVERS_FUNC p;

    p = (LOVERS_FUNC)GetProcAddress(hModTheatre, "StarCrossedLovers");
    if (p != NULL)
        (*p)("Romeo", "Juliet");
}

Most of the time the returned address will be inside the DLL being queried.

Some of the time the returned address is not from the DLL being queried. Where did the address come from?

Where did the address come from?

When the address returned from GetProcAddress() is from outside of the DLL being queried, the address returned is an address inside another DLL that is supplying functions to the original DLL using a process called Forwarding.

To demonstrate this I’m going to examine the NetApiBufferAllocate() function which is exported from netapi32.dll.

If we call GetProcAddress() to look up the address of NetApiBufferAllocate the address returned on my Windows 10 machine is 0x70342800.

But if we look at the load address for netapi32.dll it’s 0x6fcc0000 and it’s size is only 76KB. The address returned is outside of netapi32.dll. If we then look at the other DLLs in the application and find which DLL contains address 0x70342800 we find it’s netutils.dll. Here’s a screenshot from VM Validator showing that.


OK, so we know the exported function address doesn’t come from the DLL we queried (netapi32.dll), but actually comes from netutils.dll. How does that work?

It’s going to be easier if I show two images of the netapi32.dll and netutils.dll while we examine the NetApiBufferAllocate() function.

These images are being viewed using PE File Browser. Both of these images show the DLLs loaded at their preferred load addresses (if you repeat this test with PE File Browser you’ll most likely get the same addresses).

netapi32.dll


netutils.dll


What you can see in the first image is that netapi32.dll has a NULL exported address for NetApiBufferAllocate and the the forwarding column shows NETUTILS.NetApiBufferAllocate. The forwarded function is composed of a DLL name, a period and the name of the exporting function (which doesn’t have to be the same as the original function name).

When you look at the exports for netutils.dll you can see that it exports a function NetApiBufferAllocate with non-NULL address.

What happens when you use GetProcAddress() to fetch the address of an exported function that has been forwarded from another DLL is that GetProcAddress() decodes the forwarded function name and looks up the forwarded function name in the forwarded function DLL. In the case of NetApiBufferAllocate in netapi32.dll that means GetProcAddress() returns the address of NetApiBufferAllocate in netutils.dll.

What’s the purpose of function forwarding?

I think it allows DLLs that were once large repositories for a variety of code to be split into many DLLs, each of which serve one purpose. These DLLs then forward the DLLs to the original DLL which is now acting as a central location (for backward compatibility) to find the functions. Client applications never know the code they are calling now resides in a DLL dedicated to the particular task rather than in the original monolithic DLL. This can be useful for the maintainers of the DLLs, improving maintenance, testing and security of the component DLLs.

If we return to the original example of the StarCrossedLovers() function exported from theatre.dll this could be reimplemented by moving all all Shakespeare related functionality out of theatre.dll into shakespeare.dll. StarCrossedLovers() in shakespeare.dll would then be forwarded to theatre.dll.

You could take it a stage further (sorry, no pun intended!) with each of Shakespeare’s plays being represented by their own DLL which forwards functions to shakespeare.dll (or theatre.dll).

Panorama Theme by Themocracy