From e4fa7cc3fb9909c6aee411de15a06f918687b8e2 Mon Sep 17 00:00:00 2001 From: Matthias Kraft Date: Wed, 7 Mar 2018 01:48:45 +0100 Subject: [PATCH] Custome built dladdr() for AIX. Implemented a stripped down dladdr()-implementation using AIX' own loadquery()-function. Following the SGI example in the same code, the DL_info only has the dli_fname member. As the scope of dlfcn_pathbyaddr() is the filename, this implementation does not consider archive members, which can be dlopen()ed in AIX. Added DATA segment checking to catch ptrgl virtual addresses. Added test case for DSO_dsobyaddr(), but only for DSO_DLFCN. Added PIC-flag to aix*-cc build targets. Signed-off-by: Matthias Kraft Reviewed-by: Andy Polyakov Reviewed-by: Rich Salz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/5626) --- Configurations/10-main.conf | 2 + crypto/dso/dso_dlfcn.c | 83 ++++++++++++++++++++++++++++++-- crypto/init.c | 17 ++++++- test/recipes/90-test_shlibload.t | 6 ++- test/shlibloadtest.c | 58 ++++++++++++++++++++-- 5 files changed, 155 insertions(+), 11 deletions(-) diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf index 8559667b19..7e66705261 100644 --- a/Configurations/10-main.conf +++ b/Configurations/10-main.conf @@ -1210,6 +1210,7 @@ sub vms_info { perlasm_scheme => "aix32", dso_scheme => "dlfcn", shared_target => "aix-shared", + shared_cflag => "-qpic", shared_ldflag => "-q32 -G", shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", arflags => "-X 32", @@ -1228,6 +1229,7 @@ sub vms_info { perlasm_scheme => "aix64", dso_scheme => "dlfcn", shared_target => "aix-shared", + shared_cflag => "-qpic", shared_ldflag => "-q64 -G", shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", arflags => "-X 64", diff --git a/crypto/dso/dso_dlfcn.c b/crypto/dso/dso_dlfcn.c index a4b0cdd95b..e01425bc75 100644 --- a/crypto/dso/dso_dlfcn.c +++ b/crypto/dso/dso_dlfcn.c @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -26,7 +26,7 @@ # endif # include # define HAVE_DLINFO 1 -# if defined(_AIX) || defined(__CYGWIN__) || \ +# if defined(__CYGWIN__) || \ defined(__SCO_VERSION__) || defined(_SCO_ELF) || \ (defined(__osf__) && !defined(RTLD_NEXT)) || \ (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \ @@ -308,6 +308,76 @@ static int dladdr(void *address, Dl_info *dl) } # endif /* __sgi */ +# ifdef _AIX +/*- + * See IBM's AIX Version 7.2, Technical Reference: + * Base Operating System and Extensions, Volume 1 and 2 + * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm + */ +# include +# include +/* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */ +# define DLFCN_LDINFO_SIZE 86976 +typedef struct Dl_info { + const char *dli_fname; +} Dl_info; +/* + * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual + * address of a function, which is just located in the DATA segment instead of + * the TEXT segment. + */ +static int dladdr(void *ptr, Dl_info *dl) +{ + uintptr_t addr = (uintptr_t)ptr; + unsigned int found = 0; + struct ld_info *ldinfos, *next_ldi, *this_ldi; + + if ((ldinfos = (struct ld_info *)OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) { + errno = ENOMEM; + dl->dli_fname = NULL; + return 0; + } + + if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) { + /*- + * Error handling is done through errno and dlerror() reading errno: + * ENOMEM (ldinfos buffer is too small), + * EINVAL (invalid flags), + * EFAULT (invalid ldinfos ptr) + */ + OPENSSL_free((void *)ldinfos); + dl->dli_fname = NULL; + return 0; + } + next_ldi = ldinfos; + + do { + this_ldi = next_ldi; + if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg) + && (addr < ((uintptr_t)this_ldi->ldinfo_textorg + + this_ldi->ldinfo_textsize))) + || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg) + && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg + + this_ldi->ldinfo_datasize)))) { + found = 1; + /* + * Ignoring the possibility of a member name and just returning + * the path name. See docs: sys/ldr.h, loadquery() and + * dlopen()/RTLD_MEMBER. + */ + if ((dl->dli_fname = + OPENSSL_strdup(this_ldi->ldinfo_filename)) == NULL) + errno = ENOMEM; + } else { + next_ldi = + (struct ld_info *)((uintptr_t)this_ldi + this_ldi->ldinfo_next); + } + } while (this_ldi->ldinfo_next && !found); + OPENSSL_free((void *)ldinfos); + return (found && dl->dli_fname != NULL); +} +# endif /* _AIX */ + static int dlfcn_pathbyaddr(void *addr, char *path, int sz) { # ifdef HAVE_DLINFO @@ -326,12 +396,19 @@ static int dlfcn_pathbyaddr(void *addr, char *path, int sz) if (dladdr(addr, &dli)) { len = (int)strlen(dli.dli_fname); - if (sz <= 0) + if (sz <= 0) { +# ifdef _AIX + OPENSSL_free((void *)dli.dli_fname); +# endif return len + 1; + } if (len >= sz) len = sz - 1; memcpy(path, dli.dli_fname, len); path[len++] = 0; +# ifdef _AIX + OPENSSL_free((void *)dli.dli_fname); +# endif return len; } diff --git a/crypto/init.c b/crypto/init.c index 173eac3747..15531135fe 100644 --- a/crypto/init.c +++ b/crypto/init.c @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -114,6 +114,15 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_base) ERR_set_mark(); dso = DSO_dsobyaddr(&base_inited, DSO_FLAG_NO_UNLOAD_ON_FREE); +# ifdef OPENSSL_INIT_DEBUG + fprintf(stderr, "OPENSSL_INIT: obtained DSO reference? %s\n", + (dso == NULL ? "No!" : "Yes.")); + /* + * In case of No!, it is uncertain our exit()-handlers can still be + * called. After dlclose() the whole library might have been unloaded + * already. + */ +# endif DSO_free(dso); ERR_pop_to_mark(); } @@ -657,6 +666,12 @@ int OPENSSL_atexit(void (*handler)(void)) ERR_set_mark(); dso = DSO_dsobyaddr(handlersym.sym, DSO_FLAG_NO_UNLOAD_ON_FREE); +# ifdef OPENSSL_INIT_DEBUG + fprintf(stderr, + "OPENSSL_INIT: OPENSSL_atexit: obtained DSO reference? %s\n", + (dso == NULL ? "No!" : "Yes.")); + /* See same code above in ossl_init_base() for an explanation. */ +# endif DSO_free(dso); ERR_pop_to_mark(); } diff --git a/test/recipes/90-test_shlibload.t b/test/recipes/90-test_shlibload.t index aa8d98de29..04d5265890 100644 --- a/test/recipes/90-test_shlibload.t +++ b/test/recipes/90-test_shlibload.t @@ -1,5 +1,5 @@ #! /usr/bin/env perl -# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. +# Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved. # # Licensed under the OpenSSL license (the "License"). You may not use # this file except in compliance with the License. You can obtain a copy @@ -20,7 +20,7 @@ use configdata; plan skip_all => "Test only supported in a shared build" if disabled("shared"); -plan tests => 3; +plan tests => 4; my $libcrypto_idx = $unified_info{rename}->{libcrypto} // "libcrypto"; my $libssl_idx = $unified_info{rename}->{libssl} // "libssl"; @@ -35,4 +35,6 @@ ok(run(test(["shlibloadtest", "-ssl_first", $libcrypto, $libssl])), "running shlibloadtest -ssl_first"); ok(run(test(["shlibloadtest", "-just_crypto", $libcrypto, $libssl])), "running shlibloadtest -just_crypto"); +ok(run(test(["shlibloadtest", "-dso_ref", $libcrypto, $libssl])), + "running shlibloadtest -dso_ref"); diff --git a/test/shlibloadtest.c b/test/shlibloadtest.c index 25df363f23..919bf7c5f0 100644 --- a/test/shlibloadtest.c +++ b/test/shlibloadtest.c @@ -40,6 +40,16 @@ static OpenSSL_version_num_t OpenSSL_version_num; #ifdef DSO_DLFCN +# define DSO_DSOBYADDR "DSO_dsobyaddr" +# define DSO_FREE "DSO_free" + +typedef void DSO; +typedef DSO * (*DSO_dsobyaddr_t)(void (*addr)(), int flags); +typedef int (*DSO_free_t)(DSO *dso); + +static DSO_dsobyaddr_t DSO_dsobyaddr; +static DSO_free_t DSO_free; + # include typedef void * SHLIB; @@ -108,11 +118,13 @@ static int shlib_close(SHLIB lib) # define CRYPTO_FIRST_OPT "-crypto_first" # define SSL_FIRST_OPT "-ssl_first" # define JUST_CRYPTO_OPT "-just_crypto" +# define DSO_REFTEST_OPT "-dso_ref" enum test_types_en { CRYPTO_FIRST, SSL_FIRST, - JUST_CRYPTO + JUST_CRYPTO, + DSO_REFTEST }; int main(int argc, char **argv) @@ -123,7 +135,7 @@ int main(int argc, char **argv) void (*func) (void); SHLIB_SYM sym; } tls_method_sym, ssl_ctx_new_sym, ssl_ctx_free_sym, err_get_error_sym, - openssl_version_num_sym; + openssl_version_num_sym, dso_dsobyaddr_sym, dso_free_sym; enum test_types_en test_type; int i; @@ -138,6 +150,8 @@ int main(int argc, char **argv) test_type = SSL_FIRST; } else if (strcmp(argv[1], JUST_CRYPTO_OPT) == 0) { test_type = JUST_CRYPTO; + } else if (strcmp(argv[1], DSO_REFTEST_OPT) == 0) { + test_type = DSO_REFTEST; } else { printf("Unrecognised argument\n"); return 1; @@ -145,7 +159,8 @@ int main(int argc, char **argv) for (i = 0; i < 2; i++) { if ((i == 0 && (test_type == CRYPTO_FIRST - || test_type == JUST_CRYPTO)) + || test_type == JUST_CRYPTO + || test_type == DSO_REFTEST)) || (i == 1 && test_type == SSL_FIRST)) { if (!shlib_load(argv[2], &cryptolib)) { printf("Unable to load libcrypto\n"); @@ -161,7 +176,7 @@ int main(int argc, char **argv) } } - if (test_type != JUST_CRYPTO) { + if (test_type != JUST_CRYPTO && test_type != DSO_REFTEST) { if (!shlib_sym(ssllib, TLS_METHOD, &tls_method_sym.sym) || !shlib_sym(ssllib, SSL_CTX_NEW, &ssl_ctx_new_sym.sym) || !shlib_sym(ssllib, SSL_CTX_FREE, &ssl_ctx_free_sym.sym)) { @@ -215,6 +230,38 @@ int main(int argc, char **argv) return 1; } + if (test_type == DSO_REFTEST) { +# ifdef DSO_DLFCN + /* + * This is resembling the code used in ossl_init_base() and + * OPENSSL_atexit() to block unloading the library after dlclose(). + * We are not testing this on Windows, because it is done there in a + * completely different way. Especially as a call to DSO_dsobyaddr() + * will always return an error, because DSO_pathbyaddr() is not + * implemented there. + */ + if (!shlib_sym(cryptolib, DSO_DSOBYADDR, &dso_dsobyaddr_sym.sym) + || !shlib_sym(cryptolib, DSO_FREE, &dso_free_sym.sym)) { + printf("Unable to load crypto dso symbols\n"); + return 1; + } + + DSO_dsobyaddr = (DSO_dsobyaddr_t)dso_dsobyaddr_sym.func; + DSO_free = (DSO_free_t)dso_free_sym.func; + + { + DSO *hndl; + /* use known symbol from crypto module */ + if ((hndl = DSO_dsobyaddr((void (*)())ERR_get_error, 0)) != NULL) { + DSO_free(hndl); + } else { + printf("Unable to obtain DSO reference from crypto symbol\n"); + return 1; + } + } +# endif /* DSO_DLFCN */ + } + for (i = 0; i < 2; i++) { if ((i == 0 && test_type == CRYPTO_FIRST) || (i == 1 && test_type == SSL_FIRST)) { @@ -224,7 +271,8 @@ int main(int argc, char **argv) } } if ((i == 0 && (test_type == SSL_FIRST - || test_type == JUST_CRYPTO)) + || test_type == JUST_CRYPTO + || test_type == DSO_REFTEST)) || (i == 1 && test_type == CRYPTO_FIRST)) { if (!shlib_close(cryptolib)) { printf("Unable to close libcrypto\n"); -- 2.25.1