From 1d1b36d371741605bb49ca01f3a9d7d8bca857ad Mon Sep 17 00:00:00 2001 From: "ivan.penkov@gmail.com" Date: Fri, 2 Aug 2013 18:15:57 +0000 Subject: [PATCH] Updating MDRawMiscInfo to support verions 3 and 4 of the MINIDUMP_MISC_INFO_N structure. Added the necessary code for swapping and string conversion from UTF-16. Found and fixed a bug in MinidumpAssertion::Read where the max string length passed to UTF16codeunits was in bytes instead of UTF-16 chars. Tested with a minidump containing a version 3 structure to validate the string conversion routines. Interestingly enough the time_zone names does not appear to be abbreviation as the documentation was suggesting but full names, e.g. Eastern Standard Time: MDRawMiscInfo size_of_info = 232 flags1 = 0xf7 process_id = 0x54c4 process_create_time = 0x51a9323c process_user_time = 0x1 process_kernel_time = 0x0 processor_max_mhz = 3100 processor_current_mhz = 1891 processor_mhz_limit = 3100 processor_max_idle_state = 0x1 processor_current_idle_state = 0x1 The new fileds follow: process_integrity_level = 0x1000 process_execute_flags = 0x4d protected_process = 0 time_zone_id = 2 time_zone.bias = 300 time_zone.standard_name = Eastern Standard Time time_zone.daylight_name = Eastern Daylight Time Review URL: https://breakpad.appspot.com/617002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1204 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/google_breakpad/common/minidump_format.h | 113 ++++++++++- src/google_breakpad/processor/minidump.h | 7 + src/processor/minidump.cc | 188 +++++++++++++------ 3 files changed, 246 insertions(+), 62 deletions(-) diff --git a/src/google_breakpad/common/minidump_format.h b/src/google_breakpad/common/minidump_format.h index 8c47e642..90e5ba5b 100644 --- a/src/google_breakpad/common/minidump_format.h +++ b/src/google_breakpad/common/minidump_format.h @@ -347,7 +347,7 @@ typedef enum { typedef struct { uint32_t length; /* Length of buffer in bytes (not characters), - * excluding 0-terminator */ + * excluding 0-terminator */ uint16_t buffer[1]; /* UTF-16-encoded, 0-terminated */ } MDString; /* MINIDUMP_STRING */ @@ -658,6 +658,55 @@ typedef enum { MD_OS_NACL = 0x8205 /* Native Client (NaCl) */ } MDOSPlatform; +typedef struct { + uint16_t year; + uint16_t month; + uint16_t day_of_week; + uint16_t day; + uint16_t hour; + uint16_t minute; + uint16_t second; + uint16_t milliseconds; +} MDSystemTime; /* SYSTEMTIME */ + +typedef struct { + /* Required field. The bias is the difference, in minutes, between + * Coordinated Universal Time (UTC) and local time. + * Formula: UTC = local time + bias */ + int32_t bias; + /* A description for standard time. For example, "EST" could indicate Eastern + * Standard Time. In practice this contains the full time zone names. This + * string can be empty. */ + uint16_t standard_name[32]; /* UTF-16-encoded, 0-terminated */ + /* A MDSystemTime structure that contains a date and local time when the + * transition from daylight saving time to standard time occurs on this + * operating system. If the time zone does not support daylight saving time, + * the month member in the MDSystemTime structure is zero. */ + MDSystemTime standard_date; + /* The bias value to be used during local time translations that occur during + * standard time. */ + int32_t standard_bias; + /* A description for daylight saving time. For example, "PDT" could indicate + * Pacific Daylight Time. In practice this contains the full time zone names. + * This string can be empty. */ + uint16_t daylight_name[32]; /* UTF-16-encoded, 0-terminated */ + /* A MDSystemTime structure that contains a date and local time when the + * transition from standard time to daylight saving time occurs on this + * operating system. If the time zone does not support daylight saving time, + * the month member in the MDSystemTime structure is zero.*/ + MDSystemTime daylight_date; + /* The bias value to be used during local time translations that occur during + * daylight saving time. */ + int32_t daylight_bias; +} MDTimeZoneInformation; /* TIME_ZONE_INFORMATION */ + +/* MAX_PATH from windef.h */ +#define MD_MAX_PATH 260 + +/* The miscellaneous information stream contains a variety + * of small pieces of information. A member is valid if + * it's within the available size and its corresponding + * bit is set. */ typedef struct { uint32_t size_of_info; /* Length of entire MDRawMiscInfo structure. */ uint32_t flags1; @@ -673,8 +722,8 @@ typedef struct { uint32_t process_kernel_time; /* seconds of kernel CPU time */ /* The following fields are not present in MINIDUMP_MISC_INFO but are - * in MINIDUMP_MISC_INFO_2. When this struct is populated, these values - * may not be set. Use flags1 or sizeOfInfo to determine whether these + * in MINIDUMP_MISC_INFO_2. When this struct is populated, these value + * may not be set. Use flags1 or size_of_info to determine whether these * values are present. These are only valid when flags1 contains * MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO. */ uint32_t processor_max_mhz; @@ -682,20 +731,66 @@ typedef struct { uint32_t processor_mhz_limit; uint32_t processor_max_idle_state; uint32_t processor_current_idle_state; -} MDRawMiscInfo; /* MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO2 */ -#define MD_MISCINFO_SIZE 24 -#define MD_MISCINFO2_SIZE 44 + /* The following fields are not present in MINIDUMP_MISC_INFO_2 but are + * in MINIDUMP_MISC_INFO_3. When this struct is populated, these value + * may not be set. Use flags1 or size_of_info to determine whether these + * values are present. */ + + /* The following field is only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROCESS_INTEGRITY. */ + uint32_t process_integrity_level; + + /* The following field is only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROCESS_EXECUTE_FLAGS. */ + uint32_t process_execute_flags; + + /* The following field is only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROTECTED_PROCESS. */ + uint32_t protected_process; + + /* The following 2 fields are only valid if flags1 contains + * MD_MISCINFO_FLAGS1_TIMEZONE. */ + uint32_t time_zone_id; + MDTimeZoneInformation time_zone; + + /* The following fields are not present in MINIDUMP_MISC_INFO_3 but are + * in MINIDUMP_MISC_INFO_4. When this struct is populated, these value + * may not be set. Use size_of_info to determine whether these values are + * present. */ + + /* The following 2 fields are only valid if + * size_of_info is >= MD_MISCINFO4_SIZE */ + uint16_t build_string[MD_MAX_PATH]; /* UTF-16-encoded, 0-terminated */ + uint16_t dbg_bld_str[40]; /* UTF-16-encoded, 0-terminated */ +} MDRawMiscInfo; /* MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO2, + * MINIDUMP_MISC_INFO3, MINIDUMP_MISC_INFO4 */ + +static const size_t MD_MISCINFO_SIZE = + offsetof(MDRawMiscInfo, processor_max_mhz); +static const size_t MD_MISCINFO2_SIZE = + offsetof(MDRawMiscInfo, process_integrity_level); +static const size_t MD_MISCINFO3_SIZE = + offsetof(MDRawMiscInfo, build_string[0]); +static const size_t MD_MISCINFO4_SIZE = sizeof(MDRawMiscInfo); /* For (MDRawMiscInfo).flags1. These values indicate which fields in the * MDRawMiscInfoStructure are valid. */ typedef enum { - MD_MISCINFO_FLAGS1_PROCESS_ID = 0x00000001, + MD_MISCINFO_FLAGS1_PROCESS_ID = 0x00000001, /* MINIDUMP_MISC1_PROCESS_ID */ - MD_MISCINFO_FLAGS1_PROCESS_TIMES = 0x00000002, + MD_MISCINFO_FLAGS1_PROCESS_TIMES = 0x00000002, /* MINIDUMP_MISC1_PROCESS_TIMES */ - MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO = 0x00000004 + MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO = 0x00000004, /* MINIDUMP_MISC1_PROCESSOR_POWER_INFO */ + MD_MISCINFO_FLAGS1_PROCESS_INTEGRITY = 0x00000010, + /* MINIDUMP_MISC3_PROCESS_INTEGRITY */ + MD_MISCINFO_FLAGS1_PROCESS_EXECUTE_FLAGS = 0x00000020, + /* MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS */ + MD_MISCINFO_FLAGS1_TIMEZONE = 0x00000040, + /* MINIDUMP_MISC3_TIMEZONE */ + MD_MISCINFO_FLAGS1_PROTECTED_PROCESS = 0x00000080, + /* MINIDUMP_MISC3_PROTECTED_PROCESS */ } MDMiscInfoFlags1; /* diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h index 16b9d856..65b13a4e 100644 --- a/src/google_breakpad/processor/minidump.h +++ b/src/google_breakpad/processor/minidump.h @@ -782,6 +782,13 @@ class MinidumpMiscInfo : public MinidumpStream { bool Read(uint32_t expected_size_); MDRawMiscInfo misc_info_; + + // Populated by Read. Contains the converted strings from the corresponding + // UTF-16 fields in misc_info_ + string standard_name_; + string daylight_name_; + string build_string_; + string dbg_bld_str_; }; diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index 7ad736d5..b0830f4a 100755 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -140,6 +140,19 @@ static void Swap(uint128_struct* value) { Swap(&value->high); } +// Swapping signed integers +static inline void Swap(int16_t* value) { + Swap(reinterpret_cast(value)); +} + +static inline void Swap(int32_t* value) { + Swap(reinterpret_cast(value)); +} + +static inline void Swap(int64_t* value) { + Swap(reinterpret_cast(value)); +} + static inline void Swap(MDLocationDescriptor* location_descriptor) { Swap(&location_descriptor->data_size); @@ -160,6 +173,23 @@ static inline void Swap(MDGUID* guid) { // Don't swap guid->data4[] because it contains 8-bit quantities. } +static inline void Swap(MDSystemTime* system_time) { + Swap(&system_time->year); + Swap(&system_time->month); + Swap(&system_time->day_of_week); + Swap(&system_time->day); + Swap(&system_time->hour); + Swap(&system_time->minute); + Swap(&system_time->second); + Swap(&system_time->milliseconds); +} + +static inline void Swap(uint16_t* data, size_t size_in_bytes) { + size_t data_length = size_in_bytes / sizeof(data[0]); + for (size_t i = 0; i < data_length; i++) { + Swap(&data[i]); + } +} // // Character conversion routines @@ -176,7 +206,7 @@ static inline void Swap(MDGUID* guid) { // CPU's endianness into consideration. It doesn't seems worth the trouble // of making it a dependency when we don't care about anything but UTF-16. static string* UTF16ToUTF8(const vector& in, - bool swap) { + bool swap) { scoped_ptr out(new string()); // Set the string's initial capacity to the number of UTF-16 characters, @@ -256,6 +286,39 @@ static size_t UTF16codeunits(const uint16_t *string, size_t maxlen) { return count; } +static inline void Swap(MDTimeZoneInformation* time_zone) { + Swap(&time_zone->bias); + // Skip time_zone->standard_name. No need to swap UTF-16 fields. + // The swap will be done as part of the conversion to UTF-8. + Swap(&time_zone->standard_date); + Swap(&time_zone->standard_bias); + // Skip time_zone->daylight_name. No need to swap UTF-16 fields. + // The swap will be done as part of the conversion to UTF-8. + Swap(&time_zone->daylight_date); + Swap(&time_zone->daylight_bias); +} + +static void ConvertUTF16BufferToUTF8String(const uint16_t* utf16_data, + size_t max_length_in_bytes, + string* utf8_result, + bool swap) { + // Since there is no explicit byte length for each string, use + // UTF16codeunits to calculate word length, then derive byte + // length from that. + size_t max_word_length = max_length_in_bytes / sizeof(utf16_data[0]); + size_t word_length = UTF16codeunits(utf16_data, max_word_length); + if (word_length > 0) { + size_t byte_length = word_length * sizeof(utf16_data[0]); + vector utf16_vector(word_length); + memcpy(&utf16_vector[0], &utf16_data[0], byte_length); + scoped_ptr temp(UTF16ToUTF8(utf16_vector, swap)); + if (temp.get()) { + utf8_result->assign(*temp); + } + } else { + utf8_result->clear(); + } +} // // MinidumpObject @@ -2355,12 +2418,7 @@ const MDImageDebugMisc* MinidumpModule::GetMiscRecord(uint32_t* size) { uint16_t* data16 = reinterpret_cast(&(misc_record->data)); unsigned int dataBytes = module_.misc_record.data_size - MDImageDebugMisc_minsize; - unsigned int dataLength = dataBytes / 2; - for (unsigned int characterIndex = 0; - characterIndex < dataLength; - ++characterIndex) { - Swap(&data16[characterIndex]); - } + Swap(data16, dataBytes); } } @@ -3143,48 +3201,14 @@ bool MinidumpAssertion::Read(uint32_t expected_size) { // Each of {expression, function, file} is a UTF-16 string, // we'll convert them to UTF-8 for ease of use. - // expression - // Since we don't have an explicit byte length for each string, - // we use UTF16codeunits to calculate word length, then derive byte - // length from that. - uint32_t word_length = UTF16codeunits(assertion_.expression, - sizeof(assertion_.expression)); - if (word_length > 0) { - uint32_t byte_length = word_length * 2; - vector expression_utf16(word_length); - memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length); - - scoped_ptr new_expression(UTF16ToUTF8(expression_utf16, - minidump_->swap())); - if (new_expression.get()) - expression_ = *new_expression; - } - - // assertion - word_length = UTF16codeunits(assertion_.function, - sizeof(assertion_.function)); - if (word_length) { - uint32_t byte_length = word_length * 2; - vector function_utf16(word_length); - memcpy(&function_utf16[0], &assertion_.function[0], byte_length); - scoped_ptr new_function(UTF16ToUTF8(function_utf16, - minidump_->swap())); - if (new_function.get()) - function_ = *new_function; - } - - // file - word_length = UTF16codeunits(assertion_.file, - sizeof(assertion_.file)); - if (word_length > 0) { - uint32_t byte_length = word_length * 2; - vector file_utf16(word_length); - memcpy(&file_utf16[0], &assertion_.file[0], byte_length); - scoped_ptr new_file(UTF16ToUTF8(file_utf16, - minidump_->swap())); - if (new_file.get()) - file_ = *new_file; - } + ConvertUTF16BufferToUTF8String(assertion_.expression, + sizeof(assertion_.expression), &expression_, + minidump_->swap()); + ConvertUTF16BufferToUTF8String(assertion_.function, + sizeof(assertion_.function), &function_, + minidump_->swap()); + ConvertUTF16BufferToUTF8String(assertion_.file, sizeof(assertion_.file), + &file_, minidump_->swap()); if (minidump_->swap()) { Swap(&assertion_.line); @@ -3502,10 +3526,13 @@ bool MinidumpMiscInfo::Read(uint32_t expected_size) { valid_ = false; if (expected_size != MD_MISCINFO_SIZE && - expected_size != MD_MISCINFO2_SIZE) { - BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size << - " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE << - ")"; + expected_size != MD_MISCINFO2_SIZE && + expected_size != MD_MISCINFO3_SIZE && + expected_size != MD_MISCINFO4_SIZE) { + BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size + << " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE + << ", " << MD_MISCINFO3_SIZE << ", " << MD_MISCINFO4_SIZE + << ")"; return false; } @@ -3515,6 +3542,7 @@ bool MinidumpMiscInfo::Read(uint32_t expected_size) { } if (minidump_->swap()) { + // Swap version 1 fields Swap(&misc_info_.size_of_info); Swap(&misc_info_.flags1); Swap(&misc_info_.process_id); @@ -3522,12 +3550,26 @@ bool MinidumpMiscInfo::Read(uint32_t expected_size) { Swap(&misc_info_.process_user_time); Swap(&misc_info_.process_kernel_time); if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { + // Swap version 2 fields Swap(&misc_info_.processor_max_mhz); Swap(&misc_info_.processor_current_mhz); Swap(&misc_info_.processor_mhz_limit); Swap(&misc_info_.processor_max_idle_state); Swap(&misc_info_.processor_current_idle_state); } + if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) { + // Swap version 3 fields + Swap(&misc_info_.process_integrity_level); + Swap(&misc_info_.process_execute_flags); + Swap(&misc_info_.protected_process); + Swap(&misc_info_.time_zone_id); + Swap(&misc_info_.time_zone); + } + if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) { + // Swap version 4 fields. + // Do not swap UTF-16 strings. The swap is done as part of the + // conversion to UTF-8 (code follows below). + } } if (expected_size != misc_info_.size_of_info) { @@ -3536,6 +3578,26 @@ bool MinidumpMiscInfo::Read(uint32_t expected_size) { return false; } + // Convert UTF-16 strings + if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) { + // Convert UTF-16 strings in version 3 fields + ConvertUTF16BufferToUTF8String(misc_info_.time_zone.standard_name, + sizeof(misc_info_.time_zone.standard_name), + &standard_name_, minidump_->swap()); + ConvertUTF16BufferToUTF8String(misc_info_.time_zone.daylight_name, + sizeof(misc_info_.time_zone.daylight_name), + &daylight_name_, minidump_->swap()); + } + if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) { + // Convert UTF-16 strings in version 4 fields + ConvertUTF16BufferToUTF8String(misc_info_.build_string, + sizeof(misc_info_.build_string), + &build_string_, minidump_->swap()); + ConvertUTF16BufferToUTF8String(misc_info_.dbg_bld_str, + sizeof(misc_info_.dbg_bld_str), + &dbg_bld_str_, minidump_->swap()); + } + valid_ = true; return true; } @@ -3548,6 +3610,7 @@ void MinidumpMiscInfo::Print() { } printf("MDRawMiscInfo\n"); + // Print version 1 fields printf(" size_of_info = %d\n", misc_info_.size_of_info); printf(" flags1 = 0x%x\n", misc_info_.flags1); printf(" process_id = 0x%x\n", misc_info_.process_id); @@ -3558,6 +3621,7 @@ void MinidumpMiscInfo::Print() { printf(" process_kernel_time = 0x%x\n", misc_info_.process_kernel_time); if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { + // Print version 2 fields printf(" processor_max_mhz = %d\n", misc_info_.processor_max_mhz); printf(" processor_current_mhz = %d\n", @@ -3569,6 +3633,24 @@ void MinidumpMiscInfo::Print() { printf(" processor_current_idle_state = 0x%x\n", misc_info_.processor_current_idle_state); } + if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) { + // Print version 3 fields + printf(" process_integrity_level = 0x%x\n", + misc_info_.process_integrity_level); + printf(" process_execute_flags = 0x%x\n", + misc_info_.process_execute_flags); + printf(" protected_process = %d\n", + misc_info_.protected_process); + printf(" time_zone_id = %d\n", misc_info_.time_zone_id); + printf(" time_zone.bias = %d\n", misc_info_.time_zone.bias); + printf(" time_zone.standard_name = %s\n", standard_name_.c_str()); + printf(" time_zone.daylight_name = %s\n", daylight_name_.c_str()); + } + if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) { + // Print version 4 fields + printf(" build_string = %s\n", build_string_.c_str()); + printf(" dbg_bld_str = %s\n", dbg_bld_str_.c_str()); + } printf("\n"); }