Bug 27502 - $ORIGIN doesn't work when compiled with address sanitizer and dlopen
Summary: $ORIGIN doesn't work when compiled with address sanitizer and dlopen
Status: RESOLVED INVALID
Alias: None
Product: frysk
Classification: Unclassified
Component: general (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Unassigned
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-03-03 04:16 UTC by Navin P
Modified: 2021-03-03 04:34 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Navin P 2021-03-03 04:16:07 UTC
Hi,
  Please find the details below.

/* gcc dlopen.c -ldl -Wl,-rpath,\$ORIGIN -Wl,--enable-new-dtags */
#include<dlfcn.h>
#include<stdio.h>
int main(int argc,char **argv){
	void *validhandle=dlopen(argv[1],RTLD_NOW);
	if(validhandle){
		printf("dlopen succeeded \n");
		dlclose(validhandle);
	} else {
		printf("dlopen failed : %s\n",dlerror());
	}
	return 0;
}
lib.c is an empty file used to create shared object libfoo.so using 
gcc -shared -o libfoo.so lib.c

readelf -d a.out  | grep RUNPATH
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN]

navin@mint-Aspire:~/doctest/origin/plain$ ./a.out libfoo.so
dlopen succeeded 
navin@mint-Aspire:~/doctest/origin/plain$


Now compile with additonal flag -fsanitize=address
gcc -fsanitize=address dlopen.c -ldl -Wl,-rpath,\$ORIGIN -Wl,--enable-new-dtags
gcc -fsanitize=address -shared -o libfoo.so lib.c

navin@mint-Aspire:~/doctest/origin/asan$ readelf -d a.out | grep -i runpath
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN]
navin@mint-Aspire:~/doctest/origin/asan$ ls
a.out  dlopen.c  lib.c  libfoo.so
navin@mint-Aspire:~/doctest/origin/asan$ 


navin@mint-Aspire:~/doctest/origin/asan$ ./a.out libfoo.so 
dlopen failed : libfoo.so: cannot open shared object file: No such file or directory
navin@mint-Aspire:~/doctest/origin/asan$ 

Doesn't work.

Now with patch applied

 0x000000000000001d (RUNPATH)            Library runpath: [/media/navin/c19d52cf-2537-4ced-af7f-5584e3ac3004/build-glibc/scratch:/media/navin/c19d52cf-2537-4ced-af7f-5584e3ac3004/build-glibc/scratch/math:/media/navin/c19d52cf-2537-4ced-af7f-5584e3ac3004/build-glibc/scratch/rt:/media/navin/c19d52cf-2537-4ced-af7f-5584e3ac3004/build-glibc/scratch/dlfcn:/media/navin/c19d52cf-2537-4ced-af7f-5584e3ac3004/build-glibc/scratch/nptl:$ORIGIN]

navin@mint-Aspire:~/doctest/origin/afterfix$ ./a.out libfoo.so 
dlopen succeeded 


patch details

From d2cb9bba3de58af3516eb7099d0452e2ee6365d2 Mon Sep 17 00:00:00 2001
From: Navin P <navinp0304@gmail.com>
Date: Mon, 22 Feb 2021 15:02:00 +0530
Subject: [PATCH] If executables are compiled with asan, it should perform
 additional LM_ID_BASE D_RUNPATH also.

---
 elf/dl-load.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 9e2089cfaa..fe88c8c9ea 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2161,6 +2161,34 @@ _dl_map_object (struct link_map *loader, const char *name,
 			&loader->l_runpath_dirs, &realname, &fb, loader,
 			LA_SER_RUNPATH, &found_other_class);
 
+      /*
+       * This is not transitive dependency.It it just an additional
+       * lookup into executable/dso runpath when loader is different
+       * from LM_ID_BASE loader.
+       * In usual cases like direct call to executable, then main_map
+       * is same as loader and no lookup is performed.
+       * In cases like libasan.so being the parent caller with dl_caller
+       * set to libasan.so which is called by GL(dls_ns)[LIM_ID_BASE],
+       * we need to additionally search the executable/dso link_map
+       * other than loader link_map which is performed above.
+       */
+      struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+
+      /* Look at the RUNPATH information for this binary using main_map.
+       * This lookup is only done when the binary is in namespace 0.
+       * For nested namespaces from dlmopen LM_ID_NEWLM, we don't search
+       * in namespace 0 because it is detached from LM_ID_BASE.
+       * In that case lookup is done in the callers RUNPATH above.
+       */
+
+      if (fd == -1 && (nsid == LM_ID_BASE ) && main_map != NULL
+          && main_map != loader
+	  && cache_rpath (main_map, &main_map->l_runpath_dirs,
+			  DT_RUNPATH, "RUNPATH"))
+	fd = open_path (name, namelen, mode,
+			&main_map->l_runpath_dirs, &realname, &fb, main_map,
+			LA_SER_RUNPATH, &found_other_class);
+
       if (fd == -1)
         {
           realname = _dl_sysdep_open_object (name, namelen, &fd);
-- 
2.25.1
Comment 1 Navin P 2021-03-03 04:34:01 UTC
Wrong product