Bug 1449604

Summary: CC 6.3 libasan.so intercepts dlopen but does not taken the search path from the original module into account
Product: Red Hat Developer Toolset Reporter: Piyush Bhoot <pbhoot>
Component: gccAssignee: Marek Polacek <mpolacek>
Status: CLOSED UPSTREAM QA Contact: Martin Cermak <mcermak>
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: DTS 6.0 RHEL 7CC: codonell, fweimer, jakub, kanderso, law, mcermak, mnewsome
Target Milestone: alpha   
Target Release: 6.1   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2020-06-24 15:09:23 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:

Description Piyush Bhoot 2017-05-10 10:58:50 UTC
Description of problem:

Using gcc6.3 libasan may - under certain circumstances - cause dlopen fail to find the .so library when only the name of the library is used in the call to dlopen (not the relative path).

Version-Release number of selected component (if applicable):
devtoolset-6-gcc-6.3.1-3.1.el7.x86_64
devtoolset-6-libasan-devel-6.3.1-3.1.el7.x86_64


How reproducible:
Alaways

Steps to Reproduce:
1.
#include <dlfcn.h>
#include <iostream>
int main()
{
    void *handle = dlopen("libb.so", RTLD_NOW);
    if (handle == NULL) {
        std::cout << "Failed to load libb.so" << std::endl;
    }
}

#mkdir lib
#g++ --sanitize=address -Wl,-rpath='$ORIGIN/lib' -Wl,--enable-new-dtags -ldl -o main main.cpp
#cd lib; 

Create libb.so: 
int foo()
{
  return 1;
}

g++ -fPIC -shared b.C -o libb.so

Actual results:

strace ./main 2>&1 | grep libb.so
open("/opt/rh/devtoolset-6/root/usr/lib/../lib64/tls/libb.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
:
<snipped>
:
open("/usr/lib64/libb.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
write(1, "Failed to load libb.so\n", 23Failed to load libb.so

Expected results:

strace ./main 2>&1 | grep libb.so

open("/opt/rh/devtoolset-6/root/usr/lib64/tls/libb.so", O_RDONLY) = -1 ENOENT (No such file or directory)

open("/root/cc/lib/libb.so", O_RDONLY)  = 3


Additional info:
Libasan intercepts dlopen call and the libasan's RUNPATH is taken into account, not the binary one. Running it through strace, it failed to load libb.so

Comment 4 Florian Weimer 2017-05-10 11:39:04 UTC
I think the libasan dlopen interceptor could be turned into a tail call, which would address the issue.

#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag)                           \
  if (flags()->strict_init_order) {                                            \
    StopInitOrderChecking();                                                   \
  }

The above probably could just as well happen before the dlopen call.

Comment 6 Jakub Jelinek 2019-01-08 13:16:17 UTC
(In reply to Florian Weimer from comment #4)
> I think the libasan dlopen interceptor could be turned into a tail call,
> which would address the issue.
> 
> #define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag)                        
> \
>   if (flags()->strict_init_order) {                                         
> \
>     StopInitOrderChecking();                                                
> \
>   }
> 
> The above probably could just as well happen before the dlopen call.

I don't see how.  The interceptor does some work before the dlopen call and other work after it:
INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
  void *ctx;
  COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
  if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
  COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag);
  void *res = REAL(dlopen)(filename, flag);
  Symbolizer::GetOrInit()->InvalidateModuleList();
  COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);
  return res;
}