Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 27, 2026

Description

NativeAOT shared libraries on Android crash with SIGSEGV when calling RandomNumberGenerator.GetBytes(). The crypto native library requires JNI_OnLoad to be called by the Android runtime to initialize JNI state and function pointers. Previously, pal_jni_onload.c was only included in shared library builds, leaving NativeAOT scenarios (static linking) without this initialization.

Changes

  • pal_jni_onload.c: Made JNI_OnLoad a weak symbol via __attribute__((weak))

    • Provides default implementation that calls AndroidCryptoNative_InitLibraryOnLoad
    • Allows users to override with strong symbol if custom initialization needed
  • CMakeLists.txt: Added pal_jni_onload.c to NATIVECRYPTO_SOURCES list

    • Ensures weak JNI_OnLoad is available in both shared and static library builds for NativeAOT
    • Eliminates duplication by using the shared sources list for both library targets

Example

// Previously crashed with SIGSEGV due to uninitialized crypto
RandomNumberGenerator.GetBytes(1);

// Now works - JNI_OnLoad called automatically on library load

Testing

Manual testing required on Android 16 emulator with NativeAOT instrumentation test as described in the issue.

Checklist

  • Changes follow repository coding conventions
  • Changes are minimal and surgical
  • No new test infrastructure required (native library initialization)
Original prompt

This section details on the original issue you should resolve

<issue_title>[android][NativeAOT] SIGSEGV in RandomNumberGenerator.GetBytes (CryptoNative_GetRandomBytes) on Android 16 emulator</issue_title>
<issue_description>### Android framework version

net10.0-android

Affected platform version

.NET 10.0.2

Description

Summary

Calling System.Security.Cryptography.RandomNumberGenerator.GetBytes() crashes the process with a native SIGSEGV (null pointer dereference) on an Android 16 arm64 emulator when running an instrumentation test (AndroidJUnitRunner / roidJUnitRunner).

This also breaks any library that calls RandomNumberGenerator internally (e.g. MessagePack on first use).

Expected behavior

RandomNumberGenerator.GetBytes() returns random bytes (or throws a managed exception if unsupported), and the test continues.

Actual behavior

The process crashes with:

  • Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
  • Tombstone shows a null pointer dereference (register x0 == 0 at crash PC)

Reproduction

The repro repository is attached. Run the instrumentation test as described in the repo README.

The minimal trigger is:

using System.Security.Cryptography;

RandomNumberGenerator.GetBytes(1);

Environment

Host

  • macOS (Apple Silicon)
  • .NET host: 10.0.2 (arm64)

dotnet --info (run from my terminal; note: the CLI prints getcwd() failed: Operation not permitted in my environment, but it still reports the host/runtime versions):

Host:
  Version:      10.0.2
  Architecture: arm64
  Commit:       4452502459
  RID:          osx-arm64

.NET SDKs installed:
  8.0.100 [/usr/local/share/dotnet/sdk]
  8.0.303 [/usr/local/share/dotnet/sdk]
  8.0.403 [/usr/local/share/dotnet/sdk]
  9.0.100-rc.2.24474.11 [/usr/local/share/dotnet/sdk]
  9.0.100 [/usr/local/share/dotnet/sdk]
  10.0.100-rc.1.25451.107 [/usr/local/share/dotnet/sdk]
  10.0.102 [/usr/local/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 8.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.7 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.10 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 9.0.0-rc.2.24474.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 9.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 10.0.0-rc.1.25451.107 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 10.0.2 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 8.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.7 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.10 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 9.0.0-rc.2.24473.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 9.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 10.0.0-rc.1.25451.107 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 10.0.2 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

Target / device

  • Android emulator: arm64
  • Android version: Android 16
  • Build fingerprint (from tombstone):
    • google/sdk_gphone64_arm64/emu64a:16/BE4B.251210.005/14574095:user/release-keys

Notes

  • I currently cannot test on a physical Android device (I don’t own one).
  • /dev/urandom works on the emulator (so this doesn’t appear to be “no entropy”):
    • adb shell dd if=/dev/urandom bs=16 count=1 2>/dev/null | hexdump -C succeeds.

Tombstone excerpt (crypto crash)

Full tombstone file is attached in the repo; key excerpt:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/sdk_gphone64_arm64/emu64a:16/BE4B.251210.005/14574095:user/release-keys'
Kernel Release: '6.12.38-android16-5-gbb9513914902-ab13996879-4k'
Revision: '0'
ABI: 'arm64'
Timestamp: 2026-01-14 21:32:17.933551025+0100
Process uptime: 2s
Executable: /system/bin/app_process64
Cmdline: com.example.nativewrapper.test
pid: 20099, tid: 20113, name: roidJUnitRunner  >>> com.example.nativewrapper.test <<<
uid: 10313
tagged_addr_ctrl: 0000000000000001 (PR_TAGGED_ADDR_ENABLE)
pac_enabled_keys: 000000000000000f (PR_PAC_APIAKEY, PR_PAC_APIBKEY, PR_PAC_APDAKEY, PR_PAC_APDBKEY)
esr: 0000000092000006 (Data Abort Exception 0x24)
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000000 (read)
Cause: null pointer dereference
    x0  0000000000000000  x1  000000775ba00cb0  x2  0000000000010006  x3  000000000000000f
    ...
    lr  000000775b6d0420  sp  000000775ba00cb0  pc  000000775b6d02a0  pst 0000000060001000
    esr 0000000092000006

1 total frames
backtrace:
      dotnet/runtime#0 pc 000000000...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes dotnet/runtime#123355

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/dotnet/runtime/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix SIGSEGV in RandomNumberGenerator.GetBytes on Android emulator [Android][NativeAOT] Fix SIGSEGV in RandomNumberGenerator by providing weak JNI_OnLoad symbol Jan 27, 2026
Copilot AI requested a review from jkoritzinsky January 27, 2026 23:40
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
@jkoritzinsky
Copy link
Member

@grendello just to confirm, this won't break the .NET for Android integration, correct? Since the strong symbol there should supersede the weak symbol here.

@grendello
Copy link
Contributor

@grendello just to confirm, this won't break the .NET for Android integration, correct? Since the strong symbol there should supersede the weak symbol here.

I think it should be fine, yeah. We don't link that particular object file into our runtime at all. Even if it did, our strong symbol would override the weak one.

@grendello
Copy link
Contributor

Oh, you put it back in the main archive, just noticed, sorry. Yeah, it should still be fine.

@jkoritzinsky jkoritzinsky marked this pull request as ready for review January 28, 2026 14:50
Copilot AI review requested due to automatic review settings January 28, 2026 14:52
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a SIGSEGV crash in RandomNumberGenerator.GetBytes() on Android when using NativeAOT. The crash occurred because the JNI environment (gJvm) was not initialized when the crypto native library was statically linked into NativeAOT applications. The fix provides a weak JNI_OnLoad symbol that ensures proper initialization while allowing users to override it with custom initialization if needed.

Changes:

  • Made JNI_OnLoad a weak symbol to provide default initialization for statically-linked scenarios
  • Included pal_jni_onload.c in both shared and static library builds by adding it to the common sources list

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/native/libs/System.Security.Cryptography.Native.Android/pal_jni_onload.c Added __attribute__((weak)) to JNI_OnLoad to make it overridable while providing default crypto initialization
src/native/libs/System.Security.Cryptography.Native.Android/CMakeLists.txt Added pal_jni_onload.c to NATIVECRYPTO_SOURCES to ensure it's included in both shared and static library builds

#include "pal_jni.h"

JNIEXPORT jint JNICALL
JNIEXPORT jint JNICALL __attribute__((weak))
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

The placement of __attribute__((weak)) between JNICALL and the function name is unconventional. The standard GCC syntax for weak symbols places the attribute either before the entire function declaration or after the parameter list. Consider moving it to either:

  • Before the declaration: __attribute__((weak)) JNIEXPORT jint JNICALL
  • After the parameter list: JNI_OnLoad(JavaVM *vm, void *reserved) __attribute__((weak))

The current placement may work but deviates from standard patterns seen in the codebase (e.g., src/native/containers/dn-simdhash-utils.c:23).

Suggested change
JNIEXPORT jint JNICALL __attribute__((weak))
__attribute__((weak)) JNIEXPORT jint JNICALL

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants