mirror of
https://github.com/apache/doris.git
synced 2026-11-04 07:31:17 +00:00
### What problem does this PR solve? Issue Number: None Related PR: #65749 Problem Summary: Several FDB-related CloudUT ASAN binaries can terminate with a zero-page SEGV in `libfdb_c.so`. The existing runner fallback from #65749 prevents the overall job from failing when the log contains both ASAN output and `libfdb_c.so`, but it does not fix the binary or guarantee that its GoogleTest cases actually ran. This PR fixes the underlying symbol-resolution failure. ### Root cause 1. CloudUT ASAN executables are compiled against glibc 2.27, where `getentropy` exists, and run on workers with glibc 2.17, where it does not. 2. Compiler-rt links a `getentropy` ASAN interceptor into each executable. `libfdb_c` checks `getentropy` as an optional weak symbol during FDB network initialization, so the interceptor makes the availability check succeed. 3. The interceptor tries to forward the call to libc, but `dlsym(RTLD_NEXT, "getentropy")` resolves to null on glibc 2.17. 4. The call therefore jumps to address zero, producing `AddressSanitizer:DEADLYSIGNAL`, `pc=0`, and a non-zero test-binary result. An A/B diagnostic CloudUT run ([build 52882](http://47.243.177.214:8111/buildConfiguration/SelectdbCore_Cloudut/52882)) observed the full chain: - compile-time glibc 2.27 and runtime glibc 2.17; - native `getentropy` resolved to `__interceptor_trampoline_getentropy`; - `RTLD_NEXT getentropy` resolved to null; - the native probe reproduced the zero-page SEGV; - a probe exporting a strong syscall-backed `getentropy` was called during FDB network startup and completed successfully. Changing `fdb_external_client_directory` and aligning FDB client/server versions were also tested independently; neither changed the failure. This ruled out FDB client selection and protocol-version compatibility. ### Why this fix works - A strong `getentropy` implementation is linked directly into Linux ASAN CloudUT executables that link or initialize FDB. - `ENABLE_EXPORTS` puts the executable symbol in the dynamic symbol table, so `libfdb_c.so` resolves `getentropy` to this implementation instead of the unusable ASAN trampoline. - The implementation calls `SYS_getrandom` directly, avoiding both libc `getentropy` and libc `getrandom` interceptor paths. - It preserves the `getentropy(3)` 256-byte limit, retries interrupted operations, and falls back to `/dev/urandom` when the running kernel reports `ENOSYS`. - The compatibility object is limited to Linux ASAN unit tests. Production binaries, non-ASAN builds, FDB versions, and FDB configuration are unchanged. The BE glibc-compatibility objects cannot be reused directly here because Cloud is a separate CMake project and does not link them. That implementation also delegates to `getrandom`, while this workaround deliberately uses a direct syscall to avoid another interceptor dependency. ### What is changed? - Add a Linux ASAN test-only, syscall-backed `getentropy` implementation. - Export it from the FDB-linked/FDB-initializing CloudUT executables. - Keep the existing FDB client version, external-client configuration, and test runner unchanged. ### Testing - A/B CloudUT build 52882: native probe reproduced the zero-page crash; the strong-symbol probe passed and recorded the FDB network call. - CloudUT [build 53003](http://47.243.177.214:8111/buildConfiguration/SelectdbCore_Cloudut/53003): all four previously crashing binaries returned raw `ret=0`; their 1 + 19 + 22 + 24 FDB tests (66 total) passed without invoking ASAN-result masking. - Apache Doris master CloudUT on this PR: [build 1034381](http://43.132.222.7:8111/viewLog.html?buildId=1034381&buildTypeId=Doris_DorisCloudUt_CloudUt), running. - Local checks: - clang-format 16 dry-run; - C11 `-Wall -Wextra -Werror` syntax checks for both the fallback and `SYS_getrandom` paths; - CMake target-existence check; - `git diff --check`. ### Release note None ### Check List (For Author) - Test - [ ] Regression test - [x] Unit Test - [ ] Manual test (add detailed scripts or steps below) - [ ] No need to test or manual test. Explain why: - [ ] This is a refactor/code format and no logic has been changed. - [ ] Previous test can cover this change. - [ ] No code files have been changed. - [ ] Other reason - Behavior changed: - [x] No. Production binaries and FDB configuration are unchanged. - [ ] Yes. - Does this need documentation? - [x] No. - [ ] Yes. ### Check List (For Reviewer who merge this PR) - [ ] Confirm the release note - [ ] Confirm test cases - [ ] Confirm document - [ ] Add branch pick label
114 lines
3.6 KiB
C
114 lines
3.6 KiB
C
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you 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.
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stddef.h>
|
|
#include <sys/syscall.h>
|
|
#include <unistd.h>
|
|
|
|
#if !defined(SYS_getrandom) && defined(__NR_getrandom)
|
|
#define SYS_getrandom __NR_getrandom
|
|
#endif
|
|
|
|
// Why this compatibility symbol is needed:
|
|
//
|
|
// CloudUT ASAN executables are built against glibc 2.27 and run against glibc
|
|
// 2.17. The ASAN runtime exports a getentropy interceptor, so libfdb_c's weak
|
|
// symbol check concludes that getentropy is available. At runtime, however,
|
|
// the interceptor cannot resolve a next libc implementation (RTLD_NEXT is
|
|
// null on glibc 2.17) and calling it jumps to address zero.
|
|
//
|
|
// This file is linked only into Linux ASAN test executables that initialize
|
|
// the FDB network. Its strong, exported symbol gives libfdb_c a valid target
|
|
// in those processes. Calling the kernel directly is important: forwarding to
|
|
// a libc entropy function could enter the same interceptor path again.
|
|
|
|
// Build headers may define SYS_getrandom even when an older worker kernel does
|
|
// not implement it. Fall back to /dev/urandom only when the syscall reports
|
|
// ENOSYS.
|
|
static int fill_from_urandom(unsigned char* output, size_t length) {
|
|
int fd;
|
|
do {
|
|
fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
|
|
} while (fd < 0 && errno == EINTR);
|
|
if (fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
while (length > 0) {
|
|
const ssize_t bytes_read = read(fd, output, length);
|
|
if (bytes_read > 0) {
|
|
output += bytes_read;
|
|
length -= (size_t)bytes_read;
|
|
continue;
|
|
}
|
|
if (bytes_read < 0 && errno == EINTR) {
|
|
continue;
|
|
}
|
|
|
|
const int saved_errno = bytes_read == 0 ? EIO : errno;
|
|
close(fd);
|
|
errno = saved_errno;
|
|
return -1;
|
|
}
|
|
|
|
return close(fd);
|
|
}
|
|
|
|
int getentropy(void* buffer, size_t length) {
|
|
// Match the getentropy(3) contract. FDB requests small buffers, but keeping
|
|
// the standard 256-byte limit makes this a safe process-wide replacement.
|
|
if (length > 256) {
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
unsigned char* output = (unsigned char*)buffer;
|
|
|
|
#if defined(SYS_getrandom)
|
|
// Use syscall rather than libc getrandom/getentropy so this implementation
|
|
// cannot recurse through an ASAN interceptor.
|
|
while (length > 0) {
|
|
const long bytes_read = syscall(SYS_getrandom, output, length, 0);
|
|
if (bytes_read > 0) {
|
|
output += bytes_read;
|
|
length -= (size_t)bytes_read;
|
|
continue;
|
|
}
|
|
if (bytes_read < 0 && errno == EINTR) {
|
|
continue;
|
|
}
|
|
if (bytes_read < 0 && errno == ENOSYS) {
|
|
break;
|
|
}
|
|
if (bytes_read == 0) {
|
|
errno = EIO;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
if (length == 0) {
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
return fill_from_urandom(output, length);
|
|
}
|