Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d92ffb3
build: add aarch64-linux-android cross-compile target
Jarred-Sumner Apr 24, 2026
549a9fc
build(android): bun-debug links — PIE + NDK runtimes + bindings fixes
Jarred-Sumner Apr 24, 2026
f9764f8
build(android): force sanitizers off
Jarred-Sumner Apr 24, 2026
491967e
ci: add Android NDK + rustup targets to build images; matrix entries …
Jarred-Sumner Apr 24, 2026
fbb25a5
release(android): wire through compile target, upgrade, npm, install
Jarred-Sumner Apr 24, 2026
631d83f
android: codegen passes TARGET_PLATFORM/ARCH so bundled JS inlines ta…
Jarred-Sumner Apr 24, 2026
5d3a175
android: report "android" in os_name/user-agent/analytics, not "linux"
Jarred-Sumner Apr 24, 2026
95c6328
android: fix event-loop hang, /tmp, DNS default — seccomp EACCES fall…
Jarred-Sumner Apr 24, 2026
c53db69
android: load system CA certs from /system/etc/security/cacerts
Jarred-Sumner Apr 24, 2026
3a76060
android: shell/spawn paths — /system/bin/sh, /data/local/tmp, PATH fa…
Jarred-Sumner Apr 24, 2026
3fe9854
android: node:os — tmpdir, type, machine, graceful empties on EACCES/…
Jarred-Sumner Apr 24, 2026
baf4cb0
android: c-ares — drop leaked init, NO_DFLT_SVR, enable __system_prop…
Jarred-Sumner Apr 24, 2026
a63a68b
android: misc — am start for openURL, eventfd check, memfd EACCES lat…
Jarred-Sumner Apr 24, 2026
059a44e
android: bump WEBKIT_VERSION to a4ddad89, enable prebuilt + CI matrix
Jarred-Sumner Apr 24, 2026
fcf7346
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 24, 2026
565403e
[build linux images] bootstrap: v31 for NDK r27c + android rustup tar…
Jarred-Sumner Apr 24, 2026
8792c32
epoll_kqueue: abort on eventfd failure instead of returning unchecked…
Jarred-Sumner Apr 24, 2026
813b539
usockets: hoist BUN_PANIC to internal.h, use it for eventfd failure
Jarred-Sumner Apr 24, 2026
2b66335
[build images] bootstrap.ps1: v18 (keep image build trigger in HEAD)
Jarred-Sumner Apr 24, 2026
081363a
[build images] root_certs_linux: gate <hash>.0 matching on __ANDROID_…
Jarred-Sumner Apr 24, 2026
616b3e1
[build images] bootstrap: use explicit $rust_home/bin/rustup for andr…
Jarred-Sumner Apr 24, 2026
f42a15a
[build images] os.machine(): scope aarch64 fix to android only (linux…
Jarred-Sumner Apr 24, 2026
4a3ce1c
[build images] fix: cares android netdb_r (rebase casualty) + UBSan d…
Jarred-Sumner Apr 24, 2026
28958b7
[build images] bughunt + review fixes
Jarred-Sumner Apr 24, 2026
b310c0d
[build images] android: create NDK compiler-rt symlinks at image-buil…
Jarred-Sumner Apr 24, 2026
e6c6a72
[build images] android: -nostdinc++ so NDK libc++ #include_next doesn…
Jarred-Sumner Apr 24, 2026
911427a
[build images] Dockerfile: use unversioned clang in buildkite stage (…
Jarred-Sumner Apr 24, 2026
7c3c9fe
[build images] android: -nostdlibinc + rustup target add at build tim…
Jarred-Sumner Apr 24, 2026
fd53f02
[build images] android: force lto=false (no -lto prebuilt) + trim ~1.…
Jarred-Sumner Apr 24, 2026
fdb5f46
[build images] android link: --rtlib=compiler-rt + populate both rt l…
Jarred-Sumner Apr 24, 2026
f0c3159
[build images] android link: --unwindlib=libunwind + -L<ndk-rt>/<arch…
Jarred-Sumner Apr 24, 2026
c44123c
[build images] ci.ts: skip features.json generation for cross-compile…
Jarred-Sumner Apr 24, 2026
380cd90
[build images] cleanup: revert bootstrap.ps1 v18 (no Windows changes)…
Jarred-Sumner Apr 24, 2026
8c1294c
[build linux images] re-trigger: linux-only image rebuild (Windows un…
Jarred-Sumner Apr 25, 2026
a305e1f
[build linux images] ci: retrigger after rebase onto main
Jarred-Sumner Apr 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions .buildkite/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,41 @@ ARG BUILDKITE_AGENT_TAGS
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
&& export PATH=$HOME/.cargo/bin:$PATH \
&& rustup install nightly \
&& rustup default nightly

&& rustup default nightly \
&& rustup target add aarch64-linux-android x86_64-linux-android

# Android NDK — sysroot/libc++/compiler-rt for --abi=android cross-compile.
ARG ANDROID_NDK_VERSION="r27c"
RUN curl -fsSL "https://dl.google.com/android/repository/android-ndk-${ANDROID_NDK_VERSION}-linux.zip" -o /tmp/ndk.zip \
&& unzip -q /tmp/ndk.zip -d /opt \
&& mv /opt/android-ndk-${ANDROID_NDK_VERSION} /opt/android-ndk \
&& rm /tmp/ndk.zip \
# Trim ~1.1GB we don't use (NDK clang/lld, lldb, non-android runtimes) —
# we only need sysroot + android compiler-rt. Dramatically shrinks the
# docker layer / AMI size.
&& rm -rf /opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin \
/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/python3 \
/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/liblldb.so \
/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/*-gnu \
/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/*-musl* \
/opt/android-ndk/simpleperf /opt/android-ndk/shader-tools /opt/android-ndk/sources \
# Symlink NDK compiler-rt builtins + libunwind into host clang's resource
# dir — clang's driver hardcodes <resource-dir>/lib/<triple>/libclang_rt.*
# with no -L fallback. Done at image-build time (root) since the build
# user can't write to /usr.
Comment thread
claude[bot] marked this conversation as resolved.
&& RES=$(clang -print-resource-dir) \
&& NDK_RT=/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/$(ls /opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/)/lib/linux \
&& mkdir -p $RES/lib/linux \
&& for A in aarch64 x86_64; do \
ln -sf $NDK_RT/libclang_rt.builtins-${A}-android.a $RES/lib/linux/; \
mkdir -p $RES/lib/linux/${A}; \
ln -sf $NDK_RT/${A}/libunwind.a $RES/lib/linux/${A}/; \
DIR=$RES/lib/${A}-unknown-linux-android28; \
mkdir -p $DIR; \
ln -sf $NDK_RT/libclang_rt.builtins-${A}-android.a $DIR/libclang_rt.builtins.a; \
ln -sf $NDK_RT/${A}/libunwind.a $DIR/libunwind.a; \
done
ENV ANDROID_NDK_ROOT=/opt/android-ndk

RUN ARCH=$(if [ "$TARGETARCH" = "arm64" ]; then echo "arm64"; else echo "amd64"; fi) && \
echo "Downloading buildkite" && \
Expand Down Expand Up @@ -190,7 +223,8 @@ COPY . /workspace/bun
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
&& export PATH=$HOME/.cargo/bin:$PATH \
&& rustup install nightly \
&& rustup default nightly
&& rustup default nightly \
&& rustup target add aarch64-linux-android x86_64-linux-android

ENV PATH=/root/.cargo/bin:$PATH

Expand Down
17 changes: 15 additions & 2 deletions .buildkite/ci.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
/**
* @typedef {"linux" | "darwin" | "windows"} Os
* @typedef {"aarch64" | "x64"} Arch
* @typedef {"musl"} Abi
* @typedef {"musl" | "android"} Abi
* @typedef {"debian" | "ubuntu" | "alpine" | "amazonlinux"} Distro
* @typedef {"latest" | "previous" | "oldest" | "eol"} Tier
* @typedef {"release" | "assert" | "debug" | "asan"} Profile
Expand Down Expand Up @@ -129,6 +129,10 @@ const buildPlatforms = [
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.23" },
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.23" },
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.23" },
// Android: cross-compiled from glibc amazonlinux via NDK sysroot. Host arch
// matches target arch so only --abi/--target/--sysroot are cross.
{ os: "linux", arch: "aarch64", abi: "android", distro: "amazonlinux", release: "2023", features: ["docker"] },
{ os: "linux", arch: "x64", abi: "android", distro: "amazonlinux", release: "2023", features: ["docker"] },
Comment on lines +132 to +135

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Exclude Android builds from the existing baseline-verification flow.

Adding linux-aarch64-android here also enrolls it in verify-baseline, because that path currently treats every Linux aarch64 build as QEMU-verifiable. That step runs qemu-aarch64-static against a bionic-linked binary without an Android userspace, which does not match the PR's “no emulator-based tests” plan and will fail as soon as this target starts building.

Suggested follow-up outside this hunk
function needsBaselineVerification(platform) {
-  const { os, arch, baseline } = platform;
-  if (os === "linux") return (arch === "x64" && baseline) || arch === "aarch64";
+  const { os, arch, abi, baseline } = platform;
+  if (os === "linux") return abi !== "android" && ((arch === "x64" && baseline) || arch === "aarch64");
   if (os === "windows") return arch === "x64" && baseline;
   return false;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.buildkite/ci.mjs around lines 132 - 135, The new android platform entries
(objects with abi: "android") are being picked up by the verify-baseline step
because the verification logic treats all linux aarch64 builds as
QEMU-verifiable; update the verify-baseline flow (the verify-baseline
job/verifyBaseline function) to explicitly skip platforms where platform.abi ===
"android" (or platform.os === "linux" && platform.abi === "android") so Android
builds are not enrolled in baseline verification, or alternatively add and honor
a skip flag (e.g., platform.skipVerifyBaseline = true) on the Android platform
objects and make verify-baseline check that flag before queuing QEMU
verification.

{ os: "windows", arch: "x64", release: "2019" },
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
{ os: "windows", arch: "aarch64", release: "11" },
Expand Down Expand Up @@ -207,7 +211,9 @@ function getImageKey(platform) {
key += `-with-${features.join("-")}`;
}

if (abi) {
// Android cross-compiles from the same glibc image as gnu (just needs NDK,
// which bootstrap.sh installs on all Linux build images) — no separate image.
if (abi && abi !== "android") {
key += `-${abi}`;
}

Expand Down Expand Up @@ -488,6 +494,10 @@ function getBuildArgs(target, options, mode) {
if (os === "linux") args.push(`--abi=${abi ?? "gnu"}`);
} else if (abi === "musl") {
args.push("--abi=musl");
} else if (abi === "android") {
// Android cross-compiles C++ from a glibc host: arch/abi must be explicit
// (host detection would report the build box's gnu/x64, not the target).
args.push(`--os=${os}`, `--arch=${arch}`, "--abi=android");
}
if (baseline) args.push("--baseline=on");
if (profile === "asan") args.push("--asan=on");
Expand Down Expand Up @@ -598,6 +608,9 @@ function getTargetTriplet(platform) {
if (abi === "musl") {
triplet += "-musl";
}
if (abi === "android") {
triplet += "-android";
}
if (baseline) {
triplet += "-baseline";
}
Expand Down
32 changes: 28 additions & 4 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const BunBuildOptions = struct {
no_llvm: bool,
lto: bool,
override_no_export_cpp_apis: bool,
android_ndk_sysroot: ?[]const u8 = null,

cached_options_module: ?*Module = null,
windows_shim: ?WindowsShim = null,
Expand Down Expand Up @@ -204,6 +205,14 @@ pub fn build(b: *Build) !void {
const no_llvm = b.option(bool, "no_llvm", "Experiment with Zig self hosted backends. No stability guaranteed") orelse false;
const lto = b.option(bool, "lto", "Emit LLVM bitcode for full LTO instead of a native object") orelse false;
const override_no_export_cpp_apis = b.option(bool, "override-no-export-cpp-apis", "Override the default export_cpp_apis logic to disable exports") orelse false;
// Zig does not bundle bionic headers, so translate-c needs the NDK
// sysroot include paths explicitly. The obj's linkLibC() gets bionic
// via `zig build --libc <file>` (b.libc_file), which the build script
// also passes when targeting Android.
const android_ndk_sysroot = b.option([]const u8, "android_ndk_sysroot", "Android NDK sysroot for translate-c headers");
if (abi.isAndroid() and android_ndk_sysroot == null) {
std.debug.panic("-Dandroid_ndk_sysroot is required when targeting Android (zig does not bundle bionic headers)", .{});
}

var build_options = BunBuildOptions{
.target = target,
Expand Down Expand Up @@ -267,6 +276,7 @@ pub fn build(b: *Build) !void {
.enable_tinycc = b.option(bool, "enable_tinycc", "Enable TinyCC for FFI JIT compilation") orelse true,
.use_mimalloc = b.option(bool, "use_mimalloc", "Use mimalloc as default allocator") orelse false,
.llvm_codegen_threads = b.option(u32, "llvm_codegen_threads", "Number of threads to use for LLVM codegen") orelse 1,
.android_ndk_sysroot = android_ndk_sysroot,
};

// zig build obj
Expand Down Expand Up @@ -443,7 +453,7 @@ pub fn build(b: *Build) !void {
}) |t| {
const resolved = t.resolveTarget(b);
step.dependOn(
&b.addInstallFile(getTranslateC(b, resolved, .Debug), b.fmt("translated-c-headers/{s}.zig", .{
&b.addInstallFile(getTranslateC(b, resolved, .Debug, null), b.fmt("translated-c-headers/{s}.zig", .{
resolved.result.zigTriple(b.allocator) catch @panic("OOM"),
})).step,
);
Expand Down Expand Up @@ -608,14 +618,16 @@ const TargetDescription = struct {
os: OperatingSystem,
arch: Arch,
musl: bool = false,
android: bool = false,

fn resolveTarget(desc: TargetDescription, b: *Build) std.Build.ResolvedTarget {
return b.resolveTargetQuery(.{
.os_tag = OperatingSystem.stdOSTag(desc.os),
.cpu_arch = desc.arch,
.cpu_model = getCpuModel(desc.os, desc.arch) orelse .determined_by_arch_os,
.os_version_min = getOSVersionMin(desc.os),
.glibc_version = if (desc.musl) null else getOSGlibCVersion(desc.os),
.glibc_version = if (desc.musl or desc.android) null else getOSGlibCVersion(desc.os),
.abi = if (desc.android) .android else null,
});
}
};
Expand Down Expand Up @@ -659,7 +671,7 @@ fn addMultiCheck(
}
}

fn getTranslateC(b: *Build, initial_target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) LazyPath {
fn getTranslateC(b: *Build, initial_target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, android_ndk_sysroot: ?[]const u8) LazyPath {
const target = b.resolveTargetQuery(q: {
var query = initial_target.query;
if (query.os_tag == .windows)
Expand All @@ -684,6 +696,18 @@ fn getTranslateC(b: *Build, initial_target: std.Build.ResolvedTarget, optimize:

translate_c.addIncludePath(b.path("vendor/zstd/lib"));

if (target.result.abi.isAndroid()) {
const sysroot = android_ndk_sysroot orelse
std.debug.panic("translate-c for Android requires -Dandroid_ndk_sysroot", .{});
const arch_triple = switch (target.result.cpu.arch) {
.aarch64 => "aarch64-linux-android",
.x86_64 => "x86_64-linux-android",
else => |a| std.debug.panic("unsupported Android arch: {s}", .{@tagName(a)}),
};
translate_c.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/usr/include", .{sysroot}) });
translate_c.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/usr/include/{s}", .{ sysroot, arch_triple }) });
}

if (target.result.os.tag == .windows) {
// translate-c is unable to translate the unsuffixed windows functions
// like `SetCurrentDirectory` since they are defined with an odd macro
Expand Down Expand Up @@ -888,7 +912,7 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {

mod.addImport("build_options", opts.buildOptionsModule(b));

const translate_c = getTranslateC(b, opts.target, opts.optimize);
const translate_c = getTranslateC(b, opts.target, opts.optimize, opts.android_ndk_sysroot);
mod.addImport("translated-c-headers", b.createModule(.{ .root_source_file = translate_c }));

const zlib_internal_path = switch (os) {
Expand Down
22 changes: 20 additions & 2 deletions packages/bun-release/src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ export const avx2 =
arch === "x64" &&
((os === "linux" && isLinuxAVX2()) || (os === "darwin" && isDarwinAVX2()) || (os === "win32" && isWindowsAVX2()));

export const abi = os === "linux" && isLinuxMusl() ? "musl" : undefined;
export const abi = os === "android" ? "android" : os === "linux" && isLinuxMusl() ? "musl" : undefined;

export type Platform = {
os: string;
arch: string;
abi?: "musl";
abi?: "musl" | "android";
avx2?: boolean;
bin: string;
exe: string;
Expand Down Expand Up @@ -82,6 +82,24 @@ export const platforms: Platform[] = [
bin: "bun-linux-x64-musl-baseline",
exe: "bin/bun",
},
{
// Node's process.platform is "android" on Android (Termux etc.), not "linux".
// The release asset is still named bun-linux-* for consistency with the
// build triplet, but npm's os field must be "android" for optionalDependency
// resolution to pick it up on-device.
os: "android",
arch: "arm64",
abi: "android",
bin: "bun-linux-aarch64-android",
exe: "bin/bun",
},
{
os: "android",
arch: "x64",
abi: "android",
bin: "bun-linux-x64-android",
exe: "bin/bun",
},
{
os: "win32",
arch: "x64",
Expand Down
39 changes: 33 additions & 6 deletions packages/bun-usockets/src/crypto/root_certs_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,28 @@ static void load_certs_from_directory(const char* dir_path, STACK_OF(X509)* cert
continue;
}

// Check if file has .crt, .pem, or .cer extension
// Accept .crt/.pem/.cer. On Android also accept OpenSSL c_rehash-style names
// (^[0-9a-f]{8}\.[0-9]+$) since /system/etc/security/cacerts/ uses ONLY that
// format. On Debian /etc/ssl/certs/ has both *.pem and <hash>.0 symlinks to
// the same files, so accepting both there would just double-load.
const char* ext = strrchr(entry->d_name, '.');
if (!ext || (strcmp(ext, ".crt") != 0 && strcmp(ext, ".pem") != 0 && strcmp(ext, ".cer") != 0)) {
continue;
if (!ext) continue;
bool ok = strcmp(ext, ".crt") == 0 || strcmp(ext, ".pem") == 0 || strcmp(ext, ".cer") == 0;
#ifdef __ANDROID__
if (!ok) {
size_t prefix = (size_t)(ext - entry->d_name);
if (prefix == 8) {
ok = true;
for (size_t i = 0; i < 8; i++) {
char c = entry->d_name[i];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) { ok = false; break; }
}
for (const char* p = ext + 1; ok && *p; p++) if (*p < '0' || *p > '9') ok = false;
if (ext[1] == '\0') ok = false;
}
}
#endif
if (!ok) continue;
Comment thread
claude[bot] marked this conversation as resolved.

// Build full path
char filepath[PATH_MAX];
Expand Down Expand Up @@ -109,7 +126,16 @@ extern "C" void us_load_system_certificates_linux(STACK_OF(X509) **system_certs)

// Otherwise, load certificates from standard Linux/Unix paths
// These are the common locations for system certificates

#ifdef __ANDROID__
// Android: no bundle files. System CAs are individual hashed PEM files.
static const char* bundle_paths[] = { NULL };
static const char* dir_paths[] = {
"/apex/com.android.conscrypt/cacerts", // API 30+ (mainline updatable)
"/system/etc/security/cacerts", // base system store
"/data/misc/user/0/cacerts-added", // user-installed
NULL
};
Comment on lines +132 to +137

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

User-installed certificates path hardcodes user ID 0.

The path /data/misc/user/0/cacerts-added only loads user-installed CAs for the primary user. On multi-user Android devices, secondary users (uid 10+) have separate cert stores at /data/misc/user/<uid>/cacerts-added that won't be picked up.

This is acceptable for initial Android support since most devices are single-user, but worth documenting as a known limitation.

🔧 Optional: Dynamic user ID resolution
-    "/data/misc/user/0/cacerts-added",      // user-installed
+    // Note: hardcoded to user 0; multi-user devices would need
+    // runtime resolution via getuid() or directory enumeration
+    "/data/misc/user/0/cacerts-added",      // user-installed (primary user only)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bun-usockets/src/crypto/root_certs_linux.cpp` around lines 132 -
137, The hardcoded user path in dir_paths (the static const char* dir_paths[]
array in root_certs_linux.cpp) only covers UID 0 and misses secondary-user
stores; either document this limitation in a comment near dir_paths or replace
the static entry with runtime resolution: detect the active/current user ID (or
glob /data/misc/user/*/) and check /data/misc/user/<uid>/cacerts-added (or
enumerate matches) before loading certs, updating the logic that iterates
dir_paths to consult the resolved paths instead of the literal
"/data/misc/user/0/cacerts-added".

#else
// Common certificate bundle locations (single file with multiple certs)
// These paths are based on common Linux distributions and OpenSSL defaults
static const char* bundle_paths[] = {
Expand All @@ -123,20 +149,21 @@ extern "C" void us_load_system_certificates_linux(STACK_OF(X509) **system_certs)
"/usr/local/share/ca-certificates/ca-certificates.crt", // Custom CA installs
NULL
};

// Common certificate directory locations (multiple files)
// Note: OpenSSL expects hashed symlinks in directories (c_rehash format)
static const char* dir_paths[] = {
"/etc/ssl/certs", // Common location (Debian/Ubuntu with hashed links)
"/etc/pki/tls/certs", // RHEL/Fedora
"/usr/share/ca-certificates", // Debian/Ubuntu (original certs, not hashed)
"/usr/local/share/certs", // FreeBSD
"/etc/openssl/certs", // NetBSD
"/etc/openssl/certs", // NetBSD
"/var/ssl/certs", // AIX
"/usr/local/etc/openssl/certs", // Homebrew OpenSSL on macOS
"/System/Library/OpenSSL/certs", // macOS system OpenSSL (older versions)
NULL
};
#endif

// Try loading from bundle files first
for (const char** path = bundle_paths; *path != NULL; path++) {
Expand Down
13 changes: 10 additions & 3 deletions packages/bun-usockets/src/eventing/epoll_kqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ static int bun_epoll_pwait2(int epfd, struct epoll_event *events, int maxevents,
ret = sys_epoll_pwait2(epfd, events, maxevents, timeout, &mask);
} while (ret == -EINTR);

if (LIKELY(ret != -ENOSYS && ret != -EPERM && ret != -EOPNOTSUPP)) {
if (LIKELY(ret != -ENOSYS && ret != -EPERM && ret != -EOPNOTSUPP && ret != -EACCES)) {
return ret;
}

Expand Down Expand Up @@ -564,7 +564,7 @@ size_t us_internal_accept_poll_event(struct us_poll_t *p) {
struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) {
struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size);
memset(p, 0, sizeof(struct us_internal_callback_t) + ext_size);
int timerfd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
Comment thread
claude[bot] marked this conversation as resolved.
if (timerfd == -1) {
return NULL;
}
Expand Down Expand Up @@ -677,7 +677,14 @@ struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int f
struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size);
memset(p, 0, sizeof(struct us_internal_callback_t) + ext_size);

us_poll_init(p, eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC), POLL_TYPE_CALLBACK);
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
if (efd == -1) {
// eventfd only fails on EMFILE/ENFILE — the loop is unusable without
// wakeup_async, and the sole caller doesn't NULL-check. Crash loudly
// rather than NULL-deref or store -1 as a poll fd.
BUN_PANIC("eventfd() failed during loop init (out of file descriptors?)");
}
Comment thread
claude[bot] marked this conversation as resolved.
us_poll_init(p, efd, POLL_TYPE_CALLBACK);

struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p;
cb->loop = loop;
Expand Down
3 changes: 3 additions & 0 deletions packages/bun-usockets/src/internal/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop,
#define UNLIKELY(cond) __builtin_expect((_Bool)(cond), 0)
#endif

extern void __attribute((__noreturn__)) Bun__panic(const char *message, size_t length);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if __attribute (single underscore) is defined as a macro or used elsewhere in the codebase

# Check for a macro definition of __attribute
rg -n '#define\s+__attribute\b' --type=c --type=cpp

# Check usage patterns of __attribute vs __attribute__ in the repo
echo "=== Usage of __attribute (single underscore) ==="
rg -c '\b__attribute\s*\(' --type=c --type=cpp | head -20

echo "=== Usage of __attribute__ (double underscore) ==="
rg -c '\b__attribute__\s*\(' --type=c --type=cpp | head -20

Repository: oven-sh/bun

Length of output: 1027


🏁 Script executed:

# First, read the actual file content around line 65
cat -n packages/bun-usockets/src/internal/internal.h | sed -n '60,70p'

Repository: oven-sh/bun

Length of output: 551


🏁 Script executed:

# Check if there's any compatibility header or macro definition in bun-usockets
rg -n 'define.*__attribute' packages/bun-usockets/ -t c -t cpp -t h

# Also check the main libusockets.h file to see the pattern
echo "=== Checking libusockets.h ==="
head -20 packages/bun-usockets/src/libusockets.h

Repository: oven-sh/bun

Length of output: 894


🏁 Script executed:

# Check for any include guards or compatibility definitions that might explain single underscore
rg -B2 -A2 '__attribute\s*\(' packages/bun-usockets/src/internal/internal.h

Repository: oven-sh/bun

Length of output: 220


Fix the __attribute syntax error.

Line 65 uses __attribute((__noreturn__)) with a single underscore, but the correct GCC/Clang syntax requires double underscores: __attribute__((__noreturn__)). This will cause a compilation error. Change __attribute( to __attribute__(.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bun-usockets/src/internal/internal.h` at line 65, The declaration
for Bun__panic has a malformed attribute token; update the attribute syntax on
the Bun__panic prototype to use the correct GCC/Clang form by changing the
single-underscore token to a double-underscore token so the attribute reads
__attribute__((__noreturn__)); leave the function name Bun__panic and its
parameters unchanged.

#define BUN_PANIC(message) Bun__panic(message, sizeof(message) - 1)

#ifdef _WIN32
#define IS_EINTR(rc) (rc == SOCKET_ERROR && WSAGetLastError() == WSAEINTR)
#define LIBUS_ERR WSAGetLastError()
Expand Down
2 changes: 0 additions & 2 deletions packages/bun-usockets/src/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@

#if ASSERT_ENABLED
extern const size_t Bun__lock__size;
extern void __attribute((__noreturn__)) Bun__panic(const char* message, size_t length);
#define BUN_PANIC(message) Bun__panic(message, sizeof(message) - 1)
#endif

extern void Bun__internal_ensureDateHeaderTimerIsEnabled(struct us_loop_t *loop);
Expand Down
Loading
Loading