morningman opened a new pull request, #67877:
URL: https://github.com/apache/doris/pull/67877

   ### What problem does this PR solve?
   
   Related PR: #64093
   
   Problem Summary:
   
   **Every aarch64 BE built from master or branch-4.1 (since 4.1.4-rc01) 
crashes before `main()` on a 64 KiB-page kernel** (Kunpeng 920 + Kylin V10 / 
openEuler / CentOS aarch64, the common ARM server stack). The core from a 4.1.4 
arm64 deployment shows:
   
   ```
   access_mem (addr=0)            src/aarch64/Ginit.c
   is_plt_entry                   src/aarch64/Gstep.c
   _ULaarch64_step
   _ULaarch64_tdep_trace
   unw_backtrace
   <bthread/mutex.cpp static initializer: backtrace(dummy_buf, 4)>   ← bRPC 
"Warm up backtrace before main()"
   __libc_csu_init
   ```
   
   SIGSEGV with `si_addr=0`, AUXV `PAGESZ=65536`, and inside the libunwind 
cursor `ip=0`, `validate=0`, `mem_validate_func=msync_validate`, 
`last_good_addr[]={0,0,0,0}`.
   
   How it happens:
   
   1. #64093 links GNU libunwind statically into the aarch64 BE (it forces 
`USE_UNWIND=ON` on Linux, references `unw_*` from `phdr_cache.cpp` without the 
`__x86_64__` guard, and builds jemalloc with `--enable-prof-libunwind`). 
libunwind's weak `backtrace` alias then replaces glibc's `backtrace()` for 
every caller in the process: bRPC's pre-main warm-up, 
`StackTrace::tryCapture()` on aarch64, jemalloc heap profiling. The 4.1.3 arm64 
package was immune only because it was built with `USE_UNWIND=OFF` (which the 
ARM compilation doc still prescribes and #64093 removed): it has 
`backtrace@GLIBC_2.17` and zero `_ULaarch64_*` symbols.
   2. libunwind 1.6.2 hard-codes `PAGE_SIZE 4096` in `src/aarch64/Ginit.c`. On 
a 64 KiB-page kernel `mincore()`/`msync()` reject its 4 KiB-aligned probes with 
`EINVAL`, so `validate_mem()` fails for 15/16 of all addresses (the core's 
`msync_validate` + empty `last_good_addr` are exactly this).
   3. `unw_step()` therefore gets `unw_is_signal_frame() < 0` for a valid IP, 
takes the "IP points to non-mapped memory, use LR" recovery path and loads 
`uc.regs[30]` — a slot `_Uaarch64_getcontext_trace` never writes (it saves only 
FP/SP/PC). On the reporting host that stack garbage is 0.
   4. `dwarf_step(ip=0)` finds nothing, and the fallback calls `is_plt_entry()` 
with `c->validate` restored to 0, so `access_mem` executes `ldr x8, [x1]` with 
`x1=0`.
   
   The PHDR cache being empty before `main()` is not what breaks this (step 3 
has already poisoned `ip` before any FDE lookup, and the same crash reproduces 
without the Doris hook); changing the cache initialisation order would not fix 
it, and every later `backtrace()` on such a host rolls the same dice.
   
   Fix: **upgrade libunwind 1.6.2 → 1.8.3**, which was never bumped since it 
was introduced in 2023 (#21938):
   
   - 1.7.0+: `unw_page_size = sysconf(_SC_PAGESIZE)` instead of the hard-coded 
4096.
   - 1.8.0+: `src/mi/Gaddress_validator.c` validates through the pipe-write 
probe only, so `mincore`/`msync` alignment no longer matters at all; 
`unw_step()` sets `c->validate = 1` before the DWARF-failure fallback, so a 
garbage IP is rejected instead of dereferenced.
   
   Changes:
   
   - `thirdparty/vars.sh`: libunwind 1.6.2 → 1.8.3 (md5 
`13bc7b41462ac6ea157d350eaf6c1503`).
   - `thirdparty/patches/libunwind-1.8.3-doris-phdr-cache.patch`: the #64093 
hook rebased onto 1.8.3's `as->iterate_phdr_function` call site; semantics 
unchanged (`doris_unwind_iterate_phdr` still takes precedence whenever it is 
linked in). 1.8 also exposes `unw_set_iterate_phdr_function()`, which could 
replace the source patch in a follow-up.
   - `thirdparty/build-thirdparty.sh`: `--disable-tests 
--disable-documentation`; only `libunwind.a` is consumed.
   - `thirdparty/test/libunwind-page-size-test.sh` (+ run at the end of the 
Linux thirdparty CI build): reproduces the failure on any Linux host. An 
`LD_PRELOAD` shim gives libunwind a 64 KiB-page kernel's behaviour (`EINVAL` 
for unaligned `mincore`/`msync`, `sysconf(_SC_PAGESIZE)=65536`), and a program 
shaped like the bRPC warm-up calls `backtrace()` from a constructor with a 
zeroed stack. Against 1.6.2 it segfaults with the customer's exact stack and 
cursor state; against 1.8.3 it passes.
   
   No BE source change: every `unw_*` API the BE, jemalloc and the x86_64 
stack-trace code use is unchanged in 1.8.3, `cxx_exceptions` stays off on 
x86_64/aarch64, and the weak `backtrace` alias is still provided.
   
   Follow-ups outside this PR: the doris-website ARM compilation page still 
says `export USE_UNWIND=OFF` is required, which has been a silent no-op since 
#64093.
   
   ### Release note
   
   Fix the aarch64 BE crashing before `main()` (SIGSEGV in 
`access_mem`/`is_plt_entry` under `unw_backtrace`) on 64 KiB-page kernels such 
as Kunpeng 920 with Kylin, openEuler or CentOS, by upgrading the bundled 
libunwind from 1.6.2 to 1.8.3.
   
   ### Check List (For Author)
   
   - Test
       - [x] Manual test (add detailed scripts or steps below)
           - Symbolised the customer core against the matching 4.1.4 arm64 
`doris_be` (gdb): crash stack, `si_addr=0`, `PAGESZ=65536`, cursor 
`ip=0`/`validate=0`, `mem_validate_func=msync_validate`, `last_good_addr={0}`, 
`uc.regs[30]=0`; `backtrace` == `unw_backtrace` in the 4.1.4 binary vs 
`backtrace@GLIBC_2.17` in 4.1.3.
           - Reproduced the identical crash on a 4 KiB arm64 Linux container by 
running the real 4.1.4 `doris_be --version` under the 64 KiB shim and zeroing 
`uc.regs[30]` in gdb.
           - Built libunwind 1.6.2 (+Doris patch) and 1.8.3 (+rebased patch) 
for aarch64 with the exact recipe from `build_libunwind()`; ran 
`thirdparty/test/libunwind-page-size-test.sh` against both: 1.6.2 FAIL 
(SIGSEGV, same stack), 1.8.3 PASS with and without the Doris hook, and with 
`ip` forced to 0 in gdb.
           - Same build + test on x86_64 (emulated ubuntu-22.04): PASS.
           - The rebased patch applies with both GNU `patch` and Apple `patch`; 
`thirdparty/test/download-thirdparty-*-test.sh`, 
`juicefs-default-mirror-test.sh`, `azure-vcpkg-retry-test.sh` pass; shellcheck 
reports no new findings.
           - Not done here: a Linux `doris_be` rebuilt with 1.8.3 on a real 64 
KiB-page host. That is the one check that still needs an aarch64 machine.
   
   - Behavior changed:
       - [x] Yes. Bundled libunwind 1.6.2 → 1.8.3; libunwind's own test 
programs and man pages are no longer built. Unwinding semantics for the BE 
otherwise unchanged.
   
   - Does this need documentation?
       - [x] No.
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   
   https://claude.ai/code/session_016MdauUJh8AtE8SozZ29Dmv
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to