| OLD | NEW |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import re | 5 import re |
| 6 | 6 |
| 7 from crash.stacktrace import CallStack |
| 8 from crash.type_enums import LanguageType |
| 9 |
| 7 _INLINE_FUNCTION_FILE_PATH_MARKERS = [ | 10 _INLINE_FUNCTION_FILE_PATH_MARKERS = [ |
| 8 'third_party/llvm-build/Release+Asserts/include/c++/v1/', | 11 'third_party/llvm-build/Release+Asserts/include/c++/v1/', |
| 9 'linux/debian_wheezy_amd64-sysroot/usr/include/c++/4.6/bits/', | 12 'linux/debian_wheezy_amd64-sysroot/usr/include/c++/4.6/bits/', |
| 10 'eglibc-3GlaMS/eglibc-2.19/sysdeps/unix/', | 13 'eglibc-3GlaMS/eglibc-2.19/sysdeps/unix/', |
| 11 ] | 14 ] |
| 12 | 15 |
| 16 # When checking the null pointer dereference, any dereference of an address |
| 17 # within the threshold is considered as null pointer dereference. Be consistent |
| 18 # with the threshold used in Clusterfuzz. |
| 19 NULL_POINTER_DEREFERENCE_THRESHOLD = 4096 |
| 20 V8_JIT_CODE_MARKER = 'v8::internal::Invoke' |
| 21 V8_API_H_FILE_PATH = 'src/api.h' |
| 22 V8_API_CC_FILE_PATH = 'src/api.cc' |
| 23 V8_DEP_PATH_MARKER = 'src/v8' |
| 24 BLINK_BINDINGS_GENERATED_PATH_REGEX = re.compile( |
| 25 r'out/[^/]+/gen/blink/bindings/.*') |
| 26 JAVA_JRE_SDK_REGEX = re.compile( |
| 27 r'(java\..*|javax\..*|org\.xml\..*|org\.w3c\..*|' |
| 28 r'org\.omg\..*|org\.ietf\.jgss\..*)') |
| 29 |
| 13 | 30 |
| 14 class CallStackFilter(object): | 31 class CallStackFilter(object): |
| 15 """Filters frames of a callstack buffer.""" | 32 """Filters frames of a callstack buffer.""" |
| 16 | 33 |
| 17 def __call__(self, stack_buffer): | 34 def __call__(self, stack_buffer): |
| 18 """Returns the stack_buffer with frames filtered. | 35 """Returns the stack_buffer with frames filtered. |
| 19 | 36 |
| 20 Args: | 37 Args: |
| 21 stack_buffer (CallStackBuffer): stack buffer to be filtered. | 38 stack_buffer (CallStackBuffer): stack buffer to be filtered. |
| 22 | 39 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 59 def __call__(self, stack_buffer): | 76 def __call__(self, stack_buffer): |
| 60 """Returns stack_buffer with only top n frames. | 77 """Returns stack_buffer with only top n frames. |
| 61 | 78 |
| 62 If no self.top_n_frames is None, don't do any filtering. | 79 If no self.top_n_frames is None, don't do any filtering. |
| 63 """ | 80 """ |
| 64 if self.top_n_frames is None: | 81 if self.top_n_frames is None: |
| 65 return stack_buffer | 82 return stack_buffer |
| 66 | 83 |
| 67 stack_buffer.frames = stack_buffer.frames[:self.top_n_frames] | 84 stack_buffer.frames = stack_buffer.frames[:self.top_n_frames] |
| 68 return stack_buffer | 85 return stack_buffer |
| 86 |
| 87 |
| 88 class FilterJavaJreSdkFrames(CallStackFilter): |
| 89 """Filters out package names from Java JRE/SDK. |
| 90 |
| 91 For example: java.*, javax.*, org.xml.*, org.w3c.*, org.omg.*, |
| 92 org.ietf.jgss.*. These frames are misleading to Predator. |
| 93 """ |
| 94 def __call__(self, stack_buffer): |
| 95 if stack_buffer.language_type != LanguageType.JAVA: |
| 96 return stack_buffer |
| 97 |
| 98 stack_buffer.frames = filter( |
| 99 lambda frame: not JAVA_JRE_SDK_REGEX.match(frame.function), |
| 100 stack_buffer.frames) |
| 101 return stack_buffer |
| 102 |
| 103 |
| 104 class KeepV8FramesIfV8GeneratedJITCrash(CallStackFilter): |
| 105 """Keeps v8 frames if conditions met. |
| 106 |
| 107 If the top-most frames don't have symbols, but the top frame that does is |
| 108 ``v8::internal::Invoke``, the bug is likely a crash in |
| 109 V8's generated JIT code. |
| 110 """ |
| 111 def __call__(self, stack_buffer): |
| 112 if (stack_buffer and V8_JIT_CODE_MARKER in stack_buffer.frames[0].function |
| 113 and stack_buffer.metadata.get('top_frame_has_no_symbols')): |
| 114 stack_buffer.frames = filter(lambda f: V8_DEP_PATH_MARKER in f.dep_path, |
| 115 stack_buffer.frames) |
| 116 return stack_buffer |
| 117 |
| 118 |
| 119 class FilterV8FramesForV8APIBindingCode(CallStackFilter): |
| 120 """Filters all v8 frames if all conditions met. |
| 121 |
| 122 Conditions: |
| 123 (1) src/v8/src/api.h or src/v8/src/api.cc appears as the top file in the stack |
| 124 trace. |
| 125 (2) the second file is not in src/v8/src |
| 126 (e.g. src/out/Release/gen/blink/bindings) or the crash is caused by |
| 127 dereference of null pointer, then V8 should not be responsible for the crash |
| 128 (likely a bindings issue). |
| 129 """ |
| 130 def __init__(self, crash_address=None): |
| 131 """ |
| 132 Args: |
| 133 crash_address (str): Address where crash happens. |
| 134 """ |
| 135 # Record the crash address of the to-be-parsed stack_buffer. |
| 136 self.crash_address = crash_address |
| 137 |
| 138 def __call__(self, stack_buffer): |
| 139 if len(stack_buffer.frames) < 2: |
| 140 return stack_buffer |
| 141 |
| 142 first_frame_is_api_file = ( |
| 143 V8_DEP_PATH_MARKER in stack_buffer.frames[0].dep_path and |
| 144 (V8_API_H_FILE_PATH == stack_buffer.frames[0].file_path or |
| 145 V8_API_CC_FILE_PATH == stack_buffer.frames[0].file_path)) |
| 146 |
| 147 second_frame_not_from_v8_src = ( |
| 148 V8_DEP_PATH_MARKER not in stack_buffer.frames[1].dep_path or |
| 149 not stack_buffer.frames[1].file_path.startswith('src')) |
| 150 |
| 151 null_pointer_dereference = False |
| 152 if self.crash_address: |
| 153 try: |
| 154 null_pointer_dereference = int( |
| 155 self.crash_address, |
| 156 base=16) < NULL_POINTER_DEREFERENCE_THRESHOLD |
| 157 except ValueError: # pragma: no cover |
| 158 # some testcases like memcpy-param-overlap have crash addresses like |
| 159 # '[0x621000017d00,0x621000018cea) and [0x621000017d16, 0x621000018d00)' |
| 160 pass |
| 161 |
| 162 if (first_frame_is_api_file and (second_frame_not_from_v8_src or |
| 163 null_pointer_dereference)): |
| 164 stack_buffer.frames = filter( |
| 165 lambda f: V8_DEP_PATH_MARKER not in f.dep_path, |
| 166 stack_buffer.frames) |
| 167 # After deleting all v8 frames, if the top n frames are generated code, |
| 168 # need to filter them out. |
| 169 top_n_generated_code_frames = 0 |
| 170 for frame in stack_buffer.frames: |
| 171 if not BLINK_BINDINGS_GENERATED_PATH_REGEX.match(frame.file_path): |
| 172 break |
| 173 top_n_generated_code_frames += 1 |
| 174 stack_buffer.frames = stack_buffer.frames[top_n_generated_code_frames:] |
| 175 |
| 176 return stack_buffer |
| 177 |
| 178 |
| 179 class FilterFramesAfterBlinkGeneratedCode(CallStackFilter): |
| 180 """Filters all the frames after blink generated code.""" |
| 181 def __call__(self, stack_buffer): |
| 182 for index, frame in enumerate(stack_buffer): |
| 183 if BLINK_BINDINGS_GENERATED_PATH_REGEX.match(frame.file_path): |
| 184 stack_buffer.frames = stack_buffer.frames[:index] |
| 185 break |
| 186 |
| 187 return stack_buffer |
| 188 |
| 189 |
| 190 class FilterV8FramesIfV8NotInTopFrames(CallStackFilter): |
| 191 """Filters all v8 frames if there is no v8 frames in top_n_frames.""" |
| 192 def __init__(self, top_n_frames=4): |
| 193 self.top_n_frames = top_n_frames |
| 194 |
| 195 def __call__(self, stack_buffer): |
| 196 need_filter_v8 = False |
| 197 for index, frame in enumerate(stack_buffer): |
| 198 if index >= self.top_n_frames: |
| 199 need_filter_v8 = True |
| 200 break |
| 201 |
| 202 if V8_DEP_PATH_MARKER in frame.dep_path: |
| 203 break |
| 204 |
| 205 if not need_filter_v8: |
| 206 return stack_buffer |
| 207 |
| 208 stack_buffer.frames = filter( |
| 209 lambda f: V8_DEP_PATH_MARKER not in f.dep_path, stack_buffer.frames) |
| 210 |
| 211 return stack_buffer |
| OLD | NEW |