From ac66ab88e83eb6caf54d88eac61bea3088be5d00 Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Fri, 21 Oct 2022 16:28:23 -0400 Subject: [PATCH] i#5437 glibc2.34: Workaround for SIGFPE Adds a workaround for the SIGFPE in glibc 2.34+ __libc_early_init() by setting two ld.so globals located via hardcoded offsets, making this fragile and considered temporary. Tested on glibc 2.34 where every libc-using client crashes with SIGFPE but they work with this fix. Adds an Ubuntu22 GA CI run but if we have failures due to other reasons the plan is to drastically shrink the tests run or abandon if it's too much work right now. Issue: #5437 --- .github/workflows/ci-x86.yml | 63 ++++++++++++++++++++++++++++++++++++ core/unix/loader.c | 62 ++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-x86.yml b/.github/workflows/ci-x86.yml index 73ee62b5912..9621c233213 100644 --- a/.github/workflows/ci-x86.yml +++ b/.github/workflows/ci-x86.yml @@ -196,3 +196,66 @@ jobs: See more details on github.com/DynamoRIO/dynamorio/actions/runs/${{github.run_id}} to: dynamorio-devs@googlegroups.com from: Github Action CI + + # Ubuntu22 64-bit Linux build with gcc and run tests: + x86-64: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + + # Cancel any prior runs for a PR (but do not cancel master branch runs). + - uses: n1hility/cancel-previous-runs@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + if: ${{ github.event_name == 'pull_request' }} + + - run: git fetch --no-tags --depth=1 origin master + + # Install multilib for non-cross-compiling Linux build. + - name: Create Build Environment + run: | + sudo apt update + sudo apt-get -y install doxygen vera++ zlib1g-dev libsnappy-dev \ + liblz4-dev g++-multilib libunwind-dev + echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope + + # Use a newer cmake to avoid 32-bit toolchain problems (i#4830). + - name: Setup newer cmake + uses: jwlawson/actions-setup-cmake@v1.8 + with: + cmake-version: '3.19.7' + + - name: Run Suite + working-directory: ${{ github.workspace }} + run: ./suite/runsuite_wrapper.pl automated_ci 64_only + env: + DYNAMORIO_CROSS_AARCHXX_LINUX_ONLY: no + CI_TRIGGER: ${{ github.event_name }} + CI_BRANCH: ${{ github.ref }} + + - name: Send failure mail to dynamorio-devs + if: failure() && github.ref == 'refs/heads/master' + uses: dawidd6/action-send-mail@v2 + with: + server_address: smtp.gmail.com + server_port: 465 + username: ${{secrets.DYNAMORIO_NOTIFICATION_EMAIL_USERNAME}} + password: ${{secrets.DYNAMORIO_NOTIFICATION_EMAIL_PASSWORD}} + subject: | + [${{github.repository}}] ${{github.workflow}} FAILED + on ${{github.event_name}} at ${{github.ref}} + body: | + Github Actions CI workflow run FAILED! + Workflow: ${{github.workflow}}/x86-64 + Repository: ${{github.repository}} + Branch ref: ${{github.ref}} + SHA: ${{github.sha}} + Triggering actor: ${{github.actor}} + Triggering event: ${{github.event_name}} + Run Id: ${{github.run_id}} + See more details on github.com/DynamoRIO/dynamorio/actions/runs/${{github.run_id}} + to: dynamorio-devs@googlegroups.com + from: Github Action CI diff --git a/core/unix/loader.c b/core/unix/loader.c index 828be3d7dfe..1aa4a15cb42 100644 --- a/core/unix/loader.c +++ b/core/unix/loader.c @@ -675,6 +675,16 @@ privload_os_finalize(privmod_t *privmod) #ifndef LINUX return; /* Nothing to do. */ #else + static privmod_t *privmod_ld_linux; + if (strstr(privmod->name, "ld-linux") == privmod->name) { + /* We need to first get the libc version before we clobber ld vars. + * (We could instead look for versioned symbols with "@GLIBC_2.34" in ld + * but we do not have version parsing code in place.) + * We assume ld will not be unloaded. + */ + privmod_ld_linux = privmod; + return; + } if (strstr(privmod->name, "libc.so") != privmod->name) return; os_privmod_data_t *opd = (os_privmod_data_t *)privmod->os_privmod_data; @@ -690,12 +700,54 @@ privload_os_finalize(privmod_t *privmod) */ void (*libc_early_init)(bool) = (void (*)(bool))get_proc_address_from_os_data( &opd->os_data, opd->load_delta, LIBC_EARLY_INIT_NAME, NULL); - if (libc_early_init != NULL) { - LOG(GLOBAL, LOG_LOADER, 2, "%s: calling %s\n", __FUNCTION__, - LIBC_EARLY_INIT_NAME); - (*libc_early_init)(true); + if (libc_early_init == NULL) { + return; } -#endif + /* XXX i#5437: Temporary workaround to avoid a SIGFPE in glibc 2.34+ + * __libc_early_init(). As we cannot let ld/libc initialize their own TLS with the + * current design, we must explicitly initialize a few variables. Unfortunately + * we have to hardcode their offsets, making this fragile. Long-term we should try + * to find a better solution. + */ + /* Do not try to clobber vars unless we have to: get the libc version. */ +# define LIBC_GET_VERSION_NAME "gnu_get_libc_version" + const char *(*libc_ver)(void) = (const char *(*)(void))get_proc_address_from_os_data( + &opd->os_data, opd->load_delta, LIBC_GET_VERSION_NAME, NULL); + if (libc_ver == NULL) + return; + LOG(GLOBAL, LOG_LOADER, 2, "%s: calling %s\n", __FUNCTION__, LIBC_GET_VERSION_NAME); + const char *ver = (*libc_ver)(); + LOG(GLOBAL, LOG_LOADER, 2, "%s: libc version is |%s|\n", __FUNCTION__, ver); + if ((ver[0] == '\0' || ver[0] < '2') || ver[1] != '.' || ver[2] < '3') + return; + if (privmod_ld_linux == NULL) { + SYSLOG_INTERNAL_WARNING("glibc 2.34+ i#5437 workaround failed: missed ld"); + return; + } + os_privmod_data_t *ld_opd = (os_privmod_data_t *)privmod_ld_linux->os_privmod_data; + byte *glro = get_proc_address_from_os_data(&ld_opd->os_data, ld_opd->load_delta, + "_rtld_global_ro", NULL); + if (glro == NULL) { + SYSLOG_INTERNAL_WARNING("glibc 2.34+ i#5437 workaround failed: missed glro"); + return; + } +# define GLRO_dl_tls_static_size_OFFS 0x2a8 +# define GLRO_dl_tls_static_align_OFFS 0x2b0 + size_t val = 4096, written; + if (!safe_write_ex(glro + GLRO_dl_tls_static_size_OFFS, sizeof(val), &val, + &written) || + written != sizeof(val) || + !safe_write_ex(glro + GLRO_dl_tls_static_align_OFFS, sizeof(val), &val, + &written) || + written != sizeof(val)) { + SYSLOG_INTERNAL_WARNING("glibc 2.34+ i#5437 workaround failed: missed write"); + } else { + LOG(GLOBAL, LOG_LOADER, 2, "%s: glibc 2.34+ workaround succeeded\n", + __FUNCTION__); + } + LOG(GLOBAL, LOG_LOADER, 2, "%s: calling %s\n", __FUNCTION__, LIBC_EARLY_INIT_NAME); + (*libc_early_init)(true); +#endif /* LINUX */ } static void