Bug 1163404

Summary: [procps] find_elf_note invalid read if setenv has been called before libproc init
Product: Red Hat Enterprise Linux 6 Reporter: Peter Edwards <thatsafunnyname>
Component: procpsAssignee: Jaromír Cápík <jcapik>
Status: CLOSED ERRATA QA Contact: Branislav Náter <bnater>
Severity: medium Docs Contact:
Priority: medium    
Version: 6.5CC: albert, bnater, jhouska, kdudka, ovasik, pandrade, sgaikwad, thatsafunnyname
Target Milestone: rc   
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: procps-3.2.8-31.el6 Doc Type: Bug Fix
Doc Text:
Previously, behavior of the libproc library was unreliable when it was loaded with the dlopen() call after the environment was changed with the setenv() call. As a consequence, an invalid memory access error could occur in libproc. With this update, the find_elf_note() function obtains the auxiliary vector values using a different and safer method based on parsing the /proc/self/auxv file, and the described problem no longer occurs.
Story Points: ---
Clone Of:
: 1287752 (view as bug list) Environment:
Last Closed: 2015-07-22 07:35:36 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On: 1287752    
Bug Blocks:    

Description Peter Edwards 2014-11-12 16:11:21 UTC
Hello and thank you for procps(-ng),

Description of problem:

From:  http://www.freelists.org/post/procps/find-elf-note-invalid-read-if-setenv-has-been-called-before-libproc-init
 
When setenv is called before libproc is
initialized we see invalid reads reported by valgrind.
 
Version-Release number of selected component (if applicable):

 procps-3.2.8-25 on RHEL6
 procps-ng-3.3.9-6 on RHEL7

How reproducible:

Easy with the steps below:

Steps to Reproduce:

mkdir /tmp/procps
cd /tmp/procps

> cat main.c
#include<stdio.h>
#include<stdlib.h>
#include<dlfcn.h>

void  *FunctionLib;
int   (*Function)();
const char *dlError;

int main(){

  printf("main()\n");

  setenv("TEST_DUMMY_ENV_MUST_NOT_EXIST","X",1);

  FunctionLib = dlopen("/tmp/libmyproc.so",RTLD_LAZY);
  dlError = dlerror();
  if( dlError ) {
    printf("dlopen return-%s- \n", dlError);
    exit(1);
  }

  Function    = dlsym( FunctionLib, "print_vsize");
  dlError = dlerror();
  if( dlError ) {
    printf("dlsym return-%s- \n", dlError);
    exit(1);
  }

  int rc = (*Function)( );
  dlError = dlerror();
  if( dlError ) {
    printf("function dlerror-%s-\n",dlError);
  }

  if(dlclose(FunctionLib)){
    dlError = dlerror();
    if( dlError ) {
      printf("dlclose return-%s-\n",dlError);
    }
  }

}

> cat myproc.h
void print_vsize();

> cat myproc.c
#include "myproc.h"
#include <stdio.h>
#include <proc/readproc.h>

void print_vsize() {
  printf("print_vsize()\n");
  struct proc_t usage;
  look_up_our_self(&usage);
  printf("vsize: %lu\n", usage.vsize);
}

rm -f libmyproc.so /tmp/libmyproc.so main.o ; gcc -fPIC -c myproc.c -lproc
&& gcc -fPIC -c main.c -ldl &&  gcc -shared myproc.o -o libmyproc.so -lproc
&& gcc main.o -Wl -o main -ldl && cp libmyproc.so /tmp/ &&
/usr/bin/valgrind --leak-check=yes ./main
==2849== Memcheck, a memory error detector
==2849== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2849== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2849== Command: ./main
==2849==
main()
==2849== Invalid read of size 8
==2849==    at 0x3CCAC09D8C: ??? (in /lib64/libproc-3.2.8.so)
==2849==    by 0x3CCAC0AD55: ??? (in /lib64/libproc-3.2.8.so)
==2849==    by 0x3CCAC03712: ??? (in /lib64/libproc-3.2.8.so)
==2849==  Address 0x4c22170 is 0 bytes after a block of size 304 alloc'd
==2849==    at 0x4A069EE: malloc (vg_replace_malloc.c:270)
==2849==    by 0x4A06B62: realloc (vg_replace_malloc.c:662)
==2849==    by 0x3CCA83540C: __add_to_environ (in /lib64/libc-2.12.so)
==2849==    by 0x4A0A5D9: setenv (mc_replace_strmem.c:1755)
==2849==    by 0x4007FF: main (in /tmp/procps/main)
==2849==
==2849== Invalid read of size 8
==2849==    at 0x3CCAC09E34: ??? (in /lib64/libproc-3.2.8.so)
==2849==    by 0x3CCAC0AD55: ??? (in /lib64/libproc-3.2.8.so)
==2849==    by 0x3CCAC03712: ??? (in /lib64/libproc-3.2.8.so)
==2849==  Address 0x4c22170 is 0 bytes after a block of size 304 alloc'd
==2849==    at 0x4A069EE: malloc (vg_replace_malloc.c:270)
==2849==    by 0x4A06B62: realloc (vg_replace_malloc.c:662)
==2849==    by 0x3CCA83540C: __add_to_environ (in /lib64/libc-2.12.so)
==2849==    by 0x4A0A5D9: setenv (mc_replace_strmem.c:1755)
==2849==    by 0x4007FF: main (in /tmp/procps/main)
==2849==
2.4+ kernel w/o ELF notes? -- report this
print_vsize()
vsize: 126840832
==2849==
==2849== HEAP SUMMARY:
==2849==     in use at exit: 0 bytes in 0 blocks
==2849==   total heap usage: 14 allocs, 14 frees, 3,185 bytes allocated
==2849==
==2849== All heap blocks were freed -- no leaks are possible
==2849==
==2849== For counts of detected and suppressed errors, rerun with: -v
==2849== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 6 from 6)

This was using kernel 2.6.32-431.29.2.el6.x86_64 on RHEL6.5 with
procps-3.2.8-25.el6 and gcc-4.4.7-4.el6.
 
I see the same on RHEL7, kernel 3.10.0-123.el7.x86_64 with procps-ng-devel-3.3.9-6.el7.x86_64 and procps-ng-3.3.9-6.el7.x86_64.
 
rm -f libmyproc.so /tmp/libmyproc.so main.o ; gcc -fPIC -c myproc.c -lprocps && gcc -fPIC -c main.c -ldl &&  gcc -shared myproc.o -o libmyproc.so -lprocps && gcc main.o -W -o main -ldl && cp libmyproc.so /tmp/ && /usr/bin/valgrind --leak-check=yes ./main
==8902== Memcheck, a memory error detector
==8902== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==8902== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==8902== Command: ./main
==8902==
main()
==8902== Invalid read of size 8
==8902==    at 0x59FD46B: ??? (in /usr/lib64/libprocps.so.3.0.0)
==8902==    by 0x400F502: _dl_init (in /usr/lib64/ld-2.17.so)
==8902==    by 0x4013B43: dl_open_worker (in /usr/lib64/ld-2.17.so)
==8902==    by 0x400F313: _dl_catch_error (in /usr/lib64/ld-2.17.so)
==8902==    by 0x401325A: _dl_open (in /usr/lib64/ld-2.17.so)
==8902==    by 0x4E3302A: dlopen_doit (in /usr/lib64/libdl-2.17.so)
==8902==    by 0x400F313: _dl_catch_error (in /usr/lib64/ld-2.17.so)
==8902==    by 0x4E3362C: _dlerror_run (in /usr/lib64/libdl-2.17.so)
==8902==    by 0x4E330C0: dlopen@@GLIBC_2.2.5 (in /usr/lib64/libdl-2.17.so)
==8902==    by 0x40082C: main (in /tmp/procps/main)
==8902==  Address 0x53f7170 is 0 bytes after a block of size 304 alloc'd
==8902==    at 0x4C2845D: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8902==    by 0x4C2A32B: realloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8902==    by 0x506E6CF: __add_to_environ (in /usr/lib64/libc-2.17.so)
==8902==    by 0x4C2E7FF: setenv (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8902==    by 0x40081B: main (in /tmp/procps/main)
==8902==
==8902== Invalid read of size 8
==8902==    at 0x59FD4F3: ??? (in /usr/lib64/libprocps.so.3.0.0)
==8902==    by 0x400F502: _dl_init (in /usr/lib64/ld-2.17.so)
==8902==    by 0x4013B43: dl_open_worker (in /usr/lib64/ld-2.17.so)
==8902==    by 0x400F313: _dl_catch_error (in /usr/lib64/ld-2.17.so)
==8902==    by 0x401325A: _dl_open (in /usr/lib64/ld-2.17.so)
==8902==    by 0x4E3302A: dlopen_doit (in /usr/lib64/libdl-2.17.so)
==8902==    by 0x400F313: _dl_catch_error (in /usr/lib64/ld-2.17.so)
==8902==    by 0x4E3362C: _dlerror_run (in /usr/lib64/libdl-2.17.so)
==8902==    by 0x4E330C0: dlopen@@GLIBC_2.2.5 (in /usr/lib64/libdl-2.17.so)
==8902==    by 0x40082C: main (in /tmp/procps/main)
==8902==  Address 0x53f7170 is 0 bytes after a block of size 304 alloc'd
==8902==    at 0x4C2845D: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8902==    by 0x4C2A32B: realloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8902==    by 0x506E6CF: __add_to_environ (in /usr/lib64/libc-2.17.so)
==8902==    by 0x4C2E7FF: setenv (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8902==    by 0x40081B: main (in /tmp/procps/main)
==8902==
2.4+ kernel w/o ELF notes? -- report this
print_vsize()
vsize: 108171264
==8902==
==8902== HEAP SUMMARY:
==8902==     in use at exit: 0 bytes in 0 blocks
==8902==   total heap usage: 14 allocs, 14 frees, 4,257 bytes allocated
==8902==
==8902== All heap blocks were freed -- no leaks are possible
==8902==
==8902== For counts of detected and suppressed errors, rerun with: -v
==8902== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 3 from 3)

Actual results:

See above for valgrind reports of invalid reads.

Expected results:

No invalid reads reported from valgrind.

Additional info:
 
find_elf_note in proc/sysinfo.c should use getauxval and not attempt to find notes based on environ.

A valgrind test at work reported invalid reads from libproc (version 3.2.8
but I think the same code exists in the latest procps-ng).
A colleageue of mine found that when setenv is called before libproc is
initialized we will see the behaviour (invalid read), init_libproc in
proc/sysinfo.c calling find_elf_note which finds notes for ELF executables
based of environ, which at this point has changed.  The suggestion from my
colleageue was to use getauxval.
In the reproducer above, a main executable must call setenv for a logical
that does not exist before libproc is initialized.

Comment 2 Peter Edwards 2014-11-12 18:36:14 UTC
Also see:
 
  https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=460331
 
I am wondering if sysconf(_SC_CLK_TCK) can now be used to obtain Hertz.
The old comment towards the top of proc/sysinfo.c includes:
 
"Note that sysconf() is still
unreliable, because it doesn't return an error code when it is
used with a kernel that doesn't support the ELF note. On some other
architectures there may be a system call or sysctl() that will work."
 
It seems a popular approach these days - https://searchcode.com/?q=_SC_CLK_TCK
 
It was this change that added the find_elf_note function:
 
http://procps.cvs.sourceforge.net/viewvc/procps/procps/proc/sysinfo.c?r1=1.7&r2=1.8
 
getauxval was (only) added in glibc 2.16 [ http://lwn.net/Articles/519085/ ]

Comment 4 Jaromír Cápík 2015-03-02 19:23:19 UTC
Hello Peter.

Prior glibc 2.19 the getauxval didn't support the errno==ENOENT checking and we need that check in order to evaluate the AT_SECURE type as it usually contains a zero value. Therefore the getauxval function is currently unsuitable as a replacement and the only fix we can do is to document the limitation. If someone wants to use dlopen for accessing the libproc functions, he/she simply needs to do the init prior manipulating with the environment variables. There seems to be no easy way around this.
If you have any idea how to resolve this reliably, then tell me, please.

Regards,
Jaromir.

Comment 5 Jaromír Cápík 2015-03-03 12:46:05 UTC
It seems the auxiliary vector values can be obtained from /proc/self/auxv.

Comment 7 Peter Edwards 2015-03-05 22:36:27 UTC
Thank you.

Comment 10 errata-xmlrpc 2015-07-22 07:35:36 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://rhn.redhat.com/errata/RHBA-2015-1407.html