Coverage for /Syzygy/agent/asan/asan_rtl_impl.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
68.6%1311910.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "syzygy/agent/asan/asan_rtl_impl.h"
  16    :  
  17    :  #include "base/logging.h"
  18    :  #include "syzygy/agent/asan/asan_heap.h"
  19    :  #include "syzygy/agent/asan/asan_runtime.h"
  20    :  #include "syzygy/agent/asan/asan_shadow.h"
  21    :  
  22    :  namespace {
  23    :  
  24    :  using agent::asan::AsanRuntime;
  25    :  using agent::asan::HeapProxy;
  26    :  
  27    :  HANDLE process_heap = NULL;
  28    :  
  29    :  // The asan runtime manager.
  30    :  AsanRuntime* asan_runtime = NULL;
  31    :  
  32    :  }  // namespace
  33    :  
  34    :  namespace agent {
  35    :  namespace asan {
  36    :  
  37  E :  void SetUpRtl(AsanRuntime* runtime) {
  38  E :    DCHECK(runtime != NULL);
  39  E :    asan_runtime = runtime;
  40  E :    process_heap = GetProcessHeap();
  41  E :  }
  42    :  
  43  E :  void TearDownRtl() {
  44  E :    process_heap = NULL;
  45  E :  }
  46    :  
  47    :  }  // namespace asan
  48    :  }  // namespace agent
  49    :  
  50    :  extern "C" {
  51    :  
  52    :  HANDLE WINAPI asan_HeapCreate(DWORD options,
  53    :                                SIZE_T initial_size,
  54  E :                                SIZE_T maximum_size) {
  55  E :    DCHECK(asan_runtime != NULL);
  56    :    scoped_ptr<HeapProxy> proxy(new HeapProxy(asan_runtime->stack_cache(),
  57  E :                                              asan_runtime->logger()));
  58  E :    if (!proxy->Create(options, initial_size, maximum_size))
  59  E :      return NULL;
  60    :  
  61  E :    asan_runtime->AddHeap(proxy.get());
  62    :  
  63  E :    return HeapProxy::ToHandle(proxy.release());
  64  E :  }
  65    :  
  66  E :  BOOL WINAPI asan_HeapDestroy(HANDLE heap) {
  67  E :    DCHECK(process_heap != NULL);
  68  E :    if (heap == process_heap)
  69  i :      return ::HeapDestroy(heap);
  70    :  
  71  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
  72  E :    if (!proxy)
  73  i :      return FALSE;
  74    :  
  75  E :    asan_runtime->RemoveHeap(proxy);
  76    :  
  77  E :    if (proxy->Destroy()) {
  78  E :      delete proxy;
  79  E :      return TRUE;
  80    :    }
  81    :  
  82  i :    return FALSE;
  83  E :  }
  84    :  
  85    :  LPVOID WINAPI asan_HeapAlloc(HANDLE heap,
  86    :                               DWORD flags,
  87  E :                               SIZE_T bytes) {
  88  E :    DCHECK(process_heap != NULL);
  89  E :    if (heap == process_heap)
  90  i :      return ::HeapAlloc(heap, flags, bytes);
  91    :  
  92  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
  93  E :    if (!proxy)
  94  i :      return NULL;
  95    :  
  96  E :    return proxy->Alloc(flags, bytes);
  97  E :  }
  98    :  
  99    :  LPVOID WINAPI asan_HeapReAlloc(HANDLE heap,
 100    :                                 DWORD flags,
 101    :                                 LPVOID mem,
 102  E :                                 SIZE_T bytes) {
 103  E :    DCHECK(process_heap != NULL);
 104  E :    if (heap == process_heap)
 105  i :      return ::HeapReAlloc(heap, flags, mem, bytes);
 106    :  
 107  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 108  E :    if (!proxy)
 109  i :      return NULL;
 110    :  
 111  E :    return proxy->ReAlloc(flags, mem, bytes);
 112  E :  }
 113    :  
 114    :  BOOL WINAPI asan_HeapFree(HANDLE heap,
 115    :                            DWORD flags,
 116  E :                            LPVOID mem) {
 117  E :    DCHECK(process_heap != NULL);
 118  E :    if (heap == process_heap)
 119  i :      return ::HeapFree(heap, flags, mem);
 120    :  
 121  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 122  E :    if (!proxy)
 123  i :      return FALSE;
 124    :  
 125  E :    if (!proxy->Free(flags, mem)) {
 126    :      CONTEXT context;
 127  i :      ::RtlCaptureContext(&context);
 128  i :      asan_runtime->OnError(&context);
 129  i :      return false;
 130    :    }
 131    :  
 132  E :    return true;
 133  E :  }
 134    :  
 135    :  SIZE_T WINAPI asan_HeapSize(HANDLE heap,
 136    :                              DWORD flags,
 137  E :                              LPCVOID mem) {
 138  E :    DCHECK(process_heap != NULL);
 139  E :    if (heap == process_heap)
 140  i :      return ::HeapSize(heap, flags, mem);
 141    :  
 142  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 143  E :    if (!proxy)
 144  i :      return -1;
 145    :  
 146  E :    return proxy->Size(flags, mem);
 147  E :  }
 148    :  
 149    :  BOOL WINAPI asan_HeapValidate(HANDLE heap,
 150    :                                DWORD flags,
 151  E :                                LPCVOID mem) {
 152  E :    DCHECK(process_heap != NULL);
 153  E :    if (heap == process_heap)
 154  i :      return ::HeapValidate(heap, flags, mem);
 155    :  
 156  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 157  E :    if (!proxy)
 158  i :      return FALSE;
 159    :  
 160  E :    return proxy->Validate(flags, mem);
 161  E :  }
 162    :  
 163    :  SIZE_T WINAPI asan_HeapCompact(HANDLE heap,
 164  E :                                 DWORD flags) {
 165  E :    DCHECK(process_heap != NULL);
 166  E :    if (heap == process_heap)
 167  i :      return ::HeapCompact(heap, flags);
 168    :  
 169  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 170  E :    if (!proxy)
 171  i :      return 0;
 172    :  
 173  E :    return proxy->Compact(flags);
 174  E :  }
 175    :  
 176  E :  BOOL WINAPI asan_HeapLock(HANDLE heap) {
 177  E :    DCHECK(process_heap != NULL);
 178  E :    if (heap == process_heap)
 179  i :      return ::HeapLock(heap);
 180    :  
 181  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 182  E :    if (!proxy)
 183  i :      return FALSE;
 184    :  
 185  E :    return proxy->Lock();
 186  E :  }
 187    :  
 188  E :  BOOL WINAPI asan_HeapUnlock(HANDLE heap) {
 189  E :    DCHECK(process_heap != NULL);
 190  E :    if (heap == process_heap)
 191  i :      return ::HeapUnlock(heap);
 192    :  
 193  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 194  E :    if (!proxy)
 195  i :      return FALSE;
 196    :  
 197  E :    return proxy->Unlock();
 198  E :  }
 199    :  
 200    :  BOOL WINAPI asan_HeapWalk(HANDLE heap,
 201  E :                            LPPROCESS_HEAP_ENTRY entry) {
 202  E :    DCHECK(process_heap != NULL);
 203  E :    if (heap == process_heap)
 204  i :      return ::HeapWalk(heap, entry);
 205    :  
 206  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 207  E :    if (!proxy)
 208  i :      return FALSE;
 209    :  
 210  E :    return proxy->Walk(entry);
 211  E :  }
 212    :  
 213    :  BOOL WINAPI asan_HeapSetInformation(
 214    :      HANDLE heap, HEAP_INFORMATION_CLASS info_class,
 215  E :      PVOID info, SIZE_T info_length) {
 216  E :    DCHECK(process_heap != NULL);
 217  E :    if (heap == NULL || heap == process_heap)
 218  E :      return ::HeapSetInformation(heap, info_class, info, info_length);
 219    :  
 220  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 221  E :    if (!proxy)
 222  i :      return FALSE;
 223    :  
 224  E :    return proxy->SetInformation(info_class, info, info_length);
 225  E :  }
 226    :  
 227    :  BOOL WINAPI asan_HeapQueryInformation(
 228    :      HANDLE heap, HEAP_INFORMATION_CLASS info_class,
 229  E :      PVOID info, SIZE_T info_length, PSIZE_T return_length) {
 230  E :    DCHECK(process_heap != NULL);
 231  E :    if (heap == NULL || heap == process_heap) {
 232    :      return ::HeapQueryInformation(heap,
 233    :                                    info_class,
 234    :                                    info,
 235    :                                    info_length,
 236  i :                                    return_length);
 237    :    }
 238    :  
 239  E :    HeapProxy* proxy = HeapProxy::FromHandle(heap);
 240  E :    if (!proxy)
 241  i :      return FALSE;
 242    :  
 243    :    bool ret = proxy->QueryInformation(info_class,
 244    :                                       info,
 245    :                                       info_length,
 246  E :                                       return_length);
 247  E :    return ret == true;
 248  E :  }
 249    :  
 250  E :  void WINAPI asan_SetCallBack(void (*callback)(CONTEXT*)) {
 251  E :    DCHECK(asan_runtime != NULL);
 252  E :    asan_runtime->SetErrorCallBack(callback);
 253  E :  }
 254    :  
 255    :  }  // extern "C"
 256    :  
 257    :  namespace agent {
 258    :  namespace asan {
 259    :  
 260    :  // Represent the content of the stack before calling the error function.
 261    :  // The original_* fields store the value of the registers as they were before
 262    :  // calling the asan hook, the do_not_use_* values are some stack variables used
 263    :  // by asan but shouldn't be used to produce the stack trace.
 264    :  // NOTE: As this structure describes the state of the stack at the time
 265    :  // ReportBadMemoryAccess is called. It is intimately tied to the implementation
 266    :  // of the asan_check functions.
 267    :  #pragma pack(push, 1)
 268    :  struct AsanContext {
 269    :    DWORD original_edi;
 270    :    DWORD original_esi;
 271    :    DWORD original_ebp;
 272    :    DWORD do_not_use_esp;
 273    :    DWORD original_ebx;
 274    :    DWORD do_not_use_edx;
 275    :    DWORD original_ecx;
 276    :    DWORD do_not_use_eax;
 277    :    DWORD original_eflags;
 278    :    // This is the location of the bad access for this context.
 279    :    DWORD do_not_use_flags;
 280    :    void* location;
 281    :    DWORD original_eax;
 282    :    DWORD original_eip;
 283    :    DWORD original_edx;
 284    :  };
 285    :  #pragma pack(pop)
 286    :  
 287    :  // Report a bad access to the memory.
 288    :  // @param access_mode The mode of the access.
 289    :  // @param access_size The size of the access.
 290    :  // @param asan_context The context of the access.
 291    :  void __stdcall ReportBadMemoryAccess(HeapProxy::AccessMode access_mode,
 292    :                                       size_t access_size,
 293  E :                                       struct AsanContext* asan_context) {
 294    :    // Capture the context and restore the value of the register as before calling
 295    :    // the asan hook.
 296    :  
 297    :    // Capture the current context.
 298  E :    CONTEXT context = {};
 299    :  
 300    :    // We need to call ::RtlCaptureContext if we want SegSS and SegCS to be
 301    :    // properly set.
 302  E :    ::RtlCaptureContext(&context);
 303  E :    context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
 304    :  
 305    :    // Restore the original value of the registers.
 306  E :    context.Eip = asan_context->original_eip;
 307  E :    context.Eax = asan_context->original_eax;
 308  E :    context.Ecx = asan_context->original_ecx;
 309  E :    context.Edx = asan_context->original_edx;
 310  E :    context.Ebx = asan_context->original_ebx;
 311  E :    context.Ebp = asan_context->original_ebp;
 312  E :    context.Esi = asan_context->original_esi;
 313  E :    context.Edi = asan_context->original_edi;
 314  E :    context.EFlags = asan_context->original_eflags;
 315    :  
 316    :    // Our AsanContext structure is in fact the whole stack frame for the asan
 317    :    // hook, to compute the value of esp before the call to the hook we can just
 318    :    // calculate the top address of this structure.
 319  E :    context.Esp = reinterpret_cast<DWORD>(asan_context) + sizeof(AsanContext);
 320    :  
 321  E :    StackCapture stack;
 322  E :    stack.InitFromStack();
 323    :    // We need to compute a relative stack id so that for the same stack trace
 324    :    // we'll get the same value every time even if the modules are loaded at a
 325    :    // different base address.
 326  E :    stack.set_stack_id(stack.ComputeRelativeStackId());
 327    :  
 328    :    // Check if we can ignore this error.
 329  E :    if (asan_runtime->ShouldIgnoreError(stack.stack_id()))
 330  i :      return;
 331    :  
 332    :    asan_runtime->ReportAsanErrorDetails(asan_context->location,
 333    :                                         context,
 334    :                                         stack,
 335    :                                         access_mode,
 336  E :                                         access_size);
 337    :  
 338    :    // Call the callback to handle this error.
 339  E :    asan_runtime->OnError(&context);
 340  E :  }
 341    :  
 342    :  }  // namespace asan
 343    :  }  // namespace agent
 344    :  
 345    :  // Generates the asan check access functions. The name of the generated method
 346    :  // will be asan_check_(@p access_size)_byte_(@p access_mode_str)().
 347    :  // @param access_size The size of the access (in byte).
 348    :  // @param access_mode_str The string representing the access mode (read_access
 349    :  //     or write_access).
 350    :  // @param access_mode_value The internal value representing this kind of access.
 351    :  #define ASAN_CHECK_FUNCTION(access_size, access_mode_str, access_mode_value)  \
 352    :    extern "C" __declspec(naked)  \
 353    :        void asan_check_ ## access_size ## _byte_ ## access_mode_str ## () {  \
 354    :      __asm {  \
 355    :        /* Save eax as we'll use it to save the value of the flags. */  \
 356    :        __asm push eax  \
 357    :        /* Save the low byte of the flags into ah. We use this instruction */  \
 358    :        /* instead of pushfd/popfd because it's much faster. We also save */  \
 359    :        /* the overflow flag into al. We can do this because our hooks are */  \
 360    :        /* simple and don't touch the other flags. */  \
 361    :        __asm lahf  \
 362    :        __asm seto al  \
 363    :        /* Save edx for the slow path. */  \
 364    :        __asm push edx  \
 365    :        /* Check for zero shadow - fast case. */  \
 366    :        __asm shr edx, 3  \
 367    :        __asm movzx edx, BYTE ptr[edx + agent::asan::Shadow::shadow_]  \
 368    :        __asm test dl, dl  \
 369    :        __asm jnz check_access_slow  \
 370    :        /* Restore original edx. */  \
 371    :        __asm add esp, 4  \
 372    :        __asm mov edx, DWORD PTR[esp + 8]  \
 373    :        /* al is set to 1 if the overflow flag was set before the call to */  \
 374    :        /* our hook, 0 otherwise. We add 0x7f to it so it'll restore the */  \
 375    :        /* flag. */  \
 376    :        __asm add al, 0x7f  \
 377    :        /* Restore the low byte of the flags. */  \
 378    :        __asm sahf  \
 379    :        /* Restore original eax */  \
 380    :        __asm pop eax  \
 381    :        __asm ret 4  \
 382    :      __asm check_access_slow:  \
 383    :        /* Save flags on stack (keep in eax in the fastpath) */  \
 384    :        __asm push eax  \
 385    :        /* Uh-oh - non-zero shadow byte means we go to the slow case. */  \
 386    :        /* Save ecx, it's caller-save (eax, ecx and edx are caller-save). */  \
 387    :        __asm push ecx  \
 388    :        /* Push the address to check. */  \
 389    :        __asm push DWORD ptr[esp + 8]  \
 390    :        __asm call agent::asan::Shadow::IsAccessible  \
 391    :        __asm test al, al  \
 392    :        __asm pop ecx  \
 393    :        /* We've found a bad access, report this failure. */  \
 394    :        __asm jz report_failure  \
 395    :        /* Same code as in the fast path, we could jump there but it'll add */  \
 396    :        /* an instruction. */  \
 397    :       __asm epilogue:  \
 398    :        __asm pop eax  \
 399    :        __asm add esp, 4  \
 400    :        __asm add al, 0x7f  \
 401    :        __asm sahf  \
 402    :        __asm mov edx, DWORD PTR[esp + 8]  \
 403    :        __asm pop eax  \
 404    :        __asm ret 4  \
 405    :      __asm report_failure:  \
 406    :        /* As we give the user the ability to change the error handler, we */  \
 407    :        /* need to save the flags with pushfd because now we can't guarantee */  \
 408    :        /* that the direction flag won't be touched. */  \
 409    :        /* We start by restoring the flags. */  \
 410    :        __asm mov eax, DWORD PTR[esp]  \
 411    :        __asm add al, 0x7f  \
 412    :        __asm sahf  \
 413    :        /* Then we push them. */  \
 414    :        __asm pushfd  \
 415    :        /* Push all the register to have the full asan context on the stack.*/  \
 416    :        __asm pushad  \
 417    :        /* Push a pointer to this context. */  \
 418    :        __asm push esp  \
 419    :        /* Push the access size. */  \
 420    :        __asm push access_size  \
 421    :        /* Push the access type. */  \
 422    :        __asm push access_mode_value  \
 423    :        /* Call the error handler. */  \
 424    :        __asm call agent::asan::ReportBadMemoryAccess  \
 425    :        __asm popad  \
 426    :        __asm popfd  \
 427    :        __asm jmp epilogue  \
 428    :      }  \
 429    :    }
 430    :  
 431    :  enum AccessMode {
 432    :    AsanReadAccess = HeapProxy::ASAN_READ_ACCESS,
 433    :    AsanWriteAccess = HeapProxy::ASAN_WRITE_ACCESS,
 434    :  };
 435    :  
 436  i :  ASAN_CHECK_FUNCTION(1, read_access, AsanReadAccess)
 437  i :  ASAN_CHECK_FUNCTION(2, read_access, AsanReadAccess)
 438  i :  ASAN_CHECK_FUNCTION(4, read_access, AsanReadAccess)
 439  i :  ASAN_CHECK_FUNCTION(8, read_access, AsanReadAccess)
 440  i :  ASAN_CHECK_FUNCTION(10, read_access, AsanReadAccess)
 441  i :  ASAN_CHECK_FUNCTION(16, read_access, AsanReadAccess)
 442  i :  ASAN_CHECK_FUNCTION(32, read_access, AsanReadAccess)
 443  i :  ASAN_CHECK_FUNCTION(1, write_access, AsanWriteAccess)
 444  i :  ASAN_CHECK_FUNCTION(2, write_access, AsanWriteAccess)
 445  i :  ASAN_CHECK_FUNCTION(4, write_access, AsanWriteAccess)
 446  i :  ASAN_CHECK_FUNCTION(8, write_access, AsanWriteAccess)
 447  i :  ASAN_CHECK_FUNCTION(10, write_access, AsanWriteAccess)
 448  i :  ASAN_CHECK_FUNCTION(16, write_access, AsanWriteAccess)
 449  i :  ASAN_CHECK_FUNCTION(32, write_access, AsanWriteAccess)
 450    :  
 451    :  #undef ASAN_CHECK_FUNCTION
 452    :  
 453    :  // Generates the asan check access functions for the string instruction 'cmps'.
 454    :  // The name of the generated method will be
 455    :  // asan_check_(@p prefix)(@p access_size)_byte_cmps_access().
 456    :  // @param prefix The prefix of the instruction (repz or nothing).
 457    :  // @param counter The number of times the instruction must be executed.
 458    :  //     It may be a register or a constant.
 459    :  // @param access_size The size of the access (in byte).
 460    :  // @param inst The instruction used to move esi/edi (note: direction flag).
 461    :  #define ASAN_CHECK_CMPS_FUNCTION(prefix, counter, access_size, inst)  \
 462    :    extern "C" __declspec(naked)  \
 463    :    void asan_check ## prefix ## access_size ## _byte_cmps_access() {  \
 464    :      /* TODO(etienneb) : implement this function. */  \
 465    :      __asm ret  \
 466    :    }
 467    :  
 468  i :  ASAN_CHECK_CMPS_FUNCTION(_repz_, ecx, 4, cmps)
 469  i :  ASAN_CHECK_CMPS_FUNCTION(_repz_, ecx, 2, cmpsw)
 470  i :  ASAN_CHECK_CMPS_FUNCTION(_repz_, ecx, 1, cmpsb)
 471  i :  ASAN_CHECK_CMPS_FUNCTION(_, 1, 4, cmps)
 472  i :  ASAN_CHECK_CMPS_FUNCTION(_, 1, 2, cmpsw)
 473  i :  ASAN_CHECK_CMPS_FUNCTION(_, 1, 1, cmpsb)
 474    :  
 475    :  #undef ASAN_CHECK_CMPS_FUNCTION
 476    :  
 477    :  // Generates the asan check access functions for the string instruction 'movs'.
 478    :  // The name of the generated method will be
 479    :  // asan_check_(@p prefix)(@p access_size)_byte_movs_access().
 480    :  // @param prefix The prefix of the instruction (repz or nothing).
 481    :  // @param counter The number of times the instruction must be executed.
 482    :  //     It may be a register or a constant.
 483    :  // @param access_size The size of the access (in byte).
 484    :  // @param inst The instruction used to move esi/edi (note: direction flag).
 485    :  #define ASAN_CHECK_MOVS_FUNCTION(prefix, counter, access_size, inst)  \
 486    :    extern "C" __declspec(naked)  \
 487    :    void asan_check ## prefix ## access_size ## _byte_movs_access() {  \
 488    :      /* TODO(etienneb) : implement this function. */  \
 489    :      __asm ret  \
 490    :    }
 491    :  
 492  i :  ASAN_CHECK_MOVS_FUNCTION(_repz_, ecx, 4, cmps)
 493  i :  ASAN_CHECK_MOVS_FUNCTION(_repz_, ecx, 2, cmpsw)
 494  i :  ASAN_CHECK_MOVS_FUNCTION(_repz_, ecx, 1, cmpsb)
 495  i :  ASAN_CHECK_MOVS_FUNCTION(_, 1, 4, cmps);
 496  i :  ASAN_CHECK_MOVS_FUNCTION(_, 1, 2, cmpsw);
 497  i :  ASAN_CHECK_MOVS_FUNCTION(_, 1, 1, cmpsb);
 498    :  
 499    :  #undef ASAN_CHECK_MOVS_FUNCTION
 500    :  
 501    :  // Generates the asan check access functions for the string instruction 'stos'.
 502    :  // The name of the generated method will be
 503    :  // asan_check_(@p prefix)(@p access_size)_byte_movs_access().
 504    :  // @param prefix The prefix of the instruction (repz or nothing).
 505    :  // @param counter The number of times the instruction must be executed.
 506    :  //     It may be a register or a constant.
 507    :  // @param access_size The size of the access (in byte).
 508    :  // @param inst The instruction used to move esi/edi (note: direction flag).
 509    :  #define ASAN_CHECK_STOS_FUNCTION(prefix, counter, access_size, inst)  \
 510    :    extern "C" __declspec(naked)  \
 511    :    void asan_check ## prefix ## access_size ## _byte_stos_access() {  \
 512    :      /* TODO(etienneb) : implement this function. */  \
 513    :      __asm ret  \
 514    :    }
 515    :  
 516  i :  ASAN_CHECK_STOS_FUNCTION(_repz_, ecx, 4, cmps)
 517  i :  ASAN_CHECK_STOS_FUNCTION(_repz_, ecx, 2, cmpsw)
 518  i :  ASAN_CHECK_STOS_FUNCTION(_repz_, ecx, 1, cmpsb)
 519  i :  ASAN_CHECK_STOS_FUNCTION(_, 1, 4, cmps);
 520  i :  ASAN_CHECK_STOS_FUNCTION(_, 1, 2, cmpsw);
 521  i :  ASAN_CHECK_STOS_FUNCTION(_, 1, 1, cmpsb);
 522    :  
 523    :  #undef ASAN_CHECK_STOS_FUNCTION

Coverage information generated Thu Mar 14 11:53:36 2013.