2014-02-06 15:34:21 +01:00
/*
* Copyright ( C ) 2014 The Android Open Source Project
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
# include <gtest/gtest.h>
# include <dlfcn.h>
2014-11-04 20:08:05 +01:00
# include <elf.h>
2014-02-27 14:18:00 +01:00
# include <errno.h>
# include <fcntl.h>
2014-11-04 20:08:05 +01:00
# include <inttypes.h>
2014-02-27 14:18:00 +01:00
# include <stdio.h>
# include <string.h>
# include <unistd.h>
2014-02-06 15:34:21 +01:00
# include <android/dlext.h>
# include <sys/mman.h>
2014-05-02 15:57:42 +02:00
# include <sys/types.h>
2014-02-27 14:18:00 +01:00
# include <sys/wait.h>
2014-02-06 15:34:21 +01:00
2014-05-02 15:57:42 +02:00
# include <pagemap/pagemap.h>
2014-12-08 05:43:37 +01:00
# include "TemporaryFile.h"
2015-11-23 20:26:35 +01:00
# include "utils.h"
2014-02-06 15:34:21 +01:00
# define ASSERT_DL_NOTNULL(ptr) \
2014-10-04 02:52:44 +02:00
ASSERT_TRUE ( ptr ! = nullptr ) < < " dlerror: " < < dlerror ( )
2014-02-06 15:34:21 +01:00
# define ASSERT_DL_ZERO(i) \
ASSERT_EQ ( 0 , i ) < < " dlerror: " < < dlerror ( )
2014-02-27 14:18:00 +01:00
# define ASSERT_NOERROR(i) \
ASSERT_NE ( - 1 , i ) < < " errno: " < < strerror ( errno )
2014-11-04 20:08:05 +01:00
# define ASSERT_SUBSTR(needle, haystack) \
ASSERT_PRED_FORMAT2 ( : : testing : : IsSubstring , needle , haystack )
2014-02-06 15:34:21 +01:00
typedef int ( * fn ) ( void ) ;
# define LIBNAME "libdlext_test.so"
2014-04-30 16:48:40 +02:00
# define LIBNAME_NORELRO "libdlext_test_norelro.so"
2014-02-06 15:34:21 +01:00
# define LIBSIZE 1024*1024 // how much address space to reserve for it
2014-07-01 23:10:16 +02:00
# if defined(__LP64__)
2015-10-30 01:01:24 +01:00
# define NATIVE_TESTS_PATH " / nativetest64"
2014-07-01 23:10:16 +02:00
# else
2015-10-30 01:01:24 +01:00
# define NATIVE_TESTS_PATH " / nativetest"
2014-07-01 23:10:16 +02:00
# endif
2014-02-06 15:34:21 +01:00
2015-10-30 01:01:24 +01:00
# define LIBPATH NATIVE_TESTS_PATH " / libdlext_test_fd / libdlext_test_fd.so"
# define LIBZIPPATH NATIVE_TESTS_PATH " / libdlext_test_zip / libdlext_test_zip_zipaligned.zip"
# define LIBZIPPATH_WITH_RUNPATH NATIVE_TESTS_PATH " / libdlext_test_runpath_zip / libdlext_test_runpath_zip_zipaligned.zip"
2014-10-04 02:52:44 +02:00
2015-09-29 01:38:31 +02:00
# define LIBZIP_OFFSET PAGE_SIZE
2014-10-04 02:52:44 +02:00
2014-02-06 15:34:21 +01:00
class DlExtTest : public : : testing : : Test {
protected :
virtual void SetUp ( ) {
2014-10-04 02:52:44 +02:00
handle_ = nullptr ;
2014-02-06 15:34:21 +01:00
// verify that we don't have the library loaded already
2014-10-04 02:52:44 +02:00
void * h = dlopen ( LIBNAME , RTLD_NOW | RTLD_NOLOAD ) ;
ASSERT_TRUE ( h = = nullptr ) ;
h = dlopen ( LIBNAME_NORELRO , RTLD_NOW | RTLD_NOLOAD ) ;
ASSERT_TRUE ( h = = nullptr ) ;
2014-02-06 15:34:21 +01:00
// call dlerror() to swallow the error, and check it was the one we wanted
2014-10-04 02:52:44 +02:00
ASSERT_STREQ ( " dlopen failed: library \" " LIBNAME_NORELRO " \" wasn't loaded and RTLD_NOLOAD prevented it " , dlerror ( ) ) ;
2014-02-06 15:34:21 +01:00
}
virtual void TearDown ( ) {
2014-10-04 02:52:44 +02:00
if ( handle_ ! = nullptr ) {
2014-02-06 15:34:21 +01:00
ASSERT_DL_ZERO ( dlclose ( handle_ ) ) ;
}
}
void * handle_ ;
} ;
TEST_F ( DlExtTest , ExtInfoNull ) {
2014-10-04 02:52:44 +02:00
handle_ = android_dlopen_ext ( LIBNAME , RTLD_NOW , nullptr ) ;
2014-02-06 15:34:21 +01:00
ASSERT_DL_NOTNULL ( handle_ ) ;
fn f = reinterpret_cast < fn > ( dlsym ( handle_ , " getRandomNumber " ) ) ;
ASSERT_DL_NOTNULL ( f ) ;
EXPECT_EQ ( 4 , f ( ) ) ;
}
TEST_F ( DlExtTest , ExtInfoNoFlags ) {
android_dlextinfo extinfo ;
extinfo . flags = 0 ;
handle_ = android_dlopen_ext ( LIBNAME , RTLD_NOW , & extinfo ) ;
ASSERT_DL_NOTNULL ( handle_ ) ;
fn f = reinterpret_cast < fn > ( dlsym ( handle_ , " getRandomNumber " ) ) ;
ASSERT_DL_NOTNULL ( f ) ;
EXPECT_EQ ( 4 , f ( ) ) ;
}
2014-07-01 23:10:16 +02:00
TEST_F ( DlExtTest , ExtInfoUseFd ) {
2015-03-19 06:50:01 +01:00
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + LIBPATH ;
2014-07-01 23:10:16 +02:00
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_USE_LIBRARY_FD ;
2015-03-19 06:50:01 +01:00
extinfo . library_fd = TEMP_FAILURE_RETRY ( open ( lib_path . c_str ( ) , O_RDONLY | O_CLOEXEC ) ) ;
2014-07-01 23:10:16 +02:00
ASSERT_TRUE ( extinfo . library_fd ! = - 1 ) ;
2015-03-19 06:50:01 +01:00
handle_ = android_dlopen_ext ( lib_path . c_str ( ) , RTLD_NOW , & extinfo ) ;
2014-07-01 23:10:16 +02:00
ASSERT_DL_NOTNULL ( handle_ ) ;
fn f = reinterpret_cast < fn > ( dlsym ( handle_ , " getRandomNumber " ) ) ;
ASSERT_DL_NOTNULL ( f ) ;
EXPECT_EQ ( 4 , f ( ) ) ;
2015-09-03 01:32:02 +02:00
uint32_t * taxicab_number = reinterpret_cast < uint32_t * > ( dlsym ( handle_ , " dlopen_testlib_taxicab_number " ) ) ;
ASSERT_DL_NOTNULL ( taxicab_number ) ;
EXPECT_EQ ( 1729U , * taxicab_number ) ;
2014-07-01 23:10:16 +02:00
}
2014-10-04 02:52:44 +02:00
TEST_F ( DlExtTest , ExtInfoUseFdWithOffset ) {
2015-03-19 06:50:01 +01:00
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + LIBZIPPATH ;
2014-10-04 02:52:44 +02:00
android_dlextinfo extinfo ;
2014-10-21 21:09:18 +02:00
extinfo . flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET ;
2015-03-19 06:50:01 +01:00
extinfo . library_fd = TEMP_FAILURE_RETRY ( open ( lib_path . c_str ( ) , O_RDONLY | O_CLOEXEC ) ) ;
2014-10-21 21:09:18 +02:00
extinfo . library_fd_offset = LIBZIP_OFFSET ;
2014-10-04 02:52:44 +02:00
2015-03-19 06:50:01 +01:00
handle_ = android_dlopen_ext ( lib_path . c_str ( ) , RTLD_NOW , & extinfo ) ;
2014-10-04 02:52:44 +02:00
ASSERT_DL_NOTNULL ( handle_ ) ;
2015-09-29 01:38:31 +02:00
uint32_t * taxicab_number = reinterpret_cast < uint32_t * > ( dlsym ( handle_ , " dlopen_testlib_taxicab_number " ) ) ;
ASSERT_DL_NOTNULL ( taxicab_number ) ;
EXPECT_EQ ( 1729U , * taxicab_number ) ;
2014-10-04 02:52:44 +02:00
}
TEST_F ( DlExtTest , ExtInfoUseFdWithInvalidOffset ) {
2015-03-19 06:50:01 +01:00
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + LIBZIPPATH ;
2015-04-08 20:53:08 +02:00
// lib_path is relative when $ANDROID_DATA is relative
char lib_realpath_buf [ PATH_MAX ] ;
ASSERT_TRUE ( realpath ( lib_path . c_str ( ) , lib_realpath_buf ) = = lib_realpath_buf ) ;
const std : : string lib_realpath = std : : string ( lib_realpath_buf ) ;
2014-10-04 02:52:44 +02:00
android_dlextinfo extinfo ;
2014-10-21 21:09:18 +02:00
extinfo . flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET ;
2015-03-19 06:50:01 +01:00
extinfo . library_fd = TEMP_FAILURE_RETRY ( open ( lib_path . c_str ( ) , O_RDONLY | O_CLOEXEC ) ) ;
2014-10-21 21:09:18 +02:00
extinfo . library_fd_offset = 17 ;
2014-10-04 02:52:44 +02:00
handle_ = android_dlopen_ext ( " libname_placeholder " , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle_ = = nullptr ) ;
2014-10-21 21:09:18 +02:00
ASSERT_STREQ ( " dlopen failed: file offset for the library \" libname_placeholder \" is not page-aligned: 17 " , dlerror ( ) ) ;
2014-11-04 20:08:05 +01:00
// Test an address above 2^44, for http://b/18178121 .
extinfo . library_fd_offset = ( 5LL < < 48 ) + PAGE_SIZE ;
handle_ = android_dlopen_ext ( " libname_placeholder " , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle_ = = nullptr ) ;
ASSERT_SUBSTR ( " dlopen failed: file offset for the library \" libname_placeholder \" >= file size " , dlerror ( ) ) ;
extinfo . library_fd_offset = 0LL - PAGE_SIZE ;
2014-10-21 21:09:18 +02:00
handle_ = android_dlopen_ext ( " libname_placeholder " , RTLD_NOW , & extinfo ) ;
2014-11-04 20:08:05 +01:00
ASSERT_TRUE ( handle_ = = nullptr ) ;
ASSERT_SUBSTR ( " dlopen failed: file offset for the library \" libname_placeholder \" is negative " , dlerror ( ) ) ;
2014-10-21 21:09:18 +02:00
2015-09-29 01:38:31 +02:00
extinfo . library_fd_offset = 0 ;
2015-03-31 20:14:03 +02:00
handle_ = android_dlopen_ext ( " libname_ignored " , RTLD_NOW , & extinfo ) ;
2014-10-21 21:09:18 +02:00
ASSERT_TRUE ( handle_ = = nullptr ) ;
2015-04-08 20:53:08 +02:00
ASSERT_EQ ( " dlopen failed: \" " + lib_realpath + " \" has bad ELF magic " , dlerror ( ) ) ;
2014-10-21 21:09:18 +02:00
2015-11-06 19:44:37 +01:00
// Check if dlsym works after unsuccessful dlopen().
// Supply non-exiting one to make linker visit every soinfo.
void * sym = dlsym ( RTLD_DEFAULT , " this_symbol_does_not_exist___ " ) ;
ASSERT_TRUE ( sym = = nullptr ) ;
2014-10-21 21:09:18 +02:00
close ( extinfo . library_fd ) ;
2014-10-04 02:52:44 +02:00
}
TEST_F ( DlExtTest , ExtInfoUseOffsetWihtoutFd ) {
android_dlextinfo extinfo ;
2014-10-21 21:09:18 +02:00
extinfo . flags = ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET ;
extinfo . library_fd_offset = LIBZIP_OFFSET ;
2014-10-04 02:52:44 +02:00
handle_ = android_dlopen_ext ( " /some/lib/that/does_not_exist " , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle_ = = nullptr ) ;
2014-10-21 21:09:18 +02:00
ASSERT_STREQ ( " dlopen failed: invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without ANDROID_DLEXT_USE_LIBRARY_FD): 0x20 " , dlerror ( ) ) ;
2014-10-04 02:52:44 +02:00
}
2015-04-03 01:03:56 +02:00
TEST ( dlext , android_dlopen_ext_force_load_smoke ) {
// 1. Open actual file
void * handle = dlopen ( " libdlext_test.so " , RTLD_NOW ) ;
ASSERT_DL_NOTNULL ( handle ) ;
// 2. Open link with force_load flag set
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_FORCE_LOAD ;
void * handle2 = android_dlopen_ext ( " libdlext_test_v2.so " , RTLD_NOW , & extinfo ) ;
ASSERT_DL_NOTNULL ( handle2 ) ;
ASSERT_TRUE ( handle ! = handle2 ) ;
dlclose ( handle2 ) ;
dlclose ( handle ) ;
}
TEST ( dlext , android_dlopen_ext_force_load_soname_exception ) {
// Check if soname lookup still returns already loaded library
// when ANDROID_DLEXT_FORCE_LOAD flag is specified.
void * handle = dlopen ( " libdlext_test_v2.so " , RTLD_NOW ) ;
ASSERT_DL_NOTNULL ( handle ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_FORCE_LOAD ;
// Note that 'libdlext_test.so' is dt_soname for libdlext_test_v2.so
void * handle2 = android_dlopen_ext ( " libdlext_test.so " , RTLD_NOW , & extinfo ) ;
ASSERT_DL_NOTNULL ( handle2 ) ;
ASSERT_TRUE ( handle = = handle2 ) ;
dlclose ( handle2 ) ;
dlclose ( handle ) ;
}
2015-03-19 06:50:01 +01:00
TEST ( dlfcn , dlopen_from_zip_absolute_path ) {
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + LIBZIPPATH ;
2015-09-29 01:38:31 +02:00
void * handle = dlopen ( ( lib_path + " !/libdir/libatest_simple_zip.so " ) . c_str ( ) , RTLD_NOW ) ;
2015-03-19 06:50:01 +01:00
ASSERT_TRUE ( handle ! = nullptr ) < < dlerror ( ) ;
2015-09-29 01:38:31 +02:00
uint32_t * taxicab_number = reinterpret_cast < uint32_t * > ( dlsym ( handle , " dlopen_testlib_taxicab_number " ) ) ;
ASSERT_DL_NOTNULL ( taxicab_number ) ;
EXPECT_EQ ( 1729U , * taxicab_number ) ;
2015-03-19 06:50:01 +01:00
dlclose ( handle ) ;
}
2015-10-02 03:41:57 +02:00
TEST ( dlfcn , dlopen_from_zip_with_dt_runpath ) {
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + LIBZIPPATH_WITH_RUNPATH ;
void * handle = dlopen ( ( lib_path + " !/libdir/libtest_dt_runpath_d_zip.so " ) . c_str ( ) , RTLD_NOW ) ;
ASSERT_TRUE ( handle ! = nullptr ) < < dlerror ( ) ;
typedef void * ( * dlopen_b_fn ) ( ) ;
dlopen_b_fn fn = ( dlopen_b_fn ) dlsym ( handle , " dlopen_b " ) ;
ASSERT_TRUE ( fn ! = nullptr ) < < dlerror ( ) ;
void * p = fn ( ) ;
ASSERT_TRUE ( p ! = nullptr ) < < dlerror ( ) ;
dlclose ( p ) ;
dlclose ( handle ) ;
}
2015-03-19 06:50:01 +01:00
TEST ( dlfcn , dlopen_from_zip_ld_library_path ) {
2015-06-09 22:46:51 +02:00
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + LIBZIPPATH + " !/libdir " ;
2015-03-19 06:50:01 +01:00
typedef void ( * fn_t ) ( const char * ) ;
fn_t android_update_LD_LIBRARY_PATH =
reinterpret_cast < fn_t > ( dlsym ( RTLD_DEFAULT , " android_update_LD_LIBRARY_PATH " ) ) ;
ASSERT_TRUE ( android_update_LD_LIBRARY_PATH ! = nullptr ) < < dlerror ( ) ;
2015-09-29 01:38:31 +02:00
void * handle = dlopen ( " libdlext_test_zip.so " , RTLD_NOW ) ;
2015-03-19 06:50:01 +01:00
ASSERT_TRUE ( handle = = nullptr ) ;
android_update_LD_LIBRARY_PATH ( lib_path . c_str ( ) ) ;
2015-09-29 01:38:31 +02:00
handle = dlopen ( " libdlext_test_zip.so " , RTLD_NOW ) ;
2015-03-19 06:50:01 +01:00
ASSERT_TRUE ( handle ! = nullptr ) < < dlerror ( ) ;
int ( * fn ) ( void ) ;
fn = reinterpret_cast < int ( * ) ( void ) > ( dlsym ( handle , " getRandomNumber " ) ) ;
ASSERT_TRUE ( fn ! = nullptr ) ;
EXPECT_EQ ( 4 , fn ( ) ) ;
2015-09-29 01:38:31 +02:00
uint32_t * taxicab_number = reinterpret_cast < uint32_t * > ( dlsym ( handle , " dlopen_testlib_taxicab_number " ) ) ;
ASSERT_DL_NOTNULL ( taxicab_number ) ;
EXPECT_EQ ( 1729U , * taxicab_number ) ;
2015-03-19 06:50:01 +01:00
dlclose ( handle ) ;
}
2014-02-06 15:34:21 +01:00
TEST_F ( DlExtTest , Reserved ) {
2014-10-04 02:52:44 +02:00
void * start = mmap ( nullptr , LIBSIZE , PROT_NONE , MAP_PRIVATE | MAP_ANONYMOUS ,
2014-02-06 15:34:21 +01:00
- 1 , 0 ) ;
ASSERT_TRUE ( start ! = MAP_FAILED ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_RESERVED_ADDRESS ;
extinfo . reserved_addr = start ;
extinfo . reserved_size = LIBSIZE ;
handle_ = android_dlopen_ext ( LIBNAME , RTLD_NOW , & extinfo ) ;
ASSERT_DL_NOTNULL ( handle_ ) ;
fn f = reinterpret_cast < fn > ( dlsym ( handle_ , " getRandomNumber " ) ) ;
ASSERT_DL_NOTNULL ( f ) ;
2014-08-27 22:45:37 +02:00
EXPECT_GE ( reinterpret_cast < void * > ( f ) , start ) ;
2014-02-06 15:34:21 +01:00
EXPECT_LT ( reinterpret_cast < void * > ( f ) ,
reinterpret_cast < char * > ( start ) + LIBSIZE ) ;
EXPECT_EQ ( 4 , f ( ) ) ;
}
TEST_F ( DlExtTest , ReservedTooSmall ) {
2014-10-04 02:52:44 +02:00
void * start = mmap ( nullptr , PAGE_SIZE , PROT_NONE , MAP_PRIVATE | MAP_ANONYMOUS ,
2014-02-06 15:34:21 +01:00
- 1 , 0 ) ;
ASSERT_TRUE ( start ! = MAP_FAILED ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_RESERVED_ADDRESS ;
extinfo . reserved_addr = start ;
extinfo . reserved_size = PAGE_SIZE ;
handle_ = android_dlopen_ext ( LIBNAME , RTLD_NOW , & extinfo ) ;
2014-10-04 02:52:44 +02:00
EXPECT_EQ ( nullptr , handle_ ) ;
2014-02-06 15:34:21 +01:00
}
TEST_F ( DlExtTest , ReservedHint ) {
2014-10-04 02:52:44 +02:00
void * start = mmap ( nullptr , LIBSIZE , PROT_NONE , MAP_PRIVATE | MAP_ANONYMOUS ,
2014-02-06 15:34:21 +01:00
- 1 , 0 ) ;
ASSERT_TRUE ( start ! = MAP_FAILED ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT ;
extinfo . reserved_addr = start ;
extinfo . reserved_size = LIBSIZE ;
handle_ = android_dlopen_ext ( LIBNAME , RTLD_NOW , & extinfo ) ;
ASSERT_DL_NOTNULL ( handle_ ) ;
fn f = reinterpret_cast < fn > ( dlsym ( handle_ , " getRandomNumber " ) ) ;
ASSERT_DL_NOTNULL ( f ) ;
2014-08-27 22:45:37 +02:00
EXPECT_GE ( reinterpret_cast < void * > ( f ) , start ) ;
2014-02-06 15:34:21 +01:00
EXPECT_LT ( reinterpret_cast < void * > ( f ) ,
reinterpret_cast < char * > ( start ) + LIBSIZE ) ;
EXPECT_EQ ( 4 , f ( ) ) ;
}
TEST_F ( DlExtTest , ReservedHintTooSmall ) {
2014-10-04 02:52:44 +02:00
void * start = mmap ( nullptr , PAGE_SIZE , PROT_NONE , MAP_PRIVATE | MAP_ANONYMOUS ,
2014-02-06 15:34:21 +01:00
- 1 , 0 ) ;
ASSERT_TRUE ( start ! = MAP_FAILED ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT ;
extinfo . reserved_addr = start ;
extinfo . reserved_size = PAGE_SIZE ;
handle_ = android_dlopen_ext ( LIBNAME , RTLD_NOW , & extinfo ) ;
ASSERT_DL_NOTNULL ( handle_ ) ;
fn f = reinterpret_cast < fn > ( dlsym ( handle_ , " getRandomNumber " ) ) ;
ASSERT_DL_NOTNULL ( f ) ;
2014-08-27 22:45:37 +02:00
EXPECT_TRUE ( reinterpret_cast < void * > ( f ) < start | |
( reinterpret_cast < void * > ( f ) > =
reinterpret_cast < char * > ( start ) + PAGE_SIZE ) ) ;
2014-02-06 15:34:21 +01:00
EXPECT_EQ ( 4 , f ( ) ) ;
}
2014-02-27 14:18:00 +01:00
2015-10-08 01:34:20 +02:00
TEST_F ( DlExtTest , LoadAtFixedAddress ) {
void * start = mmap ( nullptr , LIBSIZE , PROT_NONE , MAP_PRIVATE | MAP_ANONYMOUS ,
- 1 , 0 ) ;
ASSERT_TRUE ( start ! = MAP_FAILED ) ;
munmap ( start , LIBSIZE ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS ;
extinfo . reserved_addr = start ;
handle_ = android_dlopen_ext ( LIBNAME , RTLD_NOW , & extinfo ) ;
ASSERT_DL_NOTNULL ( handle_ ) ;
fn f = reinterpret_cast < fn > ( dlsym ( handle_ , " getRandomNumber " ) ) ;
ASSERT_DL_NOTNULL ( f ) ;
EXPECT_GE ( reinterpret_cast < void * > ( f ) , start ) ;
EXPECT_LT ( reinterpret_cast < void * > ( f ) , reinterpret_cast < char * > ( start ) + LIBSIZE ) ;
EXPECT_EQ ( 4 , f ( ) ) ;
}
TEST_F ( DlExtTest , LoadAtFixedAddressTooSmall ) {
void * start = mmap ( nullptr , LIBSIZE + PAGE_SIZE , PROT_NONE ,
MAP_PRIVATE | MAP_ANONYMOUS , - 1 , 0 ) ;
ASSERT_TRUE ( start ! = MAP_FAILED ) ;
munmap ( start , LIBSIZE + PAGE_SIZE ) ;
void * new_addr = mmap ( reinterpret_cast < uint8_t * > ( start ) + PAGE_SIZE , LIBSIZE , PROT_NONE ,
MAP_PRIVATE | MAP_ANONYMOUS , - 1 , 0 ) ;
ASSERT_TRUE ( new_addr ! = MAP_FAILED ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS ;
extinfo . reserved_addr = start ;
handle_ = android_dlopen_ext ( LIBNAME , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle_ = = nullptr ) ;
}
2014-04-30 16:48:40 +02:00
class DlExtRelroSharingTest : public DlExtTest {
protected :
virtual void SetUp ( ) {
DlExtTest : : SetUp ( ) ;
2014-10-04 02:52:44 +02:00
void * start = mmap ( nullptr , LIBSIZE , PROT_NONE , MAP_PRIVATE | MAP_ANONYMOUS ,
2014-04-30 16:48:40 +02:00
- 1 , 0 ) ;
ASSERT_TRUE ( start ! = MAP_FAILED ) ;
extinfo_ . flags = ANDROID_DLEXT_RESERVED_ADDRESS ;
extinfo_ . reserved_addr = start ;
extinfo_ . reserved_size = LIBSIZE ;
extinfo_ . relro_fd = - 1 ;
}
2014-02-27 14:18:00 +01:00
2014-04-30 16:48:40 +02:00
virtual void TearDown ( ) {
DlExtTest : : TearDown ( ) ;
2014-02-27 14:18:00 +01:00
}
2014-12-08 05:43:37 +01:00
void CreateRelroFile ( const char * lib , const char * relro_file ) {
int relro_fd = open ( relro_file , O_RDWR | O_TRUNC ) ;
2014-04-30 16:48:40 +02:00
ASSERT_NOERROR ( relro_fd ) ;
pid_t pid = fork ( ) ;
if ( pid = = 0 ) {
// child process
extinfo_ . flags | = ANDROID_DLEXT_WRITE_RELRO ;
extinfo_ . relro_fd = relro_fd ;
void * handle = android_dlopen_ext ( lib , RTLD_NOW , & extinfo_ ) ;
2014-10-04 02:52:44 +02:00
if ( handle = = nullptr ) {
2014-04-30 16:48:40 +02:00
fprintf ( stderr , " in child: %s \n " , dlerror ( ) ) ;
exit ( 1 ) ;
}
exit ( 0 ) ;
}
// continuing in parent
ASSERT_NOERROR ( close ( relro_fd ) ) ;
ASSERT_NOERROR ( pid ) ;
int status ;
ASSERT_EQ ( pid , waitpid ( pid , & status , 0 ) ) ;
ASSERT_TRUE ( WIFEXITED ( status ) ) ;
ASSERT_EQ ( 0 , WEXITSTATUS ( status ) ) ;
// reopen file for reading so it can be used
2014-12-08 05:43:37 +01:00
relro_fd = open ( relro_file , O_RDONLY ) ;
2014-04-30 16:48:40 +02:00
ASSERT_NOERROR ( relro_fd ) ;
extinfo_ . flags | = ANDROID_DLEXT_USE_RELRO ;
extinfo_ . relro_fd = relro_fd ;
}
void TryUsingRelro ( const char * lib ) {
handle_ = android_dlopen_ext ( lib , RTLD_NOW , & extinfo_ ) ;
ASSERT_DL_NOTNULL ( handle_ ) ;
fn f = reinterpret_cast < fn > ( dlsym ( handle_ , " getRandomNumber " ) ) ;
ASSERT_DL_NOTNULL ( f ) ;
EXPECT_EQ ( 4 , f ( ) ) ;
2015-09-03 01:32:02 +02:00
uint32_t * taxicab_number = reinterpret_cast < uint32_t * > ( dlsym ( handle_ , " dlopen_testlib_taxicab_number " ) ) ;
ASSERT_DL_NOTNULL ( taxicab_number ) ;
EXPECT_EQ ( 1729U , * taxicab_number ) ;
2014-04-30 16:48:40 +02:00
}
2014-05-02 15:57:42 +02:00
void SpawnChildrenAndMeasurePss ( const char * lib , bool share_relro , size_t * pss_out ) ;
2014-04-30 16:48:40 +02:00
android_dlextinfo extinfo_ ;
} ;
TEST_F ( DlExtRelroSharingTest , ChildWritesGoodData ) {
2014-12-08 05:43:37 +01:00
TemporaryFile tf ; // Use tf to get an unique filename.
ASSERT_NOERROR ( close ( tf . fd ) ) ;
ASSERT_NO_FATAL_FAILURE ( CreateRelroFile ( LIBNAME , tf . filename ) ) ;
2014-04-30 16:48:40 +02:00
ASSERT_NO_FATAL_FAILURE ( TryUsingRelro ( LIBNAME ) ) ;
2014-12-08 05:43:37 +01:00
// Use destructor of tf to close and unlink the file.
tf . fd = extinfo_ . relro_fd ;
2014-04-30 16:48:40 +02:00
}
2014-02-27 14:18:00 +01:00
2014-04-30 16:48:40 +02:00
TEST_F ( DlExtRelroSharingTest , ChildWritesNoRelro ) {
2014-12-08 05:43:37 +01:00
TemporaryFile tf ; // // Use tf to get an unique filename.
ASSERT_NOERROR ( close ( tf . fd ) ) ;
ASSERT_NO_FATAL_FAILURE ( CreateRelroFile ( LIBNAME_NORELRO , tf . filename ) ) ;
2014-04-30 16:48:40 +02:00
ASSERT_NO_FATAL_FAILURE ( TryUsingRelro ( LIBNAME_NORELRO ) ) ;
2014-12-08 05:43:37 +01:00
// Use destructor of tf to close and unlink the file.
tf . fd = extinfo_ . relro_fd ;
2014-04-30 16:48:40 +02:00
}
TEST_F ( DlExtRelroSharingTest , RelroFileEmpty ) {
ASSERT_NO_FATAL_FAILURE ( TryUsingRelro ( LIBNAME ) ) ;
2014-02-27 14:18:00 +01:00
}
2014-05-02 15:57:42 +02:00
TEST_F ( DlExtRelroSharingTest , VerifyMemorySaving ) {
2014-09-03 20:30:21 +02:00
if ( geteuid ( ) ! = 0 ) {
GTEST_LOG_ ( INFO ) < < " This test must be run as root. \n " ;
return ;
}
2014-12-08 05:43:37 +01:00
TemporaryFile tf ; // Use tf to get an unique filename.
ASSERT_NOERROR ( close ( tf . fd ) ) ;
ASSERT_NO_FATAL_FAILURE ( CreateRelroFile ( LIBNAME , tf . filename ) ) ;
2014-05-02 15:57:42 +02:00
int pipefd [ 2 ] ;
ASSERT_NOERROR ( pipe ( pipefd ) ) ;
size_t without_sharing , with_sharing ;
ASSERT_NO_FATAL_FAILURE ( SpawnChildrenAndMeasurePss ( LIBNAME , false , & without_sharing ) ) ;
ASSERT_NO_FATAL_FAILURE ( SpawnChildrenAndMeasurePss ( LIBNAME , true , & with_sharing ) ) ;
// We expect the sharing to save at least 10% of the total PSS. In practice
// it saves 40%+ for this test.
size_t expected_size = without_sharing - ( without_sharing / 10 ) ;
EXPECT_LT ( with_sharing , expected_size ) ;
2014-12-08 05:43:37 +01:00
// Use destructor of tf to close and unlink the file.
tf . fd = extinfo_ . relro_fd ;
2014-05-02 15:57:42 +02:00
}
void getPss ( pid_t pid , size_t * pss_out ) {
pm_kernel_t * kernel ;
ASSERT_EQ ( 0 , pm_kernel_create ( & kernel ) ) ;
pm_process_t * process ;
ASSERT_EQ ( 0 , pm_process_create ( kernel , pid , & process ) ) ;
pm_map_t * * maps ;
size_t num_maps ;
ASSERT_EQ ( 0 , pm_process_maps ( process , & maps , & num_maps ) ) ;
size_t total_pss = 0 ;
for ( size_t i = 0 ; i < num_maps ; i + + ) {
pm_memusage_t usage ;
ASSERT_EQ ( 0 , pm_map_usage ( maps [ i ] , & usage ) ) ;
total_pss + = usage . pss ;
}
* pss_out = total_pss ;
free ( maps ) ;
pm_process_destroy ( process ) ;
pm_kernel_destroy ( kernel ) ;
}
void DlExtRelroSharingTest : : SpawnChildrenAndMeasurePss ( const char * lib , bool share_relro ,
size_t * pss_out ) {
const int CHILDREN = 20 ;
// Create children
pid_t childpid [ CHILDREN ] ;
int childpipe [ CHILDREN ] ;
for ( int i = 0 ; i < CHILDREN ; + + i ) {
char read_buf ;
int child_done_pipe [ 2 ] , parent_done_pipe [ 2 ] ;
ASSERT_NOERROR ( pipe ( child_done_pipe ) ) ;
ASSERT_NOERROR ( pipe ( parent_done_pipe ) ) ;
pid_t child = fork ( ) ;
if ( child = = 0 ) {
// close the 'wrong' ends of the pipes in the child
close ( child_done_pipe [ 0 ] ) ;
close ( parent_done_pipe [ 1 ] ) ;
// open the library
void * handle ;
if ( share_relro ) {
handle = android_dlopen_ext ( lib , RTLD_NOW , & extinfo_ ) ;
} else {
handle = dlopen ( lib , RTLD_NOW ) ;
}
2014-10-04 02:52:44 +02:00
if ( handle = = nullptr ) {
2014-05-02 15:57:42 +02:00
fprintf ( stderr , " in child: %s \n " , dlerror ( ) ) ;
exit ( 1 ) ;
}
// close write end of child_done_pipe to signal the parent that we're done.
close ( child_done_pipe [ 1 ] ) ;
// wait for the parent to close parent_done_pipe, then exit
read ( parent_done_pipe [ 0 ] , & read_buf , 1 ) ;
exit ( 0 ) ;
}
ASSERT_NOERROR ( child ) ;
// close the 'wrong' ends of the pipes in the parent
close ( child_done_pipe [ 1 ] ) ;
close ( parent_done_pipe [ 0 ] ) ;
// wait for the child to be done
read ( child_done_pipe [ 0 ] , & read_buf , 1 ) ;
close ( child_done_pipe [ 0 ] ) ;
// save the child's pid and the parent_done_pipe
childpid [ i ] = child ;
childpipe [ i ] = parent_done_pipe [ 1 ] ;
}
// Sum the PSS of all the children
size_t total_pss = 0 ;
for ( int i = 0 ; i < CHILDREN ; + + i ) {
size_t child_pss ;
ASSERT_NO_FATAL_FAILURE ( getPss ( childpid [ i ] , & child_pss ) ) ;
total_pss + = child_pss ;
}
* pss_out = total_pss ;
// Close pipes and wait for children to exit
for ( int i = 0 ; i < CHILDREN ; + + i ) {
ASSERT_NOERROR ( close ( childpipe [ i ] ) ) ;
}
for ( int i = 0 ; i < CHILDREN ; + + i ) {
int status ;
ASSERT_EQ ( childpid [ i ] , waitpid ( childpid [ i ] , & status , 0 ) ) ;
ASSERT_TRUE ( WIFEXITED ( status ) ) ;
ASSERT_EQ ( 0 , WEXITSTATUS ( status ) ) ;
}
}
2015-10-30 01:01:24 +01:00
// Testing namespaces
static const char * g_public_lib = " libnstest_public.so " ;
TEST ( dlext , ns_smoke ) {
static const char * root_lib = " libnstest_root.so " ;
std : : string path = std : : string ( " libc.so:libc++.so:libdl.so:libm.so: " ) + g_public_lib ;
2015-11-23 20:26:35 +01:00
ASSERT_FALSE ( android_init_namespaces ( path . c_str ( ) , nullptr ) ) ;
ASSERT_STREQ ( " android_init_namespaces failed: error initializing public namespace: "
2015-10-30 01:01:24 +01:00
" \" libnstest_public.so \" was not found in the default namespace " , dlerror ( ) ) ;
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + NATIVE_TESTS_PATH ;
2015-12-05 03:28:49 +01:00
const std : : string lib_public_path = lib_path + " /public_namespace_libs/ " + g_public_lib ;
void * handle_public = dlopen ( lib_public_path . c_str ( ) , RTLD_NOW ) ;
2015-10-30 01:01:24 +01:00
ASSERT_TRUE ( handle_public ! = nullptr ) < < dlerror ( ) ;
2015-11-23 20:26:35 +01:00
ASSERT_TRUE ( android_init_namespaces ( path . c_str ( ) , nullptr ) ) < < dlerror ( ) ;
2015-10-30 01:01:24 +01:00
// Check that libraries added to public namespace are NODELETE
dlclose ( handle_public ) ;
2015-12-14 23:11:17 +01:00
handle_public = dlopen ( ( lib_path + " /public_namespace_libs/ " + g_public_lib ) . c_str ( ) ,
RTLD_NOW | RTLD_NOLOAD ) ;
2015-10-30 01:01:24 +01:00
ASSERT_TRUE ( handle_public ! = nullptr ) < < dlerror ( ) ;
2015-12-14 23:11:17 +01:00
android_namespace_t * ns1 =
android_create_namespace ( " private " , nullptr ,
( lib_path + " /private_namespace_libs " ) . c_str ( ) ,
ANDROID_NAMESPACE_TYPE_REGULAR , nullptr ) ;
2015-10-30 01:01:24 +01:00
ASSERT_TRUE ( ns1 ! = nullptr ) < < dlerror ( ) ;
2015-12-14 23:11:17 +01:00
android_namespace_t * ns2 =
android_create_namespace ( " private_isolated " , nullptr ,
( lib_path + " /private_namespace_libs " ) . c_str ( ) ,
ANDROID_NAMESPACE_TYPE_ISOLATED , nullptr ) ;
2015-10-30 01:01:24 +01:00
ASSERT_TRUE ( ns2 ! = nullptr ) < < dlerror ( ) ;
// This should not have affect search path for default namespace:
ASSERT_TRUE ( dlopen ( root_lib , RTLD_NOW ) = = nullptr ) ;
void * handle = dlopen ( g_public_lib , RTLD_NOW ) ;
ASSERT_TRUE ( handle ! = nullptr ) < < dlerror ( ) ;
dlclose ( handle ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_USE_NAMESPACE ;
extinfo . library_namespace = ns1 ;
void * handle1 = android_dlopen_ext ( root_lib , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle1 ! = nullptr ) < < dlerror ( ) ;
extinfo . library_namespace = ns2 ;
void * handle2 = android_dlopen_ext ( root_lib , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle2 ! = nullptr ) < < dlerror ( ) ;
ASSERT_TRUE ( handle1 ! = handle2 ) ;
2015-12-05 03:28:49 +01:00
// dlopen for a public library using an absolute path should work for isolated namespaces
extinfo . library_namespace = ns2 ;
handle = android_dlopen_ext ( lib_public_path . c_str ( ) , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle ! = nullptr ) < < dlerror ( ) ;
ASSERT_TRUE ( handle = = handle_public ) ;
dlclose ( handle ) ;
2015-10-30 01:01:24 +01:00
typedef const char * ( * fn_t ) ( ) ;
fn_t ns_get_local_string1 = reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_local_string " ) ) ;
ASSERT_TRUE ( ns_get_local_string1 ! = nullptr ) < < dlerror ( ) ;
fn_t ns_get_local_string2 = reinterpret_cast < fn_t > ( dlsym ( handle2 , " ns_get_local_string " ) ) ;
ASSERT_TRUE ( ns_get_local_string2 ! = nullptr ) < < dlerror ( ) ;
EXPECT_STREQ ( " This string is local to root library " , ns_get_local_string1 ( ) ) ;
EXPECT_STREQ ( " This string is local to root library " , ns_get_local_string2 ( ) ) ;
ASSERT_TRUE ( ns_get_local_string1 ( ) ! = ns_get_local_string2 ( ) ) ;
fn_t ns_get_private_extern_string1 =
reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_private_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_private_extern_string1 ! = nullptr ) < < dlerror ( ) ;
fn_t ns_get_private_extern_string2 =
reinterpret_cast < fn_t > ( dlsym ( handle2 , " ns_get_private_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_private_extern_string2 ! = nullptr ) < < dlerror ( ) ;
EXPECT_STREQ ( " This string is from private namespace " , ns_get_private_extern_string1 ( ) ) ;
EXPECT_STREQ ( " This string is from private namespace " , ns_get_private_extern_string2 ( ) ) ;
ASSERT_TRUE ( ns_get_private_extern_string1 ( ) ! = ns_get_private_extern_string2 ( ) ) ;
fn_t ns_get_public_extern_string1 =
reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_public_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_public_extern_string1 ! = nullptr ) < < dlerror ( ) ;
fn_t ns_get_public_extern_string2 =
reinterpret_cast < fn_t > ( dlsym ( handle2 , " ns_get_public_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_public_extern_string2 ! = nullptr ) < < dlerror ( ) ;
EXPECT_STREQ ( " This string is from public namespace " , ns_get_public_extern_string1 ( ) ) ;
ASSERT_TRUE ( ns_get_public_extern_string1 ( ) = = ns_get_public_extern_string2 ( ) ) ;
// and now check that dlopen() does the right thing in terms of preserving namespace
fn_t ns_get_dlopened_string1 = reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_dlopened_string " ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string1 ! = nullptr ) < < dlerror ( ) ;
fn_t ns_get_dlopened_string2 = reinterpret_cast < fn_t > ( dlsym ( handle2 , " ns_get_dlopened_string " ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string2 ! = nullptr ) < < dlerror ( ) ;
EXPECT_STREQ ( " This string is from private namespace (dlopened library) " , ns_get_dlopened_string1 ( ) ) ;
EXPECT_STREQ ( " This string is from private namespace (dlopened library) " , ns_get_dlopened_string2 ( ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string1 ( ) ! = ns_get_dlopened_string2 ( ) ) ;
dlclose ( handle1 ) ;
// Check if handle2 is still alive (and well)
ASSERT_STREQ ( " This string is local to root library " , ns_get_local_string2 ( ) ) ;
ASSERT_STREQ ( " This string is from private namespace " , ns_get_private_extern_string2 ( ) ) ;
ASSERT_STREQ ( " This string is from public namespace " , ns_get_public_extern_string2 ( ) ) ;
ASSERT_STREQ ( " This string is from private namespace (dlopened library) " , ns_get_dlopened_string2 ( ) ) ;
dlclose ( handle2 ) ;
}
2015-11-18 03:36:50 +01:00
extern " C " void android_set_application_target_sdk_version ( uint32_t target ) ;
2015-10-30 01:01:24 +01:00
TEST ( dlext , ns_isolated ) {
static const char * root_lib = " libnstest_root_not_isolated.so " ;
std : : string path = std : : string ( " libc.so:libc++.so:libdl.so:libm.so: " ) + g_public_lib ;
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + NATIVE_TESTS_PATH ;
2015-12-05 03:28:49 +01:00
const std : : string lib_public_path = lib_path + " /public_namespace_libs/ " + g_public_lib ;
void * handle_public = dlopen ( lib_public_path . c_str ( ) , RTLD_NOW ) ;
2015-10-30 01:01:24 +01:00
ASSERT_TRUE ( handle_public ! = nullptr ) < < dlerror ( ) ;
2015-11-18 03:36:50 +01:00
android_set_application_target_sdk_version ( 42U ) ; // something > 23
2015-11-23 20:26:35 +01:00
ASSERT_TRUE ( android_init_namespaces ( path . c_str ( ) , nullptr ) ) < < dlerror ( ) ;
2015-10-30 01:01:24 +01:00
2015-12-14 23:11:17 +01:00
android_namespace_t * ns_not_isolated =
android_create_namespace ( " private " , nullptr ,
( lib_path + " /private_namespace_libs " ) . c_str ( ) ,
ANDROID_NAMESPACE_TYPE_REGULAR , nullptr ) ;
2015-10-30 01:01:24 +01:00
ASSERT_TRUE ( ns_not_isolated ! = nullptr ) < < dlerror ( ) ;
2015-12-14 23:11:17 +01:00
android_namespace_t * ns_isolated =
android_create_namespace ( " private_isolated1 " , nullptr ,
( lib_path + " /private_namespace_libs " ) . c_str ( ) ,
ANDROID_NAMESPACE_TYPE_ISOLATED , nullptr ) ;
2015-10-30 01:01:24 +01:00
ASSERT_TRUE ( ns_isolated ! = nullptr ) < < dlerror ( ) ;
2015-12-14 23:11:17 +01:00
android_namespace_t * ns_isolated2 =
android_create_namespace ( " private_isolated2 " ,
( lib_path + " /private_namespace_libs " ) . c_str ( ) ,
nullptr , ANDROID_NAMESPACE_TYPE_ISOLATED , lib_path . c_str ( ) ) ;
2015-10-30 01:01:24 +01:00
ASSERT_TRUE ( ns_isolated2 ! = nullptr ) < < dlerror ( ) ;
ASSERT_TRUE ( dlopen ( root_lib , RTLD_NOW ) = = nullptr ) ;
ASSERT_STREQ ( " dlopen failed: library \" libnstest_root_not_isolated.so \" not found " , dlerror ( ) ) ;
std : : string lib_private_external_path =
lib_path + " /private_namespace_libs_external/libnstest_private_external.so " ;
// Load lib_private_external_path to default namespace
// (it should remain invisible for the isolated namespaces after this)
void * handle = dlopen ( lib_private_external_path . c_str ( ) , RTLD_NOW ) ;
ASSERT_TRUE ( handle ! = nullptr ) < < dlerror ( ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_USE_NAMESPACE ;
extinfo . library_namespace = ns_not_isolated ;
void * handle1 = android_dlopen_ext ( root_lib , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle1 ! = nullptr ) < < dlerror ( ) ;
extinfo . library_namespace = ns_isolated ;
void * handle2 = android_dlopen_ext ( root_lib , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle2 = = nullptr ) ;
ASSERT_STREQ ( " dlopen failed: library \" libnstest_private_external.so \" not found " , dlerror ( ) ) ;
// Check dlopen by absolute path
handle2 = android_dlopen_ext ( lib_private_external_path . c_str ( ) , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle2 = = nullptr ) ;
2015-12-05 03:28:49 +01:00
ASSERT_EQ ( " dlopen failed: library \" " + lib_private_external_path + " \" is not accessible for the namespace \" private_isolated1 \" " , dlerror ( ) ) ;
2015-10-30 01:01:24 +01:00
extinfo . library_namespace = ns_isolated2 ;
2015-12-08 19:47:13 +01:00
// this should work because isolation_path for private_isolated2 includes lib_path
2015-10-30 01:01:24 +01:00
handle2 = android_dlopen_ext ( root_lib , RTLD_NOW , & extinfo ) ;
2015-12-08 19:47:13 +01:00
ASSERT_TRUE ( handle2 ! = nullptr ) < < dlerror ( ) ;
dlclose ( handle2 ) ;
2015-10-30 01:01:24 +01:00
// Check dlopen by absolute path
handle2 = android_dlopen_ext ( lib_private_external_path . c_str ( ) , RTLD_NOW , & extinfo ) ;
2015-12-08 19:47:13 +01:00
ASSERT_TRUE ( handle2 ! = nullptr ) < < dlerror ( ) ;
dlclose ( handle2 ) ;
2015-10-30 01:01:24 +01:00
typedef const char * ( * fn_t ) ( ) ;
fn_t ns_get_local_string = reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_local_string " ) ) ;
ASSERT_TRUE ( ns_get_local_string ! = nullptr ) < < dlerror ( ) ;
ASSERT_STREQ ( " This string is local to root library " , ns_get_local_string ( ) ) ;
fn_t ns_get_private_extern_string =
reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_private_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_private_extern_string ! = nullptr ) < < dlerror ( ) ;
ASSERT_STREQ ( " This string is from private namespace " , ns_get_private_extern_string ( ) ) ;
fn_t ns_get_public_extern_string =
reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_public_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_public_extern_string ! = nullptr ) < < dlerror ( ) ;
ASSERT_STREQ ( " This string is from public namespace " , ns_get_public_extern_string ( ) ) ;
fn_t ns_get_dlopened_string = reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_dlopened_string " ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string ! = nullptr ) < < dlerror ( ) ;
ASSERT_STREQ ( " This string is from private namespace (dlopened library) " , ns_get_dlopened_string ( ) ) ;
dlclose ( handle1 ) ;
}
2015-11-23 20:26:35 +01:00
2015-12-14 23:11:17 +01:00
TEST ( dlext , ns_shared ) {
static const char * root_lib = " libnstest_root_not_isolated.so " ;
static const char * root_lib_isolated = " libnstest_root.so " ;
std : : string path = std : : string ( " libc.so:libc++.so:libdl.so:libm.so: " ) + g_public_lib ;
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + NATIVE_TESTS_PATH ;
const std : : string lib_public_path = lib_path + " /public_namespace_libs/ " + g_public_lib ;
void * handle_public = dlopen ( lib_public_path . c_str ( ) , RTLD_NOW ) ;
ASSERT_TRUE ( handle_public ! = nullptr ) < < dlerror ( ) ;
android_set_application_target_sdk_version ( 42U ) ; // something > 23
ASSERT_TRUE ( android_init_namespaces ( path . c_str ( ) , nullptr ) ) < < dlerror ( ) ;
// preload this library to the default namespace to check if it
// is shared later on.
void * handle_dlopened =
dlopen ( ( lib_path + " /private_namespace_libs/libnstest_dlopened.so " ) . c_str ( ) , RTLD_NOW ) ;
ASSERT_TRUE ( handle_dlopened ! = nullptr ) < < dlerror ( ) ;
android_namespace_t * ns_not_isolated =
android_create_namespace ( " private " , nullptr ,
( lib_path + " /private_namespace_libs " ) . c_str ( ) ,
ANDROID_NAMESPACE_TYPE_REGULAR , nullptr ) ;
ASSERT_TRUE ( ns_not_isolated ! = nullptr ) < < dlerror ( ) ;
android_namespace_t * ns_isolated_shared =
android_create_namespace ( " private_isolated_shared " , nullptr ,
( lib_path + " /private_namespace_libs " ) . c_str ( ) ,
ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED ,
nullptr ) ;
ASSERT_TRUE ( ns_isolated_shared ! = nullptr ) < < dlerror ( ) ;
ASSERT_TRUE ( dlopen ( root_lib , RTLD_NOW ) = = nullptr ) ;
ASSERT_STREQ ( " dlopen failed: library \" libnstest_root_not_isolated.so \" not found " , dlerror ( ) ) ;
std : : string lib_private_external_path =
lib_path + " /private_namespace_libs_external/libnstest_private_external.so " ;
// Load lib_private_external_path to default namespace
// (it should remain invisible for the isolated namespaces after this)
void * handle = dlopen ( lib_private_external_path . c_str ( ) , RTLD_NOW ) ;
ASSERT_TRUE ( handle ! = nullptr ) < < dlerror ( ) ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_USE_NAMESPACE ;
extinfo . library_namespace = ns_not_isolated ;
void * handle1 = android_dlopen_ext ( root_lib , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle1 ! = nullptr ) < < dlerror ( ) ;
extinfo . library_namespace = ns_isolated_shared ;
void * handle2 = android_dlopen_ext ( root_lib , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle2 = = nullptr ) ;
ASSERT_STREQ ( " dlopen failed: library \" libnstest_private_external.so \" not found " , dlerror ( ) ) ;
// Check dlopen by absolute path
handle2 = android_dlopen_ext ( lib_private_external_path . c_str ( ) , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle2 = = nullptr ) ;
ASSERT_EQ ( " dlopen failed: library \" " + lib_private_external_path + " \" is not accessible for the namespace \" private_isolated_shared \" " , dlerror ( ) ) ;
// load libnstest_root.so to shared namespace in order to check that everything is different
// except shared libnstest_dlopened.so
handle2 = android_dlopen_ext ( root_lib_isolated , RTLD_NOW , & extinfo ) ;
typedef const char * ( * fn_t ) ( ) ;
fn_t ns_get_local_string = reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_local_string " ) ) ;
ASSERT_TRUE ( ns_get_local_string ! = nullptr ) < < dlerror ( ) ;
fn_t ns_get_local_string_shared = reinterpret_cast < fn_t > ( dlsym ( handle2 , " ns_get_local_string " ) ) ;
ASSERT_TRUE ( ns_get_local_string_shared ! = nullptr ) < < dlerror ( ) ;
ASSERT_STREQ ( " This string is local to root library " , ns_get_local_string ( ) ) ;
ASSERT_STREQ ( " This string is local to root library " , ns_get_local_string_shared ( ) ) ;
ASSERT_TRUE ( ns_get_local_string ( ) ! = ns_get_local_string_shared ( ) ) ;
fn_t ns_get_private_extern_string =
reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_private_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_private_extern_string ! = nullptr ) < < dlerror ( ) ;
fn_t ns_get_private_extern_string_shared =
reinterpret_cast < fn_t > ( dlsym ( handle2 , " ns_get_private_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_private_extern_string_shared ( ) ! = nullptr ) < < dlerror ( ) ;
ASSERT_STREQ ( " This string is from private namespace " , ns_get_private_extern_string ( ) ) ;
ASSERT_STREQ ( " This string is from private namespace " , ns_get_private_extern_string_shared ( ) ) ;
ASSERT_TRUE ( ns_get_private_extern_string ( ) ! = ns_get_private_extern_string_shared ( ) ) ;
fn_t ns_get_public_extern_string =
reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_public_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_public_extern_string ! = nullptr ) < < dlerror ( ) ;
fn_t ns_get_public_extern_string_shared =
reinterpret_cast < fn_t > ( dlsym ( handle2 , " ns_get_public_extern_string " ) ) ;
ASSERT_TRUE ( ns_get_public_extern_string_shared ! = nullptr ) < < dlerror ( ) ;
ASSERT_STREQ ( " This string is from public namespace " , ns_get_public_extern_string ( ) ) ;
ASSERT_STREQ ( " This string is from public namespace " , ns_get_public_extern_string_shared ( ) ) ;
ASSERT_TRUE ( ns_get_public_extern_string ( ) = = ns_get_public_extern_string_shared ( ) ) ;
fn_t ns_get_dlopened_string = reinterpret_cast < fn_t > ( dlsym ( handle1 , " ns_get_dlopened_string " ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string ! = nullptr ) < < dlerror ( ) ;
fn_t ns_get_dlopened_string_shared = reinterpret_cast < fn_t > ( dlsym ( handle2 , " ns_get_dlopened_string " ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string_shared ! = nullptr ) < < dlerror ( ) ;
const char * * ns_dlopened_string = static_cast < const char * * > ( dlsym ( handle_dlopened , " g_private_dlopened_string " ) ) ;
ASSERT_TRUE ( ns_dlopened_string ! = nullptr ) < < dlerror ( ) ;
ASSERT_STREQ ( " This string is from private namespace (dlopened library) " , ns_get_dlopened_string ( ) ) ;
ASSERT_STREQ ( " This string is from private namespace (dlopened library) " , * ns_dlopened_string ) ;
ASSERT_STREQ ( " This string is from private namespace (dlopened library) " , ns_get_dlopened_string_shared ( ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string ( ) ! = ns_get_dlopened_string_shared ( ) ) ;
ASSERT_TRUE ( * ns_dlopened_string = = ns_get_dlopened_string_shared ( ) ) ;
dlclose ( handle1 ) ;
dlclose ( handle2 ) ;
}
2015-11-23 20:26:35 +01:00
TEST ( dlext , ns_anonymous ) {
static const char * root_lib = " libnstest_root.so " ;
std : : string path = std : : string ( " libc.so:libc++.so:libdl.so:libm.so: " ) + g_public_lib ;
const std : : string lib_path = std : : string ( getenv ( " ANDROID_DATA " ) ) + NATIVE_TESTS_PATH ;
2015-12-05 03:28:49 +01:00
const std : : string lib_public_path = lib_path + " /public_namespace_libs/ " + g_public_lib ;
void * handle_public = dlopen ( lib_public_path . c_str ( ) , RTLD_NOW ) ;
2015-11-23 20:26:35 +01:00
ASSERT_TRUE ( handle_public ! = nullptr ) < < dlerror ( ) ;
ASSERT_TRUE ( android_init_namespaces ( path . c_str ( ) , ( lib_path + " /private_namespace_libs " ) . c_str ( ) ) )
< < dlerror ( ) ;
android_namespace_t * ns = android_create_namespace (
" private " , nullptr ,
( lib_path + " /private_namespace_libs " ) . c_str ( ) ,
2015-12-14 23:11:17 +01:00
ANDROID_NAMESPACE_TYPE_REGULAR , nullptr ) ;
2015-11-23 20:26:35 +01:00
ASSERT_TRUE ( ns ! = nullptr ) < < dlerror ( ) ;
std : : string private_library_absolute_path = lib_path + " /private_namespace_libs/ " + root_lib ;
android_dlextinfo extinfo ;
extinfo . flags = ANDROID_DLEXT_USE_NAMESPACE ;
extinfo . library_namespace = ns ;
// we are going to copy this library to anonymous mmap and call the copy of ns_get_dlopened_string
void * handle = android_dlopen_ext ( private_library_absolute_path . c_str ( ) , RTLD_NOW , & extinfo ) ;
ASSERT_TRUE ( handle ! = nullptr ) < < dlerror ( ) ;
uintptr_t ns_get_dlopened_string_addr =
reinterpret_cast < uintptr_t > ( dlsym ( handle , " ns_get_dlopened_string " ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string_addr ! = 0 ) < < dlerror ( ) ;
typedef const char * ( * fn_t ) ( ) ;
fn_t ns_get_dlopened_string_private = reinterpret_cast < fn_t > ( ns_get_dlopened_string_addr ) ;
std : : vector < map_record > maps ;
Maps : : parse_maps ( & maps ) ;
uintptr_t addr_start = 0 ;
uintptr_t addr_end = 0 ;
std : : vector < map_record > maps_to_copy ;
for ( const auto & rec : maps ) {
if ( rec . pathname = = private_library_absolute_path ) {
if ( addr_start = = 0 ) {
addr_start = rec . addr_start ;
}
addr_end = rec . addr_end ;
maps_to_copy . push_back ( rec ) ;
}
}
// some sanity checks..
ASSERT_TRUE ( addr_start > 0 ) ;
ASSERT_TRUE ( addr_end > 0 ) ;
ASSERT_EQ ( 3U , maps_to_copy . size ( ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string_addr > addr_start ) ;
ASSERT_TRUE ( ns_get_dlopened_string_addr < addr_end ) ;
// copy
uintptr_t reserved_addr = reinterpret_cast < uintptr_t > ( mmap ( nullptr , addr_end - addr_start ,
PROT_NONE , MAP_ANON | MAP_PRIVATE ,
- 1 , 0 ) ) ;
ASSERT_TRUE ( reinterpret_cast < void * > ( reserved_addr ) ! = MAP_FAILED ) ;
for ( const auto & rec : maps_to_copy ) {
uintptr_t offset = rec . addr_start - addr_start ;
size_t size = rec . addr_end - rec . addr_start ;
void * addr = reinterpret_cast < void * > ( reserved_addr + offset ) ;
void * map = mmap ( addr , size , PROT_READ | PROT_WRITE ,
MAP_ANON | MAP_PRIVATE | MAP_FIXED , - 1 , 0 ) ;
ASSERT_TRUE ( map ! = MAP_FAILED ) ;
memcpy ( map , reinterpret_cast < void * > ( rec . addr_start ) , size ) ;
mprotect ( map , size , rec . perms ) ;
}
// call the function copy
uintptr_t ns_get_dlopened_string_offset = ns_get_dlopened_string_addr - addr_start ;
fn_t ns_get_dlopened_string_anon = reinterpret_cast < fn_t > ( reserved_addr + ns_get_dlopened_string_offset ) ;
ASSERT_STREQ ( " This string is from private namespace (dlopened library) " ,
ns_get_dlopened_string_anon ( ) ) ;
// They should belong to different namespaces (private and anonymous)
ASSERT_STREQ ( " This string is from private namespace (dlopened library) " ,
ns_get_dlopened_string_private ( ) ) ;
ASSERT_TRUE ( ns_get_dlopened_string_anon ( ) ! = ns_get_dlopened_string_private ( ) ) ;
}