Skip to content

sincos args may get clobbered after #108401 when passed on the stack #115323

@zmodem

Description

@zmodem

Consider this program on 32-bit x86, where arguments are passed on the stack:

$ cat /tmp/a.cc
#include <math.h>
#include <stdio.h>

double __attribute__((noinline)) g(double a, double b) { return a + b; }

double __attribute__((noinline)) f(double a) {
  double foo = sin(a);
  double bar = cos(a);
  double z = g(bar + 3.14, foo);
  return z;
}

int main() {
  printf("%f\n", f(3.14));
  return 0;
}

After 3073c3c, it fails under ASan:

$ build/bin/clang.bad -target i386-unknown-linux-gnu -lm -fsanitize=address -fno-math-errno -O2 /tmp/a.cc && ASAN_OPTIONS=external_symbolizer_path=$PWD/build/bin/llvm-symbolizer ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==3898196==ERROR: AddressSanitizer: SEGV on unknown address 0x27eb4300 (pc 0x566160ed bp 0xffab1898 sp 0xffab1470 T0)
==3898196==The signal is caused by a READ memory access.
    #0 0x566160ed in QuickCheckForUnpoisonedRegion /work/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.h:37:7
    #1 0x566160ed in sincos /work/llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:5167:12
    #2 0x566b25b1 in f(double) (/work/llvm-project/a.out+0x1085b1)
    #3 0x566b25f3 in main (/work/llvm-project/a.out+0x1085f3)
    #4 0xf7c29b84  (/lib/i386-linux-gnu/libc.so.6+0x23b84) (BuildId: d16f2b0239d79fbec67438094f9a9443121ab72d)
    #5 0xf7c29c47 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x23c47) (BuildId: d16f2b0239d79fbec67438094f9a9443121ab72d)
    #6 0x565c43a6 in _start (/work/llvm-project/a.out+0x1a3a6)

==3898196==Register values:
eax = 0x27eb4300  ebx = 0x27eb4303  ecx = 0x27eb4300  edx = 0x3f5a1819  
edi = 0x07eb4303  esi = 0x07eb4302  ebp = 0xffab1898  esp = 0xffab1470  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/work/llvm-project/a.out+0x1085b1) in f(double)
==3898196==ABORTING

The calls to sin and cos will get folded to a sincos call. After 3073c3c, the call looks like this:

  37:   89 e6                   mov    %esp,%esi
  39:   8d 44 24 18             lea    0x18(%esp),%eax
  3d:   89 44 24 0c             mov    %eax,0xc(%esp)    // <-- cos
  41:   8d 46 08                lea    0x8(%esi),%eax
  44:   89 44 24 08             mov    %eax,0x8(%esp)    // <-- sin
  48:   f2 0f 11 04 24          movsd  %xmm0,(%esp)      // <-- x
  4d:   e8 fc ff ff ff          call   4e <_Z1fd+0x2e>
                        4e: R_386_PLT32 sincos

Look at the value of the sin argument: it's 0x8(%esi) = 0x8(%esp) which is the stack slot for foo in the following call to g. But it's also the stack slot used for sin in the sincos call. This seems dangerous.

Since it's a 64-bit value, when sincos does *sin = ... it will clobber both the sin and cos arguments. ASan happens to notice, because it intercepts sincos and checks the validity of the pointers afterwards.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions