Hello everybody.

While sampling with one of our tools I sometimes come across this bug.
If a sample is triggered, presumably while resolving a symbol of a
shared library, a SIGSEGV occurs.

As libunwind is iterating up the stack, it checks if the address at #20
(backtrace.txt) is accessible. But it's corresponding page is mapped
with the PROT_NONE property which is usually used for guard pages.
Since the memory is mapped correctly, calls to mincore() or msync()
succeed, stating that the address is valid. But what they don't test is
the actual accessibility of the address.

Me and a colleague of mine are not sure if it's even a valid address or
a bug of some other library. I however attached a test case to
reproduce the error (access_mem_test.c) and a possible patch that adds
the necessary accessibility test.
This test uses write() to check if the value at an address can be
written to a pipe. If the address is not accessible the write fails but
doesn't raise a signal. 

I'm certain, that the patch needs one or two more iterations e.g. the
pipe needs to be closed somewhere. Maybe you guys can help me out!


Best regards,
-- 
Dipl.-Inf. Johannes Ziegenbalg
Research Assistant

Technische Universität Dresden, Germany
Center for Information Services and High Performance Computing (ZIH)
Tel.: +49 (351) 463-42417
E-Mail: [email protected]
#0  x86_64_fallback_frame_state (context=0x7ffc501eeb80, 
context=0x7ffc501eeb80, fs=0x7ffc501eec70) at ./md-unwind-support.h:58
#1  uw_frame_state_for (context=context@entry=0x7ffc501eeb80, 
fs=fs@entry=0x7ffc501eec70) at /build/gcc/src/gcc/libgcc/unwind-dw2.c:1249
#2  0x00007fce040086d8 in _Unwind_Backtrace (trace=trace@entry=0x7fce03994180 
<unwind>,
    trace_argument=trace_argument@entry=0x7ffc501eee30) at 
/build/gcc/src/gcc/libgcc/unwind.inc:290
#3  0x00007fce039942a9 in backtrace_full (state=state@entry=0x7fce0763a000, 
skip=skip@entry=0,
    callback=callback@entry=0x7fce038a6ab0 <full_callback>, 
error_callback=error_callback@entry=0x7fce038a6c70 <error_callback>,
    data=data@entry=0x7ffc501eee80) at 
/build/gcc/src/gcc/libbacktrace/backtrace.c:127
#4  0x00007fce038a6de8 in _gfortrani_show_backtrace 
(in_signal_handler=in_signal_handler@entry=true)
    at /build/gcc/src/gcc/libgfortran/runtime/backtrace.c:162
#5  0x00007fce038a601e in _gfortrani_backtrace_handler (signum=11) at 
/build/gcc/src/gcc/libgfortran/runtime/compile_options.c:129
#6  <signal handler called>
#7  access_mem (as=<optimized out>, addr=140522739990564, val=0x7ffc501ef4c0, 
write=0, arg=<optimized out>)
    at ../../src/x86_64/Ginit.c:202
#8  0x00007fce060bcbf6 in is_plt_entry (c=0x7fcdf9db24b8) at 
../../src/x86_64/Gstep.c:43
#9  _ULx86_64_step (cursor=0x7fcdf9db24b8) at ../../src/x86_64/Gstep.c:126
#10 0x00007fce06c5e31f in get_current_stack (unwindData=0x7fcdf9db20d0, 
wrappedRegionIsOnStack=true, wrappedRegionIp=0x7ffc501ef5b8)
    at ../../build-backend/../src/services/unwinding/scorep_unwinding_cpu.c:362
#11 0x00007fce06c5eae5 in scorep_unwinding_cpu_handle_enter 
(location=0x7fcdf99ae308, instrumentedRegionHandle=0, wrappedRegion=0,
    framesToSkip=0, callingContext=0x7ffc501ef6bc, 
unwindDistance=0x7ffc501ef6b4, previousCallingContext=0x7ffc501ef6b8)
    at ../../build-backend/../src/services/unwinding/scorep_unwinding_cpu.c:615
#12 0x00007fce06c5d630 in SCOREP_Unwinding_GetCallingContext 
(location=0x7fcdf99ae308, origin=SCOREP_UNWINDING_ORIGIN_SAMPLE,
    instrumentedRegionHandle=0, wrappedRegion=0, framesToSkip=0, 
currentCallingContext=0x7ffc501ef6bc,
    previousCallingContext=0x7ffc501ef6b8, unwindDistance=0x7ffc501ef6b4)
    at ../../build-backend/../src/services/unwinding/SCOREP_Unwinding.c:234
#13 0x00007fce06c16361 in SCOREP_Sample (interruptGeneratorHandle=10568) at 
../../build-backend/../src/measurement/SCOREP_Events.c:120
#14 0x00007fce06c6333f in perf_signal_handler (signalNumber=27, 
signalInfo=0x7ffc501ef870, contextPtr=0x7ffc501ef740)
    at 
../../build-backend/../src/services/sampling/scorep_sampling_signal_perf.c:251
#15 <signal handler called>
#16 do_lookup_x (undef_name=undef_name@entry=0x7fce01b8867e "fgets", 
new_hash=new_hash@entry=258931614,
    old_hash=old_hash@entry=0x7ffc501efe20, ref=0x7fce01b7e540, 
result=result@entry=0x7ffc501efe30, scope=<optimized out>, i=17,
    version=0x7fce075f9c18, flags=1, skip=<optimized out>, type_class=1, 
undef_map=0x7fce075fd768) at dl-lookup.c:407
#17 0x00007fce0742dde1 in _dl_lookup_symbol_x (undef_name=0x7fce01b8867e 
"fgets", undef_map=0x7fce075fd768,
    ref=ref@entry=0x7ffc501efee8, symbol_scope=0x7fce075fdac0, 
version=0x7fce075f9c18, type_class=type_class@entry=1, flags=1,
    skip_map=0x0) at dl-lookup.c:833
#18 0x00007fce074328f4 in _dl_fixup (l=<optimized out>, reloc_arg=<optimized 
out>) at ../elf/dl-runtime.c:111
#19 0x00007fce07439963 in _dl_runtime_resolve_avx () at 
../sysdeps/x86_64/dl-trampoline.h:112
#20 0x00007fce00000024 in ?? ()
#21 0x000000001ca59090 in ?? ()
#22 0x000000001ca58f40 in ?? ()
#23 0x000000001ca58f40 in ?? ()
#24 0x00007fce01e89ba8 in mca_base_var_file_values () from 
/home/jz/WORK/opt/packages/VampirLive/lib/libopen-pal.so.13
#25 0x00007ffc501f0280 in ?? ()
#26 0x00007fce01bde33e in var_set_initial (var=0x1ca59140) at 
../../../../opal/mca/base/mca_base_var.c:1658
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>

int
main( int argc, char* argv[] )
{
    struct stat    file_stat;
    int            fd;
    void*          file_mmap;
    size_t         page_size;
    size_t         num_pages;
    unsigned char* mincore_vec;

    fd = open( "/bin/bash", O_RDONLY );
    if ( fd < 0 )
    {
        perror( "Couldn't open file\n" );
        exit( 1 );
    }

    fstat( fd, &file_stat );
    file_mmap = mmap( ( void* )0, file_stat.st_size, PROT_NONE, MAP_PRIVATE, fd, 0 );

    page_size   = getpagesize();
    num_pages   = ( file_stat.st_size + page_size - 1 ) / page_size;
    mincore_vec = calloc( 1, num_pages );

    /* test mincore */
    mincore( file_mmap, file_stat.st_size, mincore_vec );
    printf( "call to mincore() returned: %s\n", strerror( errno ) );

    /* test msync */
    msync( file_mmap, file_stat.st_size, MS_ASYNC );
    printf( "call to msync() returned: %s\n", strerror( errno ) );


    /* test write */
    int nullfd[ 2 ];
    pipe( nullfd );
    if ( write( nullfd[ 1 ], file_mmap, 1 ) != -1 )
    {
        // Remove written byte from the pipe buffer.
        char buf[ 1 ];
        read( nullfd[ 0 ], buf, 1 );
    }
    printf( "call to write() returned: %s\n", strerror( errno ) );
    close( nullfd[ 0 ] );
    close( nullfd[ 1 ] );

    /* volatile int this_will_crash = *( int* )file_mmap; */

    return 0;
}
From 1d8e98d431806865368495fd13f8e988a6d512da Mon Sep 17 00:00:00 2001
From: Johannes Ziegenbalg <[email protected]>
Date: Tue, 6 Sep 2016 18:46:28 +0200
Subject: [PATCH] [PATCH] x86_64: fix mincore_validate and msync_validate

The calls to mincore() or msync() are not checking for actual accessibility
this could lead to SIGSEGV if the address from a mapped page with the
PROT_NONE property occurs on the stack.
---
 src/x86_64/Ginit.c | 32 +++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/src/x86_64/Ginit.c b/src/x86_64/Ginit.c
index 7827576..7edc914 100644
--- a/src/x86_64/Ginit.c
+++ b/src/x86_64/Ginit.c
@@ -72,10 +72,27 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
 #define PAGE_SIZE 4096
 #define PAGE_START(a)   ((a) & ~(PAGE_SIZE-1))
 
+static int null_fd[2];
 static int (*mem_validate_func) (void *addr, size_t len);
 static int msync_validate (void *addr, size_t len)
 {
-  return msync (addr, len, MS_ASYNC);
+  if (msync (addr, len, MS_ASYNC) != 0)
+    {
+      return -1;
+    }
+
+  if (write (null_fd[1], addr, 1) == -1)
+    {
+      return -1;
+    }
+  else
+    {
+      /* Remove written byte from the pipe buffer. */
+      char buf[1];
+      read (null_fd[0], buf, 1);
+    }
+
+  return 0;
 }
 
 #ifdef HAVE_MINCORE
@@ -96,6 +113,17 @@ static int mincore_validate (void *addr, size_t len)
       if (!(mvec[i] & 1)) return -1;
     }
 
+  if (write (null_fd[1], addr, 1) == -1)
+    {
+      return -1;
+    }
+  else
+    {
+      /* Remove written byte from the pipe buffer. */
+      char buf[1];
+      read (null_fd[0], buf, 1);
+    }
+
   return 0;
 }
 #endif
@@ -107,6 +135,8 @@ static int mincore_validate (void *addr, size_t len)
 HIDDEN void
 tdep_init_mem_validate (void)
 {
+  while (pipe (null_fd) == -1 ) {}
+
 #ifdef HAVE_MINCORE
   unsigned char present = 1;
   unw_word_t addr = PAGE_START((unw_word_t)&present);
-- 
2.9.3

Attachment: smime.p7s
Description: S/MIME cryptographic signature

_______________________________________________
Libunwind-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/libunwind-devel

Reply via email to