Bug 58543 - Invalid unpoisoning of stack redzones on ARM
Summary: Invalid unpoisoning of stack redzones on ARM
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: sanitizer (show other bugs)
Version: 4.9.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2013-09-26 15:05 UTC by Yury Gribov
Modified: 2014-08-11 21:58 UTC (History)
5 users (show)

See Also:
Host:
Target: arm
Build:
Known to work:
Known to fail: 4.9.0
Last reconfirmed: 2013-10-31 00:00:00


Attachments
Repro (71 bytes, text/plain)
2013-09-26 15:05 UTC, Yury Gribov
Details
Proposed patch (289 bytes, patch)
2013-09-26 15:06 UTC, Yury Gribov
Details | Diff
Test results (706 bytes, text/x-log)
2013-09-27 09:56 UTC, Yury Gribov
Details
Standalone repro (305 bytes, text/x-csrc)
2013-10-02 06:18 UTC, Yury Gribov
Details
Changelog entry (130 bytes, text/plain)
2013-10-18 11:04 UTC, Yury Gribov
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Yury Gribov 2013-09-26 15:05:18 UTC
Created attachment 30902 [details]
Repro

Gcc seems to generate mismatched prologue/epilogue code to poison/unpoison stack frame for the attached source code when I compile it with

 $ ~/install/gcc-master-arm/bin/arm-v7a15v3r2-linux-gnueabi-gcc bad.i -O0 -S -fsanitize=address

Prologue seems to poison words at frame_shadow_base + { 0, 4, 8, 12, 16, 24, 28}:

  add  r4, r3, #536870912
  ldr  r3, .L3+8
  str  r3, [r4]
  ldr  r3, .L3+12
  str  r3, [r4, #4]
  ldr  r3, .L3+16
  str  r3, [r4, #8]
  ldr  r3, .L3+20
  str  r3, [r4, #12]
  ldr  r3, .L3+16
  str  r3, [r4, #16]
  ldr  r3, .L3+20
  str  r3, [r4, #24]
  ldr  r3, .L3+24
  str  r3, [r4, #28]

Whereas epilogue poisons frame_shadow_base + { 0, 4, 8, 12, 16, 40, 44}:

  add  r3, r4, #20
.L1:
  mov  r2, #0
  str  r2, [r4]
  add  r4, r4, #4
  cmp  r4, r3
  bcc  .L1
  add  r3, r4, #24   ; r3 gets invalid value here
  mov  r2, #0
  strb  r2, [r3]
  add  r3, r3, #1
  mov  r2, #0
  strb  r2, [r3]
  add  r3, r3, #1
  mov  r2, #0
  strb  r2, [r3]
  add  r3, r3, #1
  mov  r2, #0
  strb  r2, [r3]
  add  r3, r3, #1
  mov  r2, #0
  strb  r2, [r3]
  add  r3, r3, #1
  mov  r2, #0
  strb  r2, [r3]
  add  r3, r3, #1
  mov  r2, #0
  strb  r2, [r3]
  add  r3, r3, #1
  mov  r2, #0
  strb  r2, [r3]

This causes some shadow bytes to remain set after function returns. Those may trigger incorrect Asan errors later.

-Y


PS: my configuration:

* GCC version: trunk, September 24

* OS: Ubuntu 12.04

* GCC configuration string: ~/gcc/gcc-master/configure --target=arm-v7a15v3r2-linux-gnueabi --prefix=/home/ygribov/install/gcc-master-arm --disable-libmudflap --disable-libssp --disable-nls --enable-long-long --enable-languages=c,c++ --disable-bootstrap --disable-multilib --disable-libstdcxx --disable-libgcc --disable-libgomp --disable-libatomic --disable-libquadmath --disable-libstdc++-v3 --disable-libsanitizer --disable-libitm
Comment 1 Yury Gribov 2013-09-26 15:06:20 UTC
Created attachment 30903 [details]
Proposed patch
Comment 2 Yury Gribov 2013-09-26 15:06:37 UTC
I've attached a simple patch which causes compiler new register to allocate new temp address register. I'll only be able to test it tomorrow though.
Comment 3 Yury Gribov 2013-09-27 09:56:40 UTC
Created attachment 30908 [details]
Test results

Tests seem to pass both on x86_64 and on ARM (attached).
Comment 4 dodji@seketeli.org 2013-09-30 10:15:32 UTC
Thank you for reporting this bug.

Please find my comments below,


"y.gribov at samsung dot com" <gcc-bugzilla@gcc.gnu.org> a écrit:

> Prologue seems to poison words at frame_shadow_base + { 0, 4, 8, 12, 16, 24,
> 28}:

Right.

>
>   add  r4, r3, #536870912
>   ldr  r3, .L3+8
>   str  r3, [r4]
>   ldr  r3, .L3+12
>   str  r3, [r4, #4]
>   ldr  r3, .L3+16
>   str  r3, [r4, #8]
>   ldr  r3, .L3+20
>   str  r3, [r4, #12]
>   ldr  r3, .L3+16
>   str  r3, [r4, #16]
>   ldr  r3, .L3+20
>   str  r3, [r4, #24]
>   ldr  r3, .L3+24
>   str  r3, [r4, #28]
>
> Whereas epilogue poisons frame_shadow_base + { 0, 4, 8, 12, 16, 40, 44}:

I guess you mean *un*poison here.

>   add  r3, r4, #20
> .L1:
>   mov  r2, #0
>   str  r2, [r4]
>   add  r4, r4, #4
>   cmp  r4, r3
>   bcc  .L1

My understanding is that in the loop above, we are setting the memory
pointed to by frame_shadow_base + { 0, 4, 8, 12, 16} to zero.

And in the code below, we are preparing to set the memory pointed to by
frame_shadow_base + {24, 28} to zero.

>   add  r3, r4, #24   ; r3 gets invalid value here

Why is r3 invalid?  It's being set to #24, so that the strb r2, [r3]
below writes a zero byte to [r4,#24].

Or what am I missing?

>   mov  r2, #0
>   strb  r2, [r3]
>   add  r3, r3, #1
>   mov  r2, #0
>   strb  r2, [r3]
>   add  r3, r3, #1
>   mov  r2, #0
>   strb  r2, [r3]
>   add  r3, r3, #1
>   mov  r2, #0
>   strb  r2, [r3]
>   add  r3, r3, #1
>   mov  r2, #0
>   strb  r2, [r3]
>   add  r3, r3, #1
>   mov  r2, #0
>   strb  r2, [r3]
>   add  r3, r3, #1
>   mov  r2, #0
>   strb  r2, [r3]
>   add  r3, r3, #1
>   mov  r2, #0
>   strb  r2, [r3]
>
> This causes some shadow bytes to remain set after function returns. Those may
> trigger incorrect Asan errors later.

I am guessing that you have a short and self contained example of an
asan error that is caused by a wrong epilogue.  Would it be possible
that you file it so that I can understand better what is going on?

Thanks.
Comment 5 Yury Gribov 2013-09-30 10:26:24 UTC
> I guess you mean *un*poison here.

Right you are!

> My understanding is that in the loop above, we are setting the memory
> pointed to by frame_shadow_base + { 0, 4, 8, 12, 16} to zero.
>
> And in the code below, we are preparing to set the memory pointed to by
> frame_shadow_base + {24, 28} to zero.

Exactly.

>   add  r3, r4, #24   ; r3 gets invalid value here

> Why is r3 invalid?  It's being set to #24,
> so that the strb r2, [r3] below writes a zero byte to [r4,#24].

Not really - it's set to #40 because r4 was changed to #16 inside the loop

> Would it be possible that you file it
> so that I can understand better what is going on?

Sure - simply compile the attached repro with `-O0 -fsanitize-address'. But note that you need to use ARM target because x86 does not trigger this bug (most probably because it uses different code path in asan_clear_shadow).

-Y
Comment 6 Yury Gribov 2013-10-01 08:28:00 UTC
Dodji,

Let me know if I can provide additional info which may help to debug/fix this.

-Y
Comment 7 Yury Gribov 2013-10-02 06:18:00 UTC
Created attachment 30946 [details]
Standalone repro

Dodji,

It has just occured to me that you probably want an executable repro with nice runtime error. Here is the first try (mainly a harness around the previous repro).

The testcase firstly runs function bad() which fails to unpoison frame shadow. It then tries to accesses bad()'s stack which triggers Asan error.

Simply compile the attached file with `-fsanitize=address -O2' and run it on ARM target. As I mentioned before, the stack shadow gets borked so by default you'll get an abort from Asan runtime:

=================================================================
==3366== ERROR: AddressSanitizer: stack-buffer-overflow on address 0xbefff797 at pc 0x8670 bp 0xbefff7ac sp 0xbefff7a4
READ of size 1 at 0xbefff797 thread T0
    #0 0x866f (/dtv/usb/sda1/a.out+0x866f)
    #1 0x41632bcb (/mtd_exe/lib/libc-2.14.1.so+0x17bcb)
==3366== AddressSanitizer CHECK failed: /home/ygribov/gcc/gcc-master/libsanitizer/asan/asan_report.cc:250 "((name_end)) != (0)" (0x0, 0x0)
    #0 0x4003ace7 (/dtv/usb/sda1/libasan.so.0+0x12ce7)
    #1 0x400436fb (/dtv/usb/sda1/libasan.so.0+0x1b6fb)
    #2 0x4004121b (/dtv/usb/sda1/libasan.so.0+0x1921b)
    #3 0x4004136f (/dtv/usb/sda1/libasan.so.0+0x1936f)
    #4 0x40041feb (/dtv/usb/sda1/libasan.so.0+0x19feb)
    #5 0x4003b18b (/dtv/usb/sda1/libasan.so.0+0x1318b)
    #6 0x866f (/dtv/usb/sda1/a.out+0x866f)
    #7 0x41632bcb (/mtd_exe/lib/libc-2.14.1.so+0x17bcb)

This is already a sign of invalid instrumentation. To get a more informative output, replace
 CHECK(name_end);
with
 if(!name_end) return true;
in libsanitizer/asan/asan_report.cc:
=================================================================
==3468== ERROR: AddressSanitizer: stack-buffer-overflow on address 0xbefff797 at pc 0x8670 bp 0xbefff7ac sp 0xbefff7a4
READ of size 1 at 0xbefff797 thread T0
    #0 0x866f (/dtv/usb/sda1/a.out+0x866f)
    #1 0x41632bcb (/mtd_exe/lib/libc-2.14.1.so+0x17bcb)
Shadow bytes around the buggy address:
  0x37dffea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x37dffeb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x37dffec0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x37dffed0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x37dffee0: 00 00 00 00 00 00 00 00 00 00 00 00 f4 f4 f4 f3
=>0x37dffef0: f3 f3[f3]00 00 00 f1 f1 f1 f1 01 f4 f4 f4 f3 00
  0x37dfff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x37dfff10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x37dfff20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x37dfff30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x37dfff40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap righ redzone:     fb
  Freed Heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==3468== ABORTING

You can see that bad() function has not cleared the poisoned bytes.

-Y
Comment 8 Yury Gribov 2013-10-18 11:04:50 UTC
Created attachment 31032 [details]
Changelog entry

Adding Changelog entry.
Comment 9 Ramana Radhakrishnan 2013-10-31 09:58:32 UTC
Thread on lists. 

http://gcc.gnu.org/ml/gcc-patches/2013-10/msg00380.html
Comment 10 Yury Gribov 2013-10-31 12:10:03 UTC
Author: ygribov
Date: Thu Oct 31 12:10:01 2013
New Revision: 204251

URL: http://gcc.gnu.org/viewcvs?rev=204251&root=gcc&view=rev
Log:
2013-10-31  Richard Sandiford  <rsandifo@linux.vnet.ibm.com>
	    Yury Gribov  <y.gribov@samsung.com>

	PR sanitizer/58543
	* asan.c (asan_clear_shadow): Allocate a new vreg for temporary
	shadow pointer to avoid clobbering the main one.

Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/asan.c
Comment 11 Yury Gribov 2013-11-15 08:34:28 UTC
Fixed by afore-mentioned commit.
Comment 12 Yvan Roux 2014-08-11 21:58:18 UTC
Author: yroux
Date: Mon Aug 11 21:57:46 2014
New Revision: 213841

URL: https://gcc.gnu.org/viewcvs?rev=213841&root=gcc&view=rev
Log:
2014-08-11  Michael Collison  <michael.collison@linaro.org>

	Backport from trunk r204251
	2013-10-31  Richard Sandiford  <rsandifo@linux.vnet.ibm.com>
	Yury Gribov  <y.gribov@samsung.com>

	PR sanitizer/58543
	* asan.c (asan_clear_shadow): Allocate a new vreg for temporary
	shadow pointer to avoid clobbering the main one.


Modified:
    branches/linaro/gcc-4_8-branch/gcc/ChangeLog.linaro
    branches/linaro/gcc-4_8-branch/gcc/asan.c