Skip to content

OSInfo.isMusl() misdetects musl on Alpine when /proc/self/map_files exists but contains no musl entries[3.48.0.0+] #1382

@doubcrick

Description

@doubcrick

Summary

On Alpine Linux (musl), sqlite-jdbc 3.48.0.0 incorrectly detects the platform as glibc when /proc/self/map_files exists but does not contain any entries referencing musl.

As a result, the driver loads the Linux/x86_64 (glibc) native library instead of Linux-Musl/x86_64, causing runtime crashes due to missing glibc symbols (e.g. __isnan).

This happens even though:

The system is Alpine (musl libc)
ldd --version reports musl
/proc/self/maps clearly shows musl loader
The jar contains the correct Linux-Musl/x86_64 native library

Environment

sqlite-jdbc: 3.48.0.0
OS: Alpine Linux
libc: musl 1.1.24
Kernel: (two different Kubernetes nodes, behavior differs)
Java: OpenJDK 8 (running via javaagent in target JVM)
Container runtime: Kubernetes (multi-node cluster)

Observed Behavior

On one node:

OSInfo.isMusl() -> falseOSInfo.getNativeLibFolderPathForCurrentOS() -> Linux/x86_64

On another node:

OSInfo.isMusl() -> trueOSInfo.getNativeLibFolderPathForCurrentOS() -> Linux-Musl/x86_64

Both nodes:

Same container image
Same jar
Same musl version
Same /lib/ld-musl-x86_64.so.1

Root Cause Analysis

Current implementation (3.48.0.0):
java

public static boolean isMusl() { Path mapFilesDir = Paths.get("/proc/self/map_files"); try (Stream dirStream = Files.list(mapFilesDir)) { return dirStream .map(path -> { try { return path.toRealPath().toString(); } catch (IOException e) { return ""; } }) .anyMatch(s -> s.toLowerCase().contains("musl")); } catch (Exception ignored) { return isAlpineLinux(); }}

Problem

If:

/proc/self/map_files exists
But its entries do not resolve to paths containing "musl"

Then:

Files.list() succeeds
.anyMatch("musl") returns false
No exception is thrown
isAlpineLinux() is never called
isMusl() incorrectly returns false

This causes the driver to load:

Linux/x86_64/libsqlitejdbc.so

instead of:

Linux-Musl/x86_64/libsqlitejdbc.so

On musl systems, this leads to:

Error relocating ... libsqlitejdbc.so: __isnan: symbol not found

And later crashes in JNI calls (e.g. NativeDB.bind_double).
Why This Happens Only On Some Nodes

On some kernels:

/proc/self/map_files

Exists but contains only anonymous mappings
Does not expose musl paths

On other nodes:

/proc/self/map_files is empty or inaccessible
Files.list() throws
Fallback to isAlpineLinux() works correctly

Thus behavior depends on kernel and container runtime, not the image.
Suggested Fix

The current logic treats:

"map_files exists but no musl found"

as equivalent to:

"not musl"

This is unsafe.
Recommended change:

If:

/proc/self/map_files exists
But no entry contains "musl"

Then fallback to isAlpineLinux() instead of returning false.

Example:
java

boolean found = dirStream .map(...) .anyMatch(...);if (!found) { return isAlpineLinux();}return true;

Alternatively:

Use /proc/self/maps instead
Or check for existence of /lib/libc.musl-*.so*
Or rely more strongly on isAlpineLinux()

Impact

This issue causes:

Silent loading of wrong native library
JNI crashes
Difficult-to-debug production failures
Behavior differences across Kubernetes nodes with identical images

Workaround

Set JVM property to force musl native loading:

-Dorg.sqlite.lib.path=/path/to/Linux-Musl/x86_64

Conclusion

OSInfo.isMusl() should not rely solely on /proc/self/map_files presence and contents, as behavior varies across kernels and container environments.

This leads to incorrect platform detection and loading of incompatible native libraries.

/ # ls /proc/self/map_files
55a9c7a25000-55a9c7a31000 55a9c7acd000-55a9c7aee000 55a9c7af3000-55a9c7af4000 7faa71e15000-7faa71e5c000 7faa71e91000-    7faa71e92000
55a9c7a31000-55a9c7acd000 55a9c7aef000-55a9c7af3000 7faa71e00000-7faa71e15000 7faa71e5c000-7faa71e90000 7faa71e92000-7faa71e93000
/ #

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions