#!/usr/bin/env python3 # # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. # Built-in import xml.etree.ElementTree as ElementTree import argparse s_AppVerifier_LogText = "{Application Verifier}logSession" s_AppVerifier_EntryText = "{Application Verifier}logEntry" s_AppVerifier_ErrorSeverities = ["Warning", "Error", "UNKNOWN"] # A dictionary to take the error codes and convert them to basic information # on what went wrong. # # How to adjust/learn more: # To add/remove from this list, run "appverif" in a Windows terminal with # administrator privileges and then press F1 to get the help page. Then search # for the error code you got (minus the "0x" part at the beginning) and use the # information there to add/adjust the entry in the dictionary below. s_AppVerifier_ErrorCodeHelp = { "Exceptions": { "0x650": "The application is trying to run code from an address that is non-executable or free" }, "Handles": { "0x300": "The function on the top of the stack passed an invalid handle to system routines", "0x301": "The function on the top of the stack passed an invalid TLS index to TLS system routines", "0x302": "The function on the top of the stack called WaitForMultipleObjects with NULL as the address " "of the array of handles to wait for or with zero as the number of handles", "0x303": "The function on the top of the stack passed a NULL handle to system routines", "0x304": "The current thread is currently running code inside the DllMain function of one " "of the DLLs loaded in the current process and it calls WaitForSingleObject or " "WaitForMultipleObjects to wait on a thread handle in the same process", "0x305": "The current thread is calling an API with a handle to an object with an incorrect object type" }, "Heaps": { "0x01": "Unknown error encountered that cannot be determined/classified by AppVerifier", "0x02": "The application touched non-accessible page. Typically is caused by a buffer overrun error", "0x03": "A heap created with HEAP_NO_SERIALIZE flag was accessed simultaneously from two threads", "0x04": "The size of the block in a 'HeapAlloc' or 'HeapReAlloc' operation was above any reasonable value", "0x05": "Heap structure did not include magic value from AppVerifier - meaning somehow the internal heap " "structure was corrupted or a bogus value was used as heap handle", "0x06": "Typically means block was allocated in one heap and freed in another", "0x07": "Block was freed twice", "0x08": "Generic error due to corruption in the heap block that AppVerifier cannot place more specifically", "0x09": "Tried to destroy the default process heap", "0x0A": "Access violation raised while executing heap manager code", "0x0B": "AppVerifier could not determine any particular type of corruption for the block. " "Generally means heap points to non-accessible memory area", "0x0C": "AppVerifier could not determine any particular type of corruption for the block. " "Generally happens if during heap free operation you pass an address that poins to a non-accessible memory area. " "Can also occur with double free situations", "0x0D": "Block of memory is written to after being freed", "0x0E": "Freed block marked as non-accessible had access attempt", "0x0F": "Magic pattern added by AppVerifier at end of heap block changed. " "Typically means buffer overrun errors", "0x10": "Buffer underruns", "0x11": "Buffer underruns", "0x12": "Buffer underruns", "0x13": "Non-accessible page at end of heap allocation was touched. Typically caused by a buffer overrun error", "0x14": "Page heap manager detected internal inconsistencies while calling GetProcessHeaps" }, "Leak": { "0x900": "Owner DLL of the allocation was dynamically unloaded while owning resources", "0x901": "Owner DLL of the handle was dynamically unloaded while owning resources", "0x902": "Owner DLL of the registry key was dynamically unloaded while owning resources", "0x903": "Owner DLL of the virtual reservation was dynamically unloaded while owning resources", "0x904": "Owner DLL of the SysString was dynamically unloaded while owning resources", "0x905": "DLL registered for power notification was dynamically unloaded without registering", "0x906": "Owner DLL of the COM allocation was dynamically unloaded while owning resources" }, "Locks": { "0x200": "A thread is terminated, suspended, or in a state in which it cannot hold a critical section", "0x201": "A DLL has a global variable containing a critical section and the DLL is unloaded but the " "critical section has not been deleted", "0x202": "A heap allocation contains a critical section, the allocation is freed, and the critical section " "has not been deleted", "0x203": "Typicaly means a critical section has been initialized more than once. May mean the critical section " "or its debug information structure has been corrupted", "0x204": "Memory containing a critical section was freed but the critical section has not been deleted using 'DeleteCriticalSection'", "0x205": "The DebugInfo field of the critical section is pointing to freed memory", "0x206": "The owner thread ID is invalid in the current context", "0x207": "The recursion count field of the critical section structure is invalid in the current context", "0x208": "A critical section is owned by a thread if it is deleted or if the critical section is uninitialized", "0x209": "A critical section is released more times than the current thread acquired it", "0x210": "A critical section is used without being initialized or after it has been deleted", "0x211": "A critical section is reinitialized by the current thread", "0x212": "The current thread is calling VirtualFree on a memory block that contains an active critical section", "0x213": "The current thread is calling UnmapViewOfFile on a memory block that contains an active critical section", "0x214": "The current thread calling LeaveCriticalSection but does not own any critical section", "0x215": "The current thread tries to use a private lock that lives inside another DLL" }, "Memory": { "0x600": "AppVerifier detects a VirtualFree or a DLL unload with an invalid start adress or size of the memory allocation", "0x601": "AppVerifier detects a VirtualAlloc call with an invalid start adress or size of the memory allocation", "0x602": "AppVerifier detects a MapViewOfFile call with an invalid base address or size of the mapping", "0x603": "AppVerifier detects an IsBadXXXPtr call with an invalid address for the memory buffer to be probed", "0x604": "AppVerifier detects an IsBadXXXPtr call for a memory allocation that is free", "0x605": "AppVerifier detects an IsBadXXXPtr call for a memory allocation that contains at least one GUARD_PAGE", "0x606": "AppVerifier detects an IsBadXXXPtr call with a NULL address", "0x607": "AppVerifier detects an IsBadXXXPtr call with an invalid start address or invalid size for the memory buffer to be probed", "0x608": "AppVerifier detects a DLL unload with an invalid start address for the size of the DLL memory range", "0x609": "AppVerifier detects a VirtualFree for a block of memory that is actually part of the current thread's stack", "0x60A": "AppVerifier detects a VirtualFree with an incorrect value for the FreeType parameter", "0x60B": "AppVerifier detects a VirtualFree for an address that is already free", "0x60C": "AppVerifier detects a VirtualFree with a non-zero value for the dwSize parameter", "0x60D": "A DLL's entry point function is raising an exception", "0x60E": "A thread function is raising an exception", "0x60F": "An exception occured during an IsBadXXXPtr call", "0x610": "AppVerifier detects a VirtualFree call with a NULL first parameter", "0x612": "AppVerifier detects a HeapFree for a block of memory that is actually part of the current thread's stack", "0x613": "AppVerifier detects an UnmapViewOfFile for a block of memory that is actually part of the current thread's stack", "0x614": "The application is trying to use NULL or some other incorrect address as the address of a valid object", "0x615": "The application is trying to use NULL or some other incorrect address as the address of a valid object", "0x616": "The application is trying to run code from an address that is non-executable or free", "0x617": "An exception occurred while initializing a buffer specified as output parameter for a Win32 or (non-AWS) CRT API", "0x618": "An exception occurred while calling HeapSize for a heap block that is being freed", "0x619": "The program is calling VirtualFree with an IpAddress parameter that is not the base address returned by " "the VirtualAlloc or VirtualAllocEx function when the region of pages was reserved", "0x61A": "The program is calling UnmapViewOfFile with an IpBaseAddress parameter that is not identical to the value returned" "by a previous call to the MapViewOfFile or MapViewOfFileEx function", "0x61B": "A callback function in the threadpool thread is rasing an exception", "0x61C": "The application is trying to run code from an address that is non-executable or free", "0x61D": "The application is created an executable heap", "0x61E": "The application is allocating executable memory" }, "SRWLock": { "0x250": "A thread tried to use SRW lock that is not initalized", "0x251": "The SRW lock is being re-initialized", "0x252": "The SRW lock is being released with a wrong release API", "0x253": "The SRW lock is being acquired recursively by the same thread", "0x254": "The thread that owns the SRW lock is exiting or being terminated", "0x255": "The SRW lock is being released by the thread that did not acquire the lock", "0x256": "The memory address being freed contains an active SRW lock that is still in use", "0x257": "The DLL being unloaded contains an active SRW lock that is still in use" }, "Threadpool": { "0x700": "Thread priority is changed when thread is returned to threadpool", "0x701": "Thread affinity is changed when thread is returned to threadpool", "0x702": "One or more messages left as unprocessed when threadpool thread is returned to the threadpool", "0x703": "Any window is kept alive when threadpool thread is returned to the threadpool", "0x704": "ExitThread is called on a threadpool thread", "0x705": "Callback function changed the thread token to impersonate another user and forgot to reset it before " "returning it to the threadpool", "0x706": "Windows API that requires dedicated or persistent thread called from threadpool", "0x707": "Callback function forgot to close or reset the current transaction handle", "0x708": "Callback function called CoInit and CoUnInit in differing amounts (unbalanced)", "0x709": "The period to signal the timer is not zero when the timer is set to signal only once with the WT_EXECUTEONLYONCE flag", "0x70A": "The loader lock is held within the callback and is not released when the thread is returned to the threadpool", "0x70B": "The preferred language is set within the callback and is not cleared when the thread is returned to the threadpool", "0x70C": "The background priority is set within the callback and is not disabled when the thread is returned to the threadpool", "0x70D": "TerminateThread called on a threadpool thread", }, "TLS": { "0x350": "A DLL that allocated a TLS index is being unloaded before freeing that TLS index", "0x351": "The internal verifier structures used to store the state of TLS slots for thread are corrupted", "0x352": "An invalid TLS index is used" } } def parseXML(filepath, dump_xml_on_error): xml_is_app_verifier = False app_verifier_entries = [] print("Looking for AppVerifier XML file...") xml_tree = ElementTree.parse(filepath) # Go through every element in the XML tree for elem in xml_tree.iter(): if (elem.tag == s_AppVerifier_LogText): xml_is_app_verifier = True elif (elem.tag == s_AppVerifier_EntryText): app_verifier_entries.append(elem) # If the XML does not have any AppVerifier data, then something went wrong! if (xml_is_app_verifier == False): print("ERROR: XML File from AppVerifier does not include a AppVerifier session!") return -1 # If we have AppVerifier entries, then a test or tests failed, so process the data, # print it, and then return with an error to stop the GitHub action from passing if (len(app_verifier_entries) > 0): print("WARNING: AppVerifier entries found:") severity_error_found = False for entry in app_verifier_entries: element_time = entry.attrib.get("Time", "UNKNOWN") element_layer_name = entry.attrib.get("LayerName", "UNKNOWN") element_code = entry.attrib.get("StopCode", "UNKNOWN") element_severity = entry.attrib.get("Severity", "UNKNOWN") print_red = False if (element_severity in s_AppVerifier_ErrorSeverities): severity_error_found = True print_red = True if (print_red): print( f"ERROR: [{element_time}] {element_severity.upper()} - Test: {element_layer_name} - Stop Code: {element_code}") else: print( f"[{element_time}] {element_severity.upper()} - Test: {element_layer_name} - Stop Code: {element_code}") print(f"\t{getErrorCodeMeaning(element_layer_name, element_code)}") print( "\nNOTE: The error codes and information provided are just guesses based on the error code.\n" "\tRun AppVerifier locally and use WinDBG combined with the AppVerifier help to discover more " "about the error from its error code and how to debug it.") if (severity_error_found == True and dump_xml_on_error != None): if (dump_xml_on_error == True): print("\nERROR: Raw XML output for errors found:\n") for entry in app_verifier_entries: print(ElementTree.tostring( entry, encoding="unicode")) if (severity_error_found == True): print( "\nERROR: Failed due to AppVerifier finding entries marked as severe") return -1 else: print("SUCCESS: AppVerifier entries were not marked as severe") return 0 else: print("SUCCESS: No AppVerifier entries found! AppVerifier ran successfully and did not generate any entries") return 0 def getErrorCodeMeaning(element_layer_name, element_code): if (element_layer_name in s_AppVerifier_ErrorCodeHelp): layer_codes = s_AppVerifier_ErrorCodeHelp[element_layer_name] if (element_code in layer_codes): return layer_codes[element_code] else: return "Util-script unknown error: " + element_code + " for layer " + element_layer_name return "Util-script unknown layer: " + element_layer_name + " and error code: " + element_code def booleanString(string): string = string.lower() if string not in {"false", "true"}: raise ValueError("Boolean is not true or false!") return string == "true" def main(): argument_parser = argparse.ArgumentParser( description="AppVerifier XML output util") argument_parser.add_argument("--xml_file", metavar="", required=False, help="Path to XML file from AppVerifier") argument_parser.add_argument("--dump_xml_on_error", metavar="", default=True, required=False, type=booleanString, help="If true, the XML for found issues will be printed to the console") parsed_commands = argument_parser.parse_args() print("\nStarting AppVerifier XML check...", flush=True) print(parsed_commands.dump_xml_on_error) xml_result = parseXML(parsed_commands.xml_file, parsed_commands.dump_xml_on_error) print("\n") exit(xml_result) if __name__ == "__main__": main()