<div dir="ltr">We do actually have (at least) two sanitizers that catch some cases of this source of undefined behavior.<div><br></div><div>1) We have a pointer overflow sanitizer that catches pointer arithmetic that wraps around the address space.</div><div><br></div><div>  buf + very_large_number</div><div><br></div><div>2) We have an array bounds sanitizer that catches pointer arithmetic that leaves the bounds of an array of known size.<br><div><div><br></div><div>   int n = 7;<br>   int arr[5];<br>   int *p = arr + n;<br></div></div></div><div><br></div><div>Both checks are somewhat simplistic and could be extended to catch more cases:</div><div>(1) could be taught to catch pointer arithmetic that leaves the bounds determined by __builtin_object_size.</div><div>(2) could be taught to determine the known array bound in more cases, for example catching '&n + 2' because (at least in C++) the language rules say that 'n' is treated as an array of bound 1 for the purposes of pointer arithmetic.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 14 Jan 2021 at 08:03, John Criswell via cfe-dev <<a href="mailto:cfe-dev@lists.llvm.org">cfe-dev@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="overflow-wrap: break-word;">Dear All,<div><br></div><div>Actually, I think this is technically undefined behavior as (IIRC) C allows a pointer to extend one byte past the end of the referent memory object but not any further than that.</div><div><br></div><div>That said, I wouldn’t be surprised if the sanitizers do not catch this case because:</div><div><br></div><div><ol><li>Since the code doesn’t do anything useful, dead code elimination may remove the offending code before the sanitizers instrument the program.</li><li>The out of bounds pointer is not used to read or write memory, so it can’t corrupt or leak memory.  About the most danger it poses is that it may enable optimizations that the programmer is not expecting because the code is undefined.</li></ol><div><br></div><div>I’ve lost track of all the different sanitizers in LLVM and how they work, but the original Address Sanitizer just checks for out of bounds loads and stores; it doesn’t place any checks on pointer arithmetic operations (like the LLVM gep instruction).  That makes it faster at the cost of not catching all pointer arithmetic errors.</div><div><br></div><div>Regards,</div><div><br></div><div>John Criswell</div><div><br></div><div>
<div dir="auto" style="color:rgb(0,0,0);letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none"><div dir="auto" style="color:rgb(0,0,0);letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none"><div>--</div><div>John Criswell</div><div>Associate Professor<br>University of Rochester</div><div><a href="mailto:jtcriswel@gmail.com" target="_blank">jtcriswel@gmail.com</a></div><div><br></div></div><br></div><br><br>
</div>
<div><br><blockquote type="cite"><div>On Jan 14, 2021, at 9:39 AM, Marshall Clow via cfe-dev <<a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a>> wrote:</div><br><div><div><blockquote type="cite">On Jan 10, 2021, at 11:39 PM, Demi M. Obenour via cfe-dev <<a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a>> wrote:<br><br>I noticed that none of the sanitizers seems to support checking for<br>out-of-bounds pointer arithmetic, even though my understanding of<br>the C standard is that this is undefined behavior.  In particular, I<br>believe the following trivial program has undefined behavior (assuming<br>malloc() succeeds), but none of the sanitizers flag any warnings:<br><br>#include <stdlib.h><br>int main(void) {<br>  char *buf = malloc(1);<br>  if (buf) {<br>     char *this_is_ub = buf + 3;<br>     free(buf);<br>  }<br>}<br><br>Of course, I suspect this just has not been implemented yet, but<br>it still leaves me at a loss for how to track this form of UB down.<br>Is there a better solution than manual code review?<br></blockquote><br><br>This program does not have UB.<br>There’s nothing wrong with forming an "out-of-bound” pointer.<br>If you use it for anything, then that is UB - and address sanitizer will find such usages for you.<br><br>Like this:<br><br>#include <stdlib.h><br>int main(void) {<br>  int ret = 0;<br>  char *buf = (char *) malloc(1);<br>  if (buf) {<br>     char *this_is_ub = buf + 3;<br>     ret = *this_is_ub;<br>     free(buf);<br>  }<br>  return ret;<br>}<br><br>——<br>% clang++ -fsanitize=address bug.cpp && ./a.out<br>=================================================================<br>==25602==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000000d3 at pc 0x00010531ff18 bp 0x7ffeea8e0970 sp 0x7ffeea8e0968<br>READ of size 1 at 0x6020000000d3 thread T0<br>    #0 0x10531ff17 in main (a.out:x86_64+0x100000f17)<br>    #1 0x7fff6edd5cc8 in start (libdyld.dylib:x86_64+0x1acc8)<br><br>0x6020000000d3 is located 2 bytes to the right of 1-byte region [0x6020000000d0,0x6020000000d1)<br>allocated by thread T0 here:<br>    #0 0x105e08abd in wrap_malloc (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x45abd)<br>    #1 0x10531feaf in main (a.out:x86_64+0x100000eaf)<br>    #2 0x7fff6edd5cc8 in start (libdyld.dylib:x86_64+0x1acc8)<br><br>SUMMARY: AddressSanitizer: heap-buffer-overflow (a.out:x86_64+0x100000f17) in main<br>Shadow bytes around the buggy address:<br>  0x1c03ffffffc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00<br>  0x1c03ffffffd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00<br>  0x1c03ffffffe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00<br>  0x1c03fffffff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00<br>  0x1c0400000000: fa fa fd fd fa fa 00 00 fa fa 00 00 fa fa 00 04<br>=>0x1c0400000010: fa fa 00 00 fa fa 00 06 fa fa[01]fa fa fa fa fa<br>  0x1c0400000020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa<br>  0x1c0400000030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa<br>  0x1c0400000040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa<br>  0x1c0400000050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa<br>  0x1c0400000060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa<br>Shadow byte legend (one shadow byte represents 8 application bytes):<br>  Addressable:           00<br>  Partially addressable: 01 02 03 04 05 06 07 <br>  Heap left redzone:       fa<br>  Freed heap region:       fd<br>  Stack left redzone:      f1<br>  Stack mid redzone:       f2<br>  Stack right redzone:     f3<br>  Stack after return:      f5<br>  Stack use after scope:   f8<br>  Global redzone:          f9<br>  Global init order:       f6<br>  Poisoned by user:        f7<br>  Container overflow:      fc<br>  Array cookie:            ac<br>  Intra object redzone:    bb<br>  ASan internal:           fe<br>  Left alloca redzone:     ca<br>  Right alloca redzone:    cb<br>  Shadow gap:              cc<br>==25602==ABORTING<br>zsh: abort      ./a.out<br><br>_______________________________________________<br>cfe-dev mailing list<br><a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a><br><a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev</a><br></div></div></blockquote></div><br></div></div>_______________________________________________<br>
cfe-dev mailing list<br>
<a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev</a><br>
</blockquote></div>