Subject
android_system_properties is a minimal Android system-properties
wrapper that exposes one type, AndroidSystemProperties, with a
get(name) -> Option<String> method. Unlike alternatives that link
statically against Android's libc, this crate uses
dlopen(RTLD_NOLOAD) + dlsym at runtime to discover the
property-system entry points, so a single Rust binary can run against
both pre- and post-Android-L (5.0) devices: the newer callback-based
API (__system_property_find + __system_property_read_callback) is
preferred when present, and the legacy __system_property_get is used
as a fallback. On non-Android targets the crate compiles to a no-op
stub.
Methodology
The published crate contents were compared against the upstream Git
repository at the commit recorded in .cargo_vcs_info.json using
diff -r contents/src vcs/src, which showed no differences. The
single source file (src/lib.rs, 221 LoC) was read in full. The
example (examples/time_zone.rs) and the Cargo.toml were reviewed.
Each unsafe site was inspected for the invariants it relies on and
the documentation those invariants carry; the index arithmetic in the
legacy property-get path was traced manually.
Tools: openvet 0.6.0 for workspace and audit data management;
diff (Apple) for the byte-level comparison; git (2.51) for the
upstream checkout; grep/ripgrep for capability surveys.
Results
The published .crate matches the VCS tree byte-for-byte across
src/; Cargo.toml differences are cargo's standard normalisation.
The crate ships no binary assets (justifying has-binaries), has no
build.rs, no proc-macro library, and no install hooks (justifying
has-build-exec and has-install-exec). It performs no filesystem
operations of its own — dlopen reads a shared library from the
dynamic loader's cache but the crate does no path manipulation or
file I/O (justifying uses-filesystem) — and no network, environment,
JIT, interpreter, concurrency, or cryptographic operations
(justifying uses-network, uses-environment, uses-jit,
uses-interpreter, uses-concurrency, uses-crypto). The crate
implements none of impl-crypto, impl-parser, impl-interpreter,
impl-jit, impl-protocol, impl-datastructure, impl-algorithm, or
impl-concurrency — it is a thin wrapper around three Android libc
functions. dlopen does load executable code, but it is not the kind
of subprocess/dynamic-evaluation usage the audit taxonomy means by
uses-exec (which is reserved for exec()/subprocess patterns).
The crate is essentially an FFI wrapper, and accordingly it does use
unsafe (justifying uses-unsafe). Approximately a dozen unsafe
operations exist: the dlopen/dlsym/dlclose libc calls, four
mem::transmute casts from *const c_void to specific
extern "C" fn signatures, two property-system FFI calls
((find_fn)(...), (read_callback_fn)(...), (get_fn)(...)), a
buffer.set_len() after the legacy property API writes into a
pre-sized Vec, the extern "C" fn C-callback used by the newer API,
and unsafe impl Send + Sync for the wrapper struct.
Soundness of the unsafe was reviewed and is upheld:
dlopen(b"libc.so\0", RTLD_NOLOAD): RTLD_NOLOAD makes this
query-only — it returns the existing handle if libc.so is already
loaded (which it always is on Android), and null otherwise. The
null case is handled.
dlsym results are cast via mem::transmute to extern "C" fn
pointers matching the Android documented signatures. This pattern
is standard for libc-dlsym; the signatures are correct against
Android's system_properties.h.
- The legacy
__system_property_get path allocates a
Vec::with_capacity(PROPERTY_VALUE_MAX) where
PROPERTY_VALUE_MAX = 92 matches Android's PROP_VALUE_MAX,
passes a raw pointer, then
assert!(len as usize <= buffer.capacity()) before
set_len(len). Correctness depends on Android's documented
contract that the function never writes more than PROP_VALUE_MAX
bytes; the assert is defensive but cannot prevent UB if the C
side has already overflowed.
unsafe impl Send + Sync: AndroidSystemProperties is a passive
holder of a libc handle and three function pointers. After
construction it is read-only; the underlying Android libc property
store is documented thread-safe. Sound.
Drop calls dlclose. Because the function pointers live on the
same struct as the handle, no outstanding aliases exist when the
handle is closed.
Two low-severity quality findings (FINDING-1 and FINDING-2) were
recorded:
- FINDING-1 captures a real-world panic risk: the C callback at
src/lib.rs:47-50 calls cvalue.to_str().unwrap(), which panics
across an FFI boundary on non-UTF-8 property values. The legacy
path correctly uses String::from_utf8(buffer).ok(); the callback
path should do the equivalent.
- FINDING-2 records that none of the unsafe blocks (FFI calls,
mem::transmutes, set_len, the C callback signature, the
Send/Sync impls) carry // SAFETY: comments. The invariants
are knowable to a careful reader but should be documented. This
is the basis for declining unsafe-documented.
The crate justifies unsafe-safe (every block was reviewed and the
invariants hold) and unsafe-minimal (unsafe is used only where
strictly necessary — FFI). It declines unsafe-tested: there is no
test suite of any kind. No #[test] modules in src/ (justifying
has-unit-tests), no tests/ directory (justifying
has-integration-tests), no fuzz harness (justifying
has-fuzz-tests), no proptest (justifying has-property-tests). The
crate is presumably exercised in the wild by wgpu and other
downstream consumers (the README notes its scope is limited to
"what's needed by wgpu"), but no in-tree tests exist.
No malicious behaviour was observed (justifying is-benign).
Conclusion
android_system_properties is a small, focused FFI wrapper for
Android's property system, using a sensible dlopen/dlsym pattern to
avoid hard-linking against a libc version. The implementation is
correct under careful review, but two quality issues stand out: a
panic risk in the modern-API callback path (FINDING-1) and a lack
of // SAFETY: documentation on the dozen-or-so unsafe operations
(FINDING-2). Neither is a security or correctness defect for
typical Android system properties, which are ASCII; both should be
addressed for code-review-ability and robustness on edge-case
property values. Suitable for use as-is by callers that only need
standard, well-formed properties.