diff --git a/.github/workflows/allocator-diagnostics.yml b/.github/workflows/allocator-diagnostics.yml new file mode 100644 index 0000000..b816b6d --- /dev/null +++ b/.github/workflows/allocator-diagnostics.yml @@ -0,0 +1,87 @@ +name: Allocator diagnostics + +on: + pull_request: + branches: [ main, master ] + push: + branches: [ main, master ] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: allocator-diagnostics-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + audit: + name: Audit allocations (${{ matrix.os }}) + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, macos-26, macos-15-intel, windows-latest ] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + submodules: recursive + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 17 + 21 + - uses: actions/setup-python@v7 + with: + python-version: '3.12' + - uses: gradle/actions/setup-gradle@v5 + - name: Set up Windows native tools + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + path-type: inherit + install: mingw-w64-x86_64-gcc mingw-w64-x86_64-make make perl + - name: Install pinned build tools + run: python -m pip install --only-binary=cmake -r tools/native-build-requirements.txt + - name: Trace native allocations during DTLS transfers + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + ./gradlew --no-daemon --max-workers=2 -PnativeAllocatorDiagnostics=true nativeAllocatorDiagnostics + python tools/test_allocator_audit.py + binary=build/dockcross/host/native/libdatachannel-java.so + if [[ "$RUNNER_OS" == macOS ]]; then binary=build/dockcross/host/native/libdatachannel-java.dylib; fi + python tools/audit_allocator_imports.py "$binary" --output build/reports/allocator-diagnostics/imports.json + ./gradlew --no-daemon --max-workers=2 test + python tools/audit_allocator_imports.py "$binary" --mode normal --output build/reports/allocator-diagnostics/normal-imports.json + - name: Trace Windows native allocations during DTLS transfers + if: runner.os == 'Windows' + shell: msys2 {0} + env: + CC: gcc + CXX: g++ + CMAKE_GENERATOR: MinGW Makefiles + run: | + set -euo pipefail + export LIBDATACHANNEL_BASH="$(cygpath -m /usr/bin/bash.exe)" + ./gradlew --no-daemon --max-workers=2 -PnativeAllocatorDiagnostics=true nativeAllocatorDiagnostics + python tools/test_allocator_audit.py + binary=build/dockcross/host/native/libdatachannel-java.dll + python tools/audit_allocator_imports.py "$binary" --output build/reports/allocator-diagnostics/imports.json + ./gradlew --no-daemon --max-workers=2 test + python tools/audit_allocator_imports.py "$binary" --mode normal --output build/reports/allocator-diagnostics/normal-imports.json + - name: Save allocation counters, import audits and negative controls + if: always() + uses: actions/upload-artifact@v6 + with: + name: allocator-diagnostics-${{ matrix.os }} + path: | + build/reports/allocator-diagnostics/ + build/reports/tests/nativeAllocatorDiagnostics/ + build/test-results/nativeAllocatorDiagnostics/ + if-no-files-found: warn diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 87ec6bc..806b4a8 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -2,13 +2,136 @@ name: Build on: push: - branches: [ '*' ] + branches: [ '**' ] tags: [ '*' ] pull_request: - branches: [ main ] + branches: [ main, master ] workflow_dispatch: jobs: + test-linux: + name: Test Linux JNI and packaging + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + submodules: recursive + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 17 + 21 + - uses: actions/setup-python@v7 + with: + python-version: '3.12' + - uses: gradle/actions/setup-gradle@v5 + - name: Install native tools + run: python -m pip install --only-binary=cmake -r tools/native-build-requirements.txt + - name: Build and test actual JNI + run: ./gradlew --no-daemon --max-workers=2 test packageNativeSources + - name: Check native validators and source packaging + run: | + python tools/test_macos_native.py + python tools/test_private_dependencies.py + python tools/test_package_native_sources.py + - name: Upload test reports + if: always() + uses: actions/upload-artifact@v6 + with: + name: linux-jni-tests + path: | + build/reports/tests/ + build/test-results/ + build/source-archives/ + + build-android: + name: Build Android platform allocator + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 17 + 21 + - uses: gradle/actions/setup-gradle@v5 + - name: Build Android ARM64 JNI without mimalloc + run: ./gradlew --no-daemon --max-workers=2 :compileNativeForAndroidArm64V8a + - uses: actions/upload-artifact@v6 + with: + name: libdatachannel-android-arm64 + path: build/dockcross/android-arm64-v8a/native/libdatachannel-java.so + if-no-files-found: error + + test-windows: + name: Test Windows JNI + runs-on: windows-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + submodules: recursive + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 17 + 21 + - uses: actions/setup-python@v7 + with: + python-version: '3.12' + - uses: gradle/actions/setup-gradle@v5 + - uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + path-type: inherit + install: mingw-w64-x86_64-gcc mingw-w64-x86_64-make make perl + - name: Install native tools + shell: pwsh + run: python -m pip install --only-binary=cmake -r tools/native-build-requirements.txt + - name: Build actual Windows JNI + shell: msys2 {0} + env: + CC: gcc + CXX: g++ + CMAKE_GENERATOR: MinGW Makefiles + run: | + export LIBDATACHANNEL_BASH="$(cygpath -m /usr/bin/bash.exe)" + ./gradlew --no-daemon --max-workers=2 compileNativeForHost + for library in build/dockcross/host/native/*.dll; do + echo "$library" + objdump -p "$library" > "$library.imports.txt" + sed -n '/DLL Name:/p' "$library.imports.txt" + done + if sed -n '/The Import Tables/,/The Export Tables/p' build/dockcross/host/native/libdatachannel-java.dll.imports.txt | grep -E '[[:space:]]DllMain(@[0-9]+)?$'; then + echo '::error::JNI must own its DLL entry point instead of importing the allocator entry point' + exit 1 + fi + - name: Test actual Windows JNI + shell: msys2 {0} + env: + CC: gcc + CXX: g++ + CMAKE_GENERATOR: MinGW Makefiles + run: | + export LIBDATACHANNEL_BASH="$(cygpath -m /usr/bin/bash.exe)" + ./gradlew --no-daemon --max-workers=2 test + - name: Upload test reports + if: always() + uses: actions/upload-artifact@v6 + with: + name: windows-jni-tests + path: | + build/reports/tests/ + build/test-results/ + build/dockcross/host/native/*.dll + build/dockcross/host/native/*.dll.imports.txt + build-macos: name: Build macOS Native Libraries strategy: @@ -77,7 +200,6 @@ jobs: name: libdatachannel-${{ matrix.classifier }} path: | build/dockcross/host/native/libdatachannel-java.dylib - build/dockcross/host/native/libdatachannel_mimalloc*.dylib retention-days: 30 if-no-files-found: error @@ -85,6 +207,9 @@ jobs: needs: - build-macos - build-ios + - build-android + - test-linux + - test-windows runs-on: ubuntu-latest environment: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && 'sonatype-central' || null }} steps: @@ -129,7 +254,7 @@ jobs: -Plibdatachannel.macos-x86_64.prebuilt-path=artifacts/macos-x86_64/libdatachannel-java.dylib -Plibdatachannel.ios.prebuilt-path=artifacts/ios ) - ./gradlew --no-configuration-cache :githubActions "${prebuilts[@]}" + ./gradlew --no-configuration-cache :githubActions :packageNativeForMacosArm64 :packageNativeForMacosX8664 :packageNativeForIos "${prebuilts[@]}" env: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.gitmodules b/.gitmodules index b7bd4b7..99146e7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "jni/libdatachannel"] path = jni/libdatachannel url = https://github.com/paullouisageneau/libdatachannel.git - branch = v0.24.1 + branch = v0.24.6 [submodule "jni/cmake-conan"] path = jni/cmake-conan url = https://github.com/conan-io/cmake-conan.git diff --git a/README.md b/README.md index 0a07f7f..26e33aa 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is a fork of [libdatachannel](https://github.com/paullouisageneau/libdatach The fork diverges from the original repository in few key areas: - Direct ByteBuffer allocation can be wired to a custom allocator -- Mimalloc (w/ MI_SECURE) for internal allocations on the jni binding side. +- Private secure mimalloc for desktop JNI, libdatachannel and embedded native dependencies. Android and iOS use the platform allocator. - JUL instead of SLF4J for logging. @@ -35,7 +35,7 @@ implementation("org.ngengine:libdatachannel-java:0.24.1.nge8") implementation("org.ngengine:libdatachannel-java-ios:0.24.1.nge8") ``` -The iOS module bundles `LibDataChannelJava.xcframework`, -`LibSSL.xcframework`, and `LibCrypto.xcframework` under `lib/ios`, so -libJGLIOS can link the correct device or simulator slices from the runtime -classpath. +The iOS module bundles `LibDataChannelJava.xcframework` under `lib/ios`, +including private OpenSSL code, so libJGLIOS can link the correct device or +simulator slice from the runtime classpath. Separate OpenSSL frameworks are +not required. diff --git a/SOURCE-BUILD.txt b/SOURCE-BUILD.txt new file mode 100644 index 0000000..155b545 --- /dev/null +++ b/SOURCE-BUILD.txt @@ -0,0 +1,75 @@ +Native source build + +The native-sources ZIP contains the binding, initialized native submodules, +generated JNI sources, build scripts and dependency notices. SOURCE-REVISIONS.json +records the checkout revisions, whether each source tree was modified, and the +SHA-256 digest of every included source file. Local archives include current +tracked changes and non-ignored new source files. Release deployment requires +clean committed repositories. + +Install a Java 21 JDK and Python 3.9 or newer. Create a Python virtual environment +and install tools/native-build-requirements.txt. Add its bin directory (Scripts +on Windows) to PATH. +Run ./gradlew test to build and exercise the host JNI library, including native +callback thread lifetime and bidirectional IPv4/IPv6 ICE tests. Windows host +builds use the MinGW toolchain through MSYS2. In an MSYS2 MINGW64 shell with gcc, +make and perl installed, select its shell and native compiler before Gradle: + +export LIBDATACHANNEL_BASH="$(cygpath -m /usr/bin/bash.exe)" +export CC=gcc CXX=g++ CMAKE_GENERATOR="MinGW Makefiles" +./gradlew test + +Host and desktop classifiers contain one JNI library with private static OpenSSL +and mimalloc. Native Bootstrap loads that single resource; no allocator sidecar +or dependent-DLL preload is required. Other platform classifiers are built +through the targets in build.gradle.kts and .github/workflows/gradle.yml. + +libdatachannel is pinned to official release v0.24.6 at +6b1e2e620f1e37f0eafeee702eaea0043cb305fd. No local DTLS queue patch is applied. +The submodule tree includes its own exact dependency source revisions. +jni/conanfile.py specifies the OpenSSL dependency. jni/CMakeLists.txt specifies +mimalloc and the private allocator build settings. Conan and CMake retrieve +those dependencies from their declared upstream sources when absent locally. + +OpenSSL is linked statically into JNI on every platform; desktop builds also +embed secure mimalloc statically without exporting allocator symbols. Only JNI entry points +are exported; OpenSSL and libdatachannel implementation symbols stay private. +Desktop classifiers contain JNI only, with no shared libssl/libcrypto/mimalloc. +JNI, libdatachannel, libjuice and usrsctp use the private allocator for C heap +requests and C++ new/delete, including internal message buffers. OpenSSL's memory +callbacks are installed before its initialization and must succeed. C++ runtimes +are embedded privately too, so strings, streams and other out-of-line runtime +operations allocate and release within the same heap. macOS builds the static +LLVM libc++/libc++abi 20.1.8 runtime from SHA-256-verified official source archives; +Linux and MinGW use their toolchain's static GNU C++ runtime. Mimalloc is compiled +as C++ so failed throwing new calls preserve std::bad_alloc semantics. Implementation +symbols are localized by the JNI export allow-list; no process allocator hooks, +DYLD interposition, malloc-zone replacement or Windows CRT redirection are enabled. +MinGW static-runtime C allocation calls and import-address references resolve +to private definitions at link time; the process CRT is not patched or hooked. +The Java setAllocator callback retains ownership of application-facing buffers. +Platform API results keep their corresponding platform release functions. +Linux uses mimalloc's local-dynamic thread-local storage mode so JNI can be +loaded after JVM startup without relying on reserved static TLS space. +OpenSSL is built without dynamic modules, engines or automatic openssl.cnf +loading, keeping this copy independent of other OpenSSL users in the process. +TLS/DTLS and their default algorithms remain enabled. The linker selects needed +archive members and discards unused sections where supported. + +iOS and Android do not compile or link mimalloc; native C/C++ heap requests use +the platform allocator. getInnerAllocator reports passthrough on these platforms. +iOS packages one LibDataChannelJava xcframework containing JNI, libdatachannel +and OpenSSL. A relocatable link makes implementation definitions +local or private before packaging the static archive. LibSSL/LibCrypto xcframeworks are not +required. Prebuilt iOS metadata must declare privateOpenSSL=true and allocator=platform; older prebuilts +must be rebuilt. Dependency licenses and corresponding-source notices still +apply to code incorporated statically. + +For a Git checkout, initialize submodules recursively before building. A source +archive already contains their files and can build without Git metadata. Creating +a new revision manifest or publishing release sources requires a Git checkout. + +To exercise a local Native Bootstrap checkout, add +-PnativeBootstrapDir=/absolute/path/to/native-bootstrap to the Gradle command. +This substitutes all four Bootstrap modules from source for local verification; +ordinary consumers continue resolving the published dependency coordinates. diff --git a/SOURCE-NOTICES.txt b/SOURCE-NOTICES.txt new file mode 100644 index 0000000..bc541f1 --- /dev/null +++ b/SOURCE-NOTICES.txt @@ -0,0 +1,54 @@ +libdatachannel-java and native dependency notices + +The binding's root LICENSE is Mozilla Public License 2.0. Maven metadata +describes that license. Third-party libraries retain their individual licenses, +copied without alteration under META-INF/licenses in the published JARs. + +Binding: https://github.com/NostrGameEngine/libdatachannel-java +Derived from the original Java binding by Phillip Schichtel and contributors: +https://github.com/pschichtel/libdatachannel-java +The binding retains the original MPL-2.0 license. + +The native-sources artifact contains the binding and initialized native source +submodules. SOURCE-REVISIONS.json records their revisions and exact file hashes. + +libdatachannel: https://github.com/paullouisageneau/libdatachannel +MPL-2.0, official v0.24.6 at 6b1e2e620f1e37f0eafeee702eaea0043cb305fd. +Its nested submodules pin libjuice (MPL-2.0), usrsctp (BSD), plog (MIT), +nlohmann/json (MIT), and libsrtp (BSD). Those source trees and their license +files are included in the native-sources artifact, with exact revisions. + +mimalloc: https://github.com/microsoft/mimalloc/tree/v2.5.2 +MIT, version 2.5.2. CMake verifies the SHA-256 digest of the official tag archive. The JNI build selects +secure mode and embeds the static allocator with private symbols on desktop. +JNI, libdatachannel, libjuice, usrsctp and private OpenSSL use this heap. Process +allocator overrides and Windows CRT redirection remain disabled. Android and iOS +use their platform allocators and do not incorporate mimalloc. No shared mimalloc +sidecar is distributed. + +LLVM libc++ and libc++abi: https://github.com/llvm/llvm-project/tree/llvmorg-20.1.8 +Apache-2.0 with LLVM exceptions and the additional notices in their LICENSE.TXT +files. macOS desktop builds embed private static runtimes. The build recipe pins +the official 20.1.8 release archives and verifies each SHA-256 digest, including +the LLVM CMake build modules. No LLVM compiler is built or distributed. + +GNU C++ runtime: https://gcc.gnu.org/onlinedocs/libstdc++/manual/license.html +GPL-3.0 with GCC Runtime Library Exception 3.1. Linux and MinGW desktop builds +embed their toolchain's static runtime, with implementation symbols kept private. +The runtime exception permits distribution with independently licensed modules. + +Winpthreads: https://github.com/mingw-w64/mingw-w64/tree/master/mingw-w64-libraries/winpthreads +MIT and BSD notices in COPYING, reproduced in licenses/winpthreads-MIT-BSD.txt. +MinGW Windows builds embed the toolchain's static POSIX thread runtime. + +OpenSSL: https://github.com/openssl/openssl/tree/openssl-3.6.0 +Apache-2.0, version 3.6.0 from the pinned Conan recipe. The Conan package records +the official release source archive and its digest. All platforms embed OpenSSL +statically with private symbols. No libssl/libcrypto shared libraries or +separate OpenSSL xcframeworks are distributed in the classifiers. + +Native Bootstrap: https://github.com/jMonkeyEngine/native-bootstrap +MIT, org.jmonkeyengine:native-loader and native-os version 0.1.2, with their +transitive modules. These Java libraries remain external Maven dependencies. + +Build dependencies and platform tools retain their own licenses. diff --git a/arch-detect/build.gradle.kts b/arch-detect/build.gradle.kts index 0e813d0..ff5a3bb 100644 --- a/arch-detect/build.gradle.kts +++ b/arch-detect/build.gradle.kts @@ -32,6 +32,7 @@ tasks.jar.configure { val classifier = jar.classifier ?: continue from(zipTree(jar.file)) { include("native/*.so") + include("native/*.so.*") include("native/*.dll") include("native/*.dylib") into(classifier) @@ -43,4 +44,4 @@ publishing.publications.withType().configureEach { pom { description = "${rootProject.description} The ${project.name} module bundles all architectures and allows runtime architecture detection." } -} \ No newline at end of file +} diff --git a/build.gradle.kts b/build.gradle.kts index acdfe0f..8ef46d7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,6 @@ import tel.schich.dockcross.execute.RemoteSshRunner import tel.schich.dockcross.tasks.DockcrossRunTask import java.net.URI import java.nio.file.Files -import java.nio.file.Paths plugins { id("tel.schich.libdatachannel.convention.common") @@ -31,9 +30,13 @@ fun extractLibDataChannelVersion(): String { fun produceVersion(): String { val libDataChannelVersion = extractLibDataChannelVersion() - val hasTags = project.providers.exec { - commandLine("git", "tag") - }.standardOutput.asText.get().trim().isNotEmpty() + val hasTags = try { + project.providers.exec { + commandLine("git", "tag") + }.standardOutput.asText.get().trim().isNotEmpty() + } catch (e: Exception) { + false + } val defaultVersion = "$libDataChannelVersion.0-SNAPSHOT" if (!hasTags) { return defaultVersion @@ -119,6 +122,42 @@ tasks.compileJava.configure { options.headerOutputDirectory = jniPath.dir("generated") } +val nativeSourcesArchive = layout.buildDirectory.file("source-archives/libdatachannel-native-sources.zip") +val packageNativeSources by tasks.registering(Exec::class) { + group = "build" + description = "Packages the current binding and initialized native source trees with their hashes" + dependsOn(tasks.compileJava) + outputs.file(nativeSourcesArchive) + // Git state and generated JNI inputs must be checked even after a previous archive. + outputs.upToDateWhen { false } + commandLine("python3", "tools/package_native_sources.py", "--root", projectDir, + "--output", nativeSourcesArchive.get().asFile) +} + +val verifyReleaseSources by tasks.registering(Exec::class) { + group = "verification" + dependsOn(tasks.compileJava) + commandLine("python3", "tools/package_native_sources.py", "--root", projectDir, + "--check-clean") +} + +allprojects { + plugins.withId("maven-publish") { + tasks.withType().configureEach { + if (!isSnapshot) dependsOn(verifyReleaseSources) + } + extensions.configure { + publications.withType().configureEach { + artifact(nativeSourcesArchive) { + classifier = "native-sources" + extension = "zip" + builtBy(packageNativeSources) + } + } + } + } +} + val nativeGroup = "native" val ci = System.getenv("CI") != null val buildReleaseBinaries = project.findProperty("libdatachannel.build-release-binaries") @@ -213,9 +252,6 @@ fun Jar.baseConfigure(compileTask: TaskProvider, buildOutputDi include("native/libdatachannel-java.so") include("native/libdatachannel-java.dll") include("native/libdatachannel-java.dylib") - include("native/libdatachannel_mimalloc*.so") - include("native/libdatachannel_mimalloc*.dylib") - include("native/libdatachannel_mimalloc*.dll") includeEmptyDirs = false eachFile { val fileName = relativeSourcePath.lastName @@ -229,10 +265,17 @@ fun Jar.baseConfigure(compileTask: TaskProvider, buildOutputDi val dockcrossOutputDir: Directory = project.layout.buildDirectory.get().dir("dockcross") val nativeForHostOutputDir: Directory = dockcrossOutputDir.dir("host") +val allocatorDiagnosticsEnabled = providers.gradleProperty("nativeAllocatorDiagnostics") + .map { it.toBooleanStrict() }.orElse(false) val compileNativeForHost by tasks.registering(DockcrossRunTask::class) { baseConfigure(nativeForHostOutputDir, BuildTarget(image = null, family = "host", classifier = "host")) + script = listOf(listOf(providers.environmentVariable("LIBDATACHANNEL_BASH").orElse("bash").get(), + "../../../../jni/build.sh")) unsafeWritableMountSource = true runner(NonContainerRunner) + extraEnv.put("BUILD_JNI_TESTS", "ON") + extraEnv.put("ALLOCATOR_DIAGNOSTICS", if (allocatorDiagnosticsEnabled.get()) "ON" else "OFF") + inputs.property("allocatorDiagnostics", allocatorDiagnosticsEnabled) } val packageNativeForHost by tasks.registering(Jar::class) { @@ -324,7 +367,12 @@ for (target in targets) { val prebuiltPath = project.findProperty("libdatachannel.${target.classifier}.prebuilt-path") ?.toString() ?.ifBlank { null } - ?.let { Paths.get(it).toFile() } + ?.let { project.file(it) } + val prebuiltNativeFiles = prebuiltPath?.let { path -> + project.fileTree(path.parentFile) { + include(path.name) + } + } val packageTaskName = "packageNativeFor$taskSuffix" val packageNative = if (prebuiltPath == null) { @@ -367,11 +415,7 @@ for (target in targets) { } else { tasks.register(packageTaskName, Jar::class) { group = nativeGroup - from(prebuiltPath.parentFile) { - include(prebuiltPath.name) - include("libdatachannel_mimalloc*.so") - include("libdatachannel_mimalloc*.dylib") - include("libdatachannel_mimalloc*.dll") + from(requireNotNull(prebuiltNativeFiles)) { includeEmptyDirs = false eachFile { val fileName = when (target.family) { @@ -393,6 +437,14 @@ for (target in targets) { if (!prebuiltPath.exists()) { throw GradleException("Prebuilt binary for $target does not exist: $prebuiltPath") } + if (target.family != "macos") { + project.serviceOf().exec { + commandLine("cmake", "-DINPUT_FILE=${prebuiltPath.absolutePath}", + "-DPLATFORM=${if (target.family == "windows") "Windows" else "Linux"}", + "-DNM_TOOL=nm", "-DOBJDUMP_TOOL=objdump", "-P", + rootProject.file("jni/cmake/verify_private_dependencies.cmake").absolutePath) + } + } } } } @@ -404,9 +456,14 @@ for (target in targets) { packageNative.configure { inputs.file(nativeChecker) doFirst { - val stagedDirectory = prebuiltPath?.parentFile ?: outputDir.dir("native").asFile execOps.exec { - commandLine("python3", nativeChecker.asFile, nativeArchitecture, stagedDirectory) + if (prebuiltPath == null) { + commandLine("python3", nativeChecker.asFile, nativeArchitecture, outputDir.dir("native").asFile) + } else { + commandLine(listOf("python3", nativeChecker.asFile, nativeArchitecture, + prebuiltPath.absoluteFile, "--prebuilt") + + prebuiltNativeFiles!!.files.sortedBy { it.path }.map { it.absolutePath }) + } } } doLast { @@ -432,7 +489,7 @@ val iosOutputDir: Directory = dockcrossOutputDir.dir("ios") val iosPrebuiltPath = project.findProperty("libdatachannel.ios.prebuilt-path") ?.toString() ?.ifBlank { null } - ?.let { Paths.get(it).toFile() } + ?.let { project.file(it) } val compileNativeForIos by tasks.registering(DockcrossRunTask::class) { baseConfigure( iosOutputDir, @@ -457,7 +514,8 @@ val packageNativeForIos by tasks.registering(Jar::class) { if (iosPrebuiltPath == null) { dependsOn(compileNativeForIos) from(iosOutputDir) { - include("native/lib/ios/**") + include("native/lib/ios/LibDataChannelJava.xcframework/**") + include("native/lib/ios/LibDataChannelJava.xcframework.json") includeEmptyDirs = false eachFile { relativePath = RelativePath.parse(true, relativePath.pathString.removePrefix("native/")) @@ -468,6 +526,13 @@ val packageNativeForIos by tasks.registering(Jar::class) { if (!iosPrebuiltPath.isDirectory) { throw GradleException("Prebuilt iOS path does not exist or is not a directory: $iosPrebuiltPath") } + val metadata = if (iosPrebuiltPath.name.endsWith(".xcframework")) + File(iosPrebuiltPath.parentFile, "${iosPrebuiltPath.name}.json") + else File(iosPrebuiltPath, "LibDataChannelJava.xcframework.json") + if (!metadata.isFile || + (groovy.json.JsonSlurper().parse(metadata) as? Map<*, *>)?.let { it["privateOpenSSL"] == true && it["allocator"] == "platform" && it["privateMimalloc"] != true } != true) { + throw GradleException("Prebuilt iOS binding must embed private OpenSSL and use the platform allocator; rebuild its xcframework") + } } if (iosPrebuiltPath.name.endsWith(".xcframework")) { from(iosPrebuiltPath.parentFile) { @@ -478,8 +543,8 @@ val packageNativeForIos by tasks.registering(Jar::class) { } } else { from(iosPrebuiltPath) { - include("*.xcframework/**") - include("*.xcframework.json") + include("LibDataChannelJava.xcframework/**") + include("LibDataChannelJava.xcframework.json") includeEmptyDirs = false into("lib/ios") } @@ -508,8 +573,44 @@ dependencies { testRuntimeOnly("org.junit.platform:junit-platform-launcher") } -tasks.test { +fun Test.configureNativeTests() { systemProperty("native.test.jar", packageNativeForHost.get().archiveFile.get().asFile.absolutePath) + systemProperty("libdatachannel.native.tests", "true") + val nativeDirectory = nativeForHostOutputDir.dir("native").asFile + val nativeExtension = when { + System.getProperty("os.name").lowercase().startsWith("win") -> "dll" + System.getProperty("os.name").lowercase().contains("mac") -> "dylib" + else -> "so" + } + val nativeLibrary = nativeDirectory.resolve("libdatachannel-java.$nativeExtension") + systemProperty("libdatachannel.native.datachannel-java.path", nativeLibrary.absolutePath) + systemProperty("native.test.library.path", nativeLibrary.absolutePath) + systemProperty("native.test.probe.path", nativeDirectory.resolve("libdatachannel-java-test-probe.$nativeExtension").absolutePath) + systemProperty("native.test.dependency.collision.path", nativeDirectory.resolve("libdatachannel-java-dependency-collision.$nativeExtension").absolutePath) +} + +tasks.test { + configureNativeTests() + exclude("**/NativeAllocatorDiagnosticsTest*") +} + +tasks.register("nativeAllocatorDiagnostics") { + group = "verification" + description = "Audit private native allocation ownership during real desktop transfers" + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + useJUnitPlatform() + configureNativeTests() + include("**/NativeAllocatorDiagnosticsTest.class") + val reportsDirectory = layout.buildDirectory.dir("reports/allocator-diagnostics") + systemProperty("native.test.classpath", sourceSets.test.get().runtimeClasspath.asPath) + systemProperty("native.test.allocator.reports", reportsDirectory.get().asFile.absolutePath) + outputs.dir(reportsDirectory) + doFirst { + check(allocatorDiagnosticsEnabled.get()) { + "Use -PnativeAllocatorDiagnostics=true to build the CI allocator instrumentation" + } + } } publishing.publications.withType().configureEach { @@ -565,6 +666,7 @@ tasks.deploy { val mavenCentralDeploy by tasks.registering(DefaultTask::class) { group = "publishing" + if (!isSnapshot) dependsOn(verifyReleaseSources) val repo = if (isSnapshot) { Constants.SNAPSHOTS_REPO diff --git a/conventions/src/main/kotlin/tel.schich.libdatachannel.convention.common.gradle.kts b/conventions/src/main/kotlin/tel.schich.libdatachannel.convention.common.gradle.kts index 368630b..5328213 100644 --- a/conventions/src/main/kotlin/tel.schich.libdatachannel.convention.common.gradle.kts +++ b/conventions/src/main/kotlin/tel.schich.libdatachannel.convention.common.gradle.kts @@ -43,6 +43,12 @@ repositories { mavenLocal() } +tasks.withType().configureEach { + from(rootProject.file("LICENSE")) { into("META-INF/licenses/libdatachannel-java") } + from(rootProject.file("licenses")) { into("META-INF/licenses") } + from(rootProject.file("SOURCE-NOTICES.txt")) { into("META-INF") } +} + dependencies { compileOnly(libs.jdtAnnotations) @@ -73,11 +79,11 @@ publishing { pom { name = artifactId description = project.description - url = "https://github.com/pschichtel/libdatachannel-java" + url = "https://github.com/NostrGameEngine/libdatachannel-java" licenses { license { - name = "MIT" - url = "https://opensource.org/licenses/MIT" + name = "Mozilla Public License 2.0" + url = "https://www.mozilla.org/en-US/MPL/2.0/" } } developers { @@ -91,9 +97,9 @@ publishing { } } scm { - url.set("https://github.com/pschichtel/libdatachannel-java") - connection.set("scm:git:https://github.com/pschichtel/libdatachannel-java") - developerConnection.set("scm:git:git@github.com:pschichtel/libdatachannel-java") + url.set("https://github.com/NostrGameEngine/libdatachannel-java") + connection.set("scm:git:https://github.com/NostrGameEngine/libdatachannel-java.git") + developerConnection.set("scm:git:ssh://git@github.com/NostrGameEngine/libdatachannel-java.git") } } } diff --git a/docs/native-allocator-diagnostics.md b/docs/native-allocator-diagnostics.md new file mode 100644 index 0000000..292cdcd --- /dev/null +++ b/docs/native-allocator-diagnostics.md @@ -0,0 +1,46 @@ +# Native allocator diagnostics + +The `Allocator diagnostics` workflow builds an instrumented desktop JNI library +on Linux, Windows, and both macOS architectures. Instrumentation is off by +default and absent from normal native artifacts. The workflow rebuilds without +instrumentation and verifies that its diagnostic JNI methods disappear. + +To reproduce a CI run with the normal native build prerequisites installed: + +```sh +./gradlew -PnativeAllocatorDiagnostics=true nativeAllocatorDiagnostics +python tools/test_allocator_audit.py +python tools/audit_allocator_imports.py build/dockcross/host/native/libdatachannel-java.so +``` + +Use the `.dylib` or `.dll` filename on macOS or Windows. Rebuild with +`./gradlew test` afterwards to rebuild and repackage without instrumentation. + +The adapter uses the pinned mimalloc version's allocation/free tracking macros. +Callbacks count successful allocations, requested bytes, releases and alignment +adjustments, and check ownership against the private allocator instance. They do +not allocate memory, log, unwind stacks or replace the process allocator. +Counters cover initialization, the existing component ownership probes, +repeated bidirectional DTLS transfers with 64-byte, 4-KiB and 64-KiB messages, +four concurrent peer pairs, and explicit native cleanup. Each workload must +exercise allocations and frees without ownership violations. Message payloads +and ordering are checked. A separate JVM injects a JVM-owned pointer and must +fail the ownership rule. The foreign pointer is never freed by mimalloc. + +An independent final-binary audit rejects imports of standard C heap functions, +allocating formatting functions, C++ new/delete and exception allocation APIs. +A genuinely compiled library importing system malloc is its negative control. +This complements tracing: a call that bypasses mimalloc would not reach its +tracking callbacks. The audit checks direct imports, not arbitrary calls made +through dynamically discovered function pointers. + +Reports are uploaded as `allocator-diagnostics-*` artifacts. JSONL files contain +per-phase counter deltas, final totals and cumulative ownership violations. Counts are not +leak reports: runtime caches and allocator bookkeeping may survive a phase, +and snapshots are taken while background workers can still allocate. + +The scope is the desktop native heap controlled by the binding. JVM/application +buffers and system APIs with their own matching release functions remain owned +by their respective platforms. Android and iOS use their platform allocator and +are not instrumented. Passing these workloads establishes observed coverage and +direct-import isolation, not a proof for every possible execution path. diff --git a/example/java/build.gradle.kts b/example/java/build.gradle.kts index 9e86f84..f9c693f 100644 --- a/example/java/build.gradle.kts +++ b/example/java/build.gradle.kts @@ -13,14 +13,12 @@ dependencies { java { toolchain { - languageVersion = JavaLanguageVersion.of(11) + languageVersion = JavaLanguageVersion.of(21) } +} - testing { - toolchain { - languageVersion = JavaLanguageVersion.of(25) - } - } +tasks.withType().configureEach { + options.release.set(11) } application { diff --git a/jni/CMakeLists.txt b/jni/CMakeLists.txt index d968235..64f516f 100644 --- a/jni/CMakeLists.txt +++ b/jni/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.20) project(libdatachannel_jni C CXX) set(CMAKE_C_STANDARD 11) @@ -6,66 +6,97 @@ set(CMAKE_CXX_STANDARD 11) option(PROJECT_VERSION "The version of the project" "unspecified") option(ENABLE_HARDENING "Enable compiler/linker hardening flags" ON) +option(BUILD_JNI_TESTS "Build the private JVM thread attachment test probe" OFF) +option(ALLOCATOR_DIAGNOSTICS "Instrument the private allocator for CI only" OFF) + +set(OPENSSL_USE_STATIC_LIBS ON CACHE BOOL "Embed private OpenSSL in the binding" FORCE) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(LIBDATACHANNEL_JNI_IOS OFF) if(CMAKE_SYSTEM_NAME STREQUAL "iOS") set(LIBDATACHANNEL_JNI_IOS ON) set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build static libraries for iOS" FORCE) set(OPENSSL_USE_STATIC_LIBS ON CACHE BOOL "Use static OpenSSL for iOS" FORCE) + # Materialize tentative C definitions so the relocatable link can localize + # their storage instead of leaving private-external commons for the app link. + add_compile_options($<$:-fno-common>) endif() -# Add mimalloc with MI_SECURE for non-Android targets -if(NOT ANDROID) +set(LIBDATACHANNEL_PRIVATE_ALLOCATOR OFF) +if(NOT ANDROID AND NOT LIBDATACHANNEL_JNI_IOS) + set(LIBDATACHANNEL_PRIVATE_ALLOCATOR ON) include(FetchContent) FetchContent_Declare( mimalloc - URL https://github.com/microsoft/mimalloc/archive/refs/tags/v2.1.7.tar.gz + URL https://github.com/microsoft/mimalloc/archive/refs/tags/v2.5.2.tar.gz + URL_HASH SHA256=5cf9d2a10fae5551b764e888a623da411c6c6beb0221b847f83492ab1932bdab DOWNLOAD_EXTRACT_TIMESTAMP FALSE ) set(MI_SECURE ON CACHE BOOL "Enable mimalloc security features" FORCE) + set(MI_USE_CXX ON CACHE STRING "Preserve C++ bad_alloc semantics" FORCE) set(MI_OVERRIDE OFF CACHE BOOL "Do not override global malloc symbols" FORCE) + set(MI_OSX_INTERPOSE OFF CACHE BOOL "Do not interpose the process allocator" FORCE) + set(MI_OSX_ZONE OFF CACHE BOOL "Do not replace the process malloc zone" FORCE) set(MI_WIN_REDIRECT OFF CACHE BOOL "Disable mimalloc Windows redirect exports" FORCE) - if(LIBDATACHANNEL_JNI_IOS) - set(MI_BUILD_SHARED OFF CACHE BOOL "Do not build shared mimalloc for iOS" FORCE) - set(MI_BUILD_STATIC ON CACHE BOOL "Build static mimalloc for iOS" FORCE) - else() - set(MI_BUILD_SHARED ON CACHE BOOL "Build mimalloc as shared library" FORCE) + if(MINGW) + # The default CRT/TLS path initializes twice with MinGW's MSVCRT. + # Use mimalloc's supported FLS lifecycle, without a second DLL entry point. + set(MI_WIN_USE_FLS ON CACHE BOOL "Use one MinGW allocator initialization path" FORCE) + endif() + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # JVMs load JNI after startup; initial-exec TLS can exhaust the loader's reserve. + set(MI_LOCAL_DYNAMIC_TLS ON CACHE BOOL "Use dlopen-compatible allocator TLS" FORCE) endif() + set(MI_BUILD_SHARED OFF CACHE BOOL "Embed mimalloc in JNI" FORCE) + set(MI_BUILD_STATIC ON CACHE BOOL "Build private static mimalloc" FORCE) + set(MI_BUILD_OBJECT OFF CACHE BOOL "Do not build unused mimalloc objects" FORCE) set(MI_BUILD_TESTS OFF CACHE BOOL "Build mimalloc tests" FORCE) FetchContent_MakeAvailable(mimalloc) - set(MIMALLOC_BUILD_TARGET mimalloc) - if(LIBDATACHANNEL_JNI_IOS) - set(MIMALLOC_BUILD_TARGET mimalloc-static) - endif() - set(MIMALLOC_PREFIX_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/mimalloc_prefix_symbols.h") - if(MSVC) - target_compile_options(${MIMALLOC_BUILD_TARGET} PRIVATE /FI"${MIMALLOC_PREFIX_HEADER}") - else() - target_compile_options(${MIMALLOC_BUILD_TARGET} PRIVATE -include "${MIMALLOC_PREFIX_HEADER}") - endif() - if(WIN32) - set_target_properties(${MIMALLOC_BUILD_TARGET} PROPERTIES OUTPUT_NAME "libdatachannel_mimalloc") - else() - set_target_properties(${MIMALLOC_BUILD_TARGET} PROPERTIES OUTPUT_NAME "datachannel_mimalloc") + if(MINGW) + get_target_property(private_allocator_definitions mimalloc-static COMPILE_DEFINITIONS) + if(NOT "MI_WIN_INIT_USE_FLS=1" IN_LIST private_allocator_definitions) + message(FATAL_ERROR "MinGW mimalloc must enable its FLS lifecycle") + endif() endif() - # Empty version strings still add separators to Mach-O install names. - # Remove the properties inherited from mimalloc to build an unversioned library. - set_property(TARGET ${MIMALLOC_BUILD_TARGET} PROPERTY VERSION) - set_property(TARGET ${MIMALLOC_BUILD_TARGET} PROPERTY SOVERSION) - if(WIN32) - set(MIMALLOC_RUNTIME_NAME "libdatachannel_mimalloc.dll") - elseif(APPLE) - set(MIMALLOC_RUNTIME_NAME "libdatachannel_mimalloc.dylib") - else() - set(MIMALLOC_RUNTIME_NAME "libdatachannel_mimalloc.so") + if(APPLE) + include(cmake/private_cxx_runtime.cmake) endif() endif() +if(ALLOCATOR_DIAGNOSTICS AND (NOT LIBDATACHANNEL_PRIVATE_ALLOCATOR OR NOT BUILD_JNI_TESTS)) + message(FATAL_ERROR "Allocator diagnostics require a desktop JNI test build") +endif() + set(NO_WEBSOCKET ON CACHE BOOL "configure libdatachannel build") set(NO_MEDIA ON CACHE BOOL "configure libdatachannel build") set(NO_TESTS ON CACHE BOOL "configure libdatachannel build") set(NO_EXAMPLES ON CACHE BOOL "configure libdatachannel build") +if(LIBDATACHANNEL_PRIVATE_ALLOCATOR) + find_package(OpenSSL REQUIRED) +endif() add_subdirectory(libdatachannel) +set_property(TARGET datachannel PROPERTY EXCLUDE_FROM_ALL TRUE) + +function(use_private_allocator target) + target_compile_definitions(${target} PRIVATE LIBDATACHANNEL_PRIVATE_ALLOCATOR=1) + target_include_directories(${target} PRIVATE "${mimalloc_SOURCE_DIR}/include") + # C source redirection also covers DLL-imported CRT calls on Windows. + target_compile_options(${target} PRIVATE + "$<$:-include${CMAKE_CURRENT_SOURCE_DIR}/src/mimalloc_redirect.h>") + if(APPLE) + add_dependencies(${target} private-cxx-runtime) + target_compile_options(${target} PRIVATE "$<$:-nostdinc++>") + target_include_directories(${target} SYSTEM BEFORE PRIVATE "${PRIVATE_CXX_ROOT}/include/c++/v1") + endif() +endfunction() + +if(LIBDATACHANNEL_PRIVATE_ALLOCATOR) + foreach(native_target IN ITEMS datachannel datachannel-static juice-static usrsctp mimalloc-static) + if(TARGET ${native_target}) + use_private_allocator(${native_target}) + endif() + endforeach() +endif() include_directories(libdatachannel/include generated) include_directories(jdk) @@ -116,7 +147,7 @@ endif() if(APPLE) add_link_options(-dead_strip) set(CMAKE_POSITION_INDEPENDENT_CODE ON) - set(CMAKE_INSTALL_RPATH "@loader_path") + set(CMAKE_INSTALL_RPATH "") set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) set(CMAKE_MACOSX_RPATH ON) elseif(NOT WIN32) @@ -136,45 +167,96 @@ endif() add_library(datachannel-java ${LIBDATACHANNEL_JAVA_LIBRARY_TYPE} generated/jni-c-to-java.c src/init.c + src/thread_attachment.cpp src/util.c src/native_channel.c src/native_peer.c src/native_track.c src/callback.c) target_link_libraries(datachannel-java PRIVATE datachannel-static) -if(NOT ANDROID) - set(LIBDATACHANNEL_MIMALLOC_TARGET mimalloc) - if(LIBDATACHANNEL_JNI_IOS) - set(LIBDATACHANNEL_MIMALLOC_TARGET mimalloc-static) +set_target_properties(datachannel-java PROPERTIES SKIP_BUILD_RPATH TRUE INSTALL_RPATH "") +if(NOT LIBDATACHANNEL_JNI_IOS) + # Static dependencies must not expose or interpose implementation symbols. + # Only JNI entry points belong to this shared library's public ABI. + if(APPLE) + find_program(LIBDATACHANNEL_OTOOL NAMES otool llvm-otool REQUIRED) + target_link_options(datachannel-java PRIVATE + "LINKER:-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/cmake/jni.exports.apple") + set_property(TARGET datachannel-java APPEND PROPERTY LINK_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/jni.exports.apple") + elseif(WIN32) + if(MINGW) + target_link_options(datachannel-java PRIVATE + "LINKER:--exclude-all-symbols" "LINKER:--exclude-libs,ALL") + endif() + else() + target_link_options(datachannel-java PRIVATE + "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/cmake/jni.exports.map" + "LINKER:--exclude-libs,ALL" "LINKER:--gc-sections" "LINKER:--no-undefined") + set_property(TARGET datachannel-java APPEND PROPERTY LINK_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/jni.exports.map") endif() - target_link_libraries(datachannel-java PRIVATE ${LIBDATACHANNEL_MIMALLOC_TARGET}) - if(MSVC) - target_compile_options(datachannel-java PRIVATE /FI"${CMAKE_CURRENT_SOURCE_DIR}/src/mimalloc_redirect.h") + add_custom_command(TARGET datachannel-java POST_BUILD + COMMAND "${CMAKE_COMMAND}" -DINPUT_FILE=$ + -DNM_TOOL=${CMAKE_NM} -DOBJDUMP_TOOL=${CMAKE_OBJDUMP} + -DOTOOL_TOOL=${LIBDATACHANNEL_OTOOL} -DPLATFORM=${CMAKE_SYSTEM_NAME} + -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/verify_private_dependencies.cmake" + VERBATIM) +endif() +if(WIN32) + target_sources(datachannel-java PRIVATE src/windows_entry.c) +endif() + +if(BUILD_JNI_TESTS) + add_library(datachannel-java-dependency-collision SHARED + tests/dependency_collision.c tests/dependency_cpp_collision.cpp) + if(UNIX AND NOT APPLE) + target_link_options(datachannel-java-dependency-collision PRIVATE "LINKER:-Bsymbolic") + endif() + set_target_properties(datachannel-java-dependency-collision PROPERTIES PREFIX "lib") + set_property(TARGET datachannel-java-dependency-collision PROPERTY CXX_STANDARD 17) + add_library(datachannel-java-test-probe SHARED tests/thread_attachment_probe.cpp) + set_target_properties(datachannel-java-test-probe PROPERTIES PREFIX "lib") + target_include_directories(datachannel-java-test-probe PRIVATE jdk) + if(WIN32) + target_include_directories(datachannel-java-test-probe PRIVATE jdk/windows) else() - target_compile_options(datachannel-java PRIVATE -include "${CMAKE_CURRENT_SOURCE_DIR}/src/mimalloc_redirect.h") + target_include_directories(datachannel-java-test-probe PRIVATE jdk/unix) + target_link_libraries(datachannel-java-test-probe PRIVATE ${CMAKE_DL_LIBS}) + endif() + find_package(Threads REQUIRED) + target_link_libraries(datachannel-java-test-probe PRIVATE Threads::Threads) +endif() +if(LIBDATACHANNEL_PRIVATE_ALLOCATOR) + use_private_allocator(datachannel-java) + target_sources(datachannel-java PRIVATE src/private_allocator.cpp) + target_sources(datachannel-java PRIVATE src/private_malloc.c) + set_source_files_properties(src/private_malloc.c PROPERTIES + COMPILE_DEFINITIONS LIBDATACHANNEL_ALLOCATOR_IMPLEMENTATION=1) + set_property(TARGET datachannel-java PROPERTY CXX_STANDARD 17) + target_link_libraries(datachannel-java PRIVATE mimalloc-static OpenSSL::Crypto) + if(ALLOCATOR_DIAGNOSTICS) + # The adapter uses mimalloc's documented allocation/free tracking macros. + # No upstream source is changed and no process allocator is interposed. + target_compile_options(mimalloc-static PRIVATE + "-include${CMAKE_CURRENT_SOURCE_DIR}/tests/allocator_diagnostics_hooks.h") + target_sources(datachannel-java PRIVATE tests/allocator_diagnostics.c) endif() if(APPLE) - set_target_properties(datachannel-java PROPERTIES INSTALL_RPATH "@loader_path") - elseif(NOT WIN32) - set_target_properties(datachannel-java PROPERTIES INSTALL_RPATH "$ORIGIN") + target_link_options(datachannel-java PRIVATE -nostdlib++) + target_link_libraries(datachannel-java PRIVATE + "${PRIVATE_CXX_ROOT}/lib/libc++.a" "${PRIVATE_CXX_ROOT}/lib/libc++abi.a") + else() + target_link_options(datachannel-java PRIVATE -static-libstdc++ -static-libgcc) endif() - if(NOT LIBDATACHANNEL_JNI_IOS) - add_custom_command( - TARGET datachannel-java POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_if_different - "$" - "$/${MIMALLOC_RUNTIME_NAME}" - VERBATIM - ) - add_custom_command( - TARGET datachannel-java POST_BUILD - COMMAND "${CMAKE_COMMAND}" - -DINPUT_FILE=$ - -DNM_TOOL=${CMAKE_NM} - -DOBJDUMP_TOOL=${CMAKE_OBJDUMP} - -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/verify_mimalloc_symbols.cmake" - VERBATIM - ) + if(BUILD_JNI_TESTS) + foreach(component IN ITEMS datachannel-java juice-static usrsctp) + string(REPLACE "-" "_" probe_name "${component}_allocator_probe") + target_sources(${component} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/tests/c_allocator_probe.c") + target_compile_definitions(${component} PRIVATE "LIBDATACHANNEL_ALLOCATOR_PROBE=${probe_name}") + endforeach() + target_sources(mimalloc-static PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/tests/secure_allocator_probe.c") + target_sources(datachannel-java PRIVATE tests/native_allocator_probe.cpp) endif() endif() @@ -183,7 +265,6 @@ if(LIBDATACHANNEL_JNI_IOS) set(LIBDATACHANNEL_JAVA_IOS_ARCHIVES "$" "$" - "$" ) foreach(candidate_target IN ITEMS usrsctp juice-static) if(TARGET ${candidate_target}) @@ -193,7 +274,7 @@ if(LIBDATACHANNEL_JNI_IOS) add_custom_command( OUTPUT "${LIBDATACHANNEL_JAVA_IOS_ARCHIVE}" COMMAND xcrun libtool -static -o "${LIBDATACHANNEL_JAVA_IOS_ARCHIVE}" ${LIBDATACHANNEL_JAVA_IOS_ARCHIVES} - DEPENDS datachannel-java datachannel-static ${LIBDATACHANNEL_MIMALLOC_TARGET} + DEPENDS datachannel-java datachannel-static VERBATIM ) add_custom_target(datachannel-java-ios-archive ALL diff --git a/jni/build.sh b/jni/build.sh index dce0faf..ac566b5 100755 --- a/jni/build.sh +++ b/jni/build.sh @@ -4,6 +4,11 @@ set -euo pipefail set -x +# Windows Gradle supplies native paths; forward slashes also work in MSYS2 and Conan. +OUTPUT_DIR="${OUTPUT_DIR//\\//}" +MOUNT_SOURCE="${MOUNT_SOURCE//\\//}" +RELATIVE_PROJECT_PATH="${RELATIVE_PROJECT_PATH//\\//}" + export CONAN_HOME="${OUTPUT_DIR}/conan/home" # OpenSSL's makefile constructs broken compiler paths due to CROSS_COMPILE export CROSS_COMPILE="" @@ -74,6 +79,11 @@ find_conan_package_folder() { local package_folder package_folder="$(sed -nE "s/^set\\(${variable_prefix}_PACKAGE_FOLDER_[^ ]* \"([^\"]+)\"\\).*$/\\1/p" "$data_file" | head -n 1)" + case "$package_folder" in + '${CMAKE_CURRENT_LIST_DIR}/'*) + package_folder="${conan_install_dir}/${package_folder#'${CMAKE_CURRENT_LIST_DIR}/'}" + ;; + esac if [ -z "$package_folder" ] || [ ! -d "$package_folder" ] then echo "Unable to resolve ${file_prefix} package folder from ${data_file}" >&2 @@ -94,20 +104,6 @@ create_ios_xcframework() { -output "$xcframework_dir" } -write_ios_xcframework_metadata() { - local framework_name=$1 - local force_load=$2 - local metadata_file="${OUTPUT_DIR}/lib/ios/${framework_name}.xcframework.json" - - cat > "$metadata_file" < --target install-cxx install-cxxabi + BUILD_BYPRODUCTS "${PRIVATE_CXX_ROOT}/lib/libc++.a" "${PRIVATE_CXX_ROOT}/lib/libc++abi.a") diff --git a/jni/cmake/verify_mimalloc_symbols.cmake b/jni/cmake/verify_mimalloc_symbols.cmake deleted file mode 100644 index 460ea8d..0000000 --- a/jni/cmake/verify_mimalloc_symbols.cmake +++ /dev/null @@ -1,103 +0,0 @@ -if(NOT DEFINED INPUT_FILE OR INPUT_FILE STREQUAL "") - message(FATAL_ERROR "verify_mimalloc_symbols: INPUT_FILE is required") -endif() - -if(NOT EXISTS "${INPUT_FILE}") - message(FATAL_ERROR "verify_mimalloc_symbols: INPUT_FILE does not exist: ${INPUT_FILE}") -endif() - -set(_nm_tool "${NM_TOOL}") -if(_nm_tool STREQUAL "") - set(_nm_tool "nm") -endif() - -set(_bad_symbols "") - -if(INPUT_FILE MATCHES "\\.dll$") - set(_objdump_tool "${OBJDUMP_TOOL}") - if(_objdump_tool STREQUAL "") - set(_objdump_tool "objdump") - endif() - - execute_process( - COMMAND "${_objdump_tool}" -p "${INPUT_FILE}" - RESULT_VARIABLE _rc - OUTPUT_VARIABLE _out - ERROR_VARIABLE _err - ) - if(NOT _rc EQUAL 0) - message(FATAL_ERROR - "verify_mimalloc_symbols: failed to inspect PE exports for ${INPUT_FILE} using '${_objdump_tool}'. " - "objdump stderr: ${_err}") - endif() - - string(REPLACE "\n" ";" _lines "${_out}") - set(_in_export_names FALSE) - foreach(_line IN LISTS _lines) - if(_line MATCHES "^\\[Ordinal/Name Pointer\\] Table") - set(_in_export_names TRUE) - continue() - endif() - if(NOT _in_export_names) - continue() - endif() - if(_line MATCHES "^[ \t]*\\[[^]]+\\].*[ \t]([_A-Za-z][_A-Za-z0-9]*)$") - string(REGEX REPLACE ".*[ \t]([_A-Za-z][_A-Za-z0-9]*)$" "\\1" _sym "${_line}") - if(_sym MATCHES "^_?mi_" AND NOT _sym MATCHES "^_?libdatachannel_mi_") - list(APPEND _bad_symbols "${_sym}") - endif() - endif() - endforeach() -else() - # Try a few nm flag styles (GNU/LLVM, then Darwin). - set(_nm_flag_sets - "-g;--defined-only" - "-gU" - "-g;-U" - ) - - set(_rc 1) - set(_out "") - set(_err "") - foreach(_flags IN LISTS _nm_flag_sets) - execute_process( - COMMAND "${_nm_tool}" ${_flags} "${INPUT_FILE}" - RESULT_VARIABLE _this_rc - OUTPUT_VARIABLE _this_out - ERROR_VARIABLE _this_err - ) - if(_this_rc EQUAL 0) - set(_rc 0) - set(_out "${_this_out}") - break() - endif() - if(NOT _this_err STREQUAL "") - string(APPEND _err "\n[flags: ${_flags}] ${_this_err}") - endif() - endforeach() - - if(NOT _rc EQUAL 0) - message(FATAL_ERROR - "verify_mimalloc_symbols: failed to inspect symbols for ${INPUT_FILE} using '${_nm_tool}'. " - "nm stderr: ${_err}") - endif() - - string(REPLACE "\n" ";" _lines "${_out}") - foreach(_line IN LISTS _lines) - if(_line MATCHES "(^|[ \t])_?mi_[A-Za-z0-9_]+$") - string(REGEX REPLACE ".*([_]?(mi_[A-Za-z0-9_]+))$" "\\1" _sym "${_line}") - if(NOT _sym MATCHES "^_?libdatachannel_mi_") - list(APPEND _bad_symbols "${_sym}") - endif() - endif() - endforeach() -endif() - -if(_bad_symbols) - list(REMOVE_DUPLICATES _bad_symbols) - string(JOIN ", " _bad_symbols_joined ${_bad_symbols}) - message(FATAL_ERROR - "verify_mimalloc_symbols: unprefixed mimalloc symbols found in ${INPUT_FILE}: ${_bad_symbols_joined}") -endif() - -message(STATUS "verify_mimalloc_symbols: OK for ${INPUT_FILE}") diff --git a/jni/cmake/verify_private_dependencies.cmake b/jni/cmake/verify_private_dependencies.cmake new file mode 100644 index 0000000..78fe51f --- /dev/null +++ b/jni/cmake/verify_private_dependencies.cmake @@ -0,0 +1,109 @@ +if(NOT EXISTS "${INPUT_FILE}") + message(FATAL_ERROR "Private dependency verification requires INPUT_FILE") +endif() +if(INPUT_FILE MATCHES "\\.dll$") + execute_process(COMMAND "${OBJDUMP_TOOL}" -p "${INPUT_FILE}" + RESULT_VARIABLE rc OUTPUT_VARIABLE headers ERROR_VARIABLE error) + if(NOT rc EQUAL 0) + message(FATAL_ERROR "Cannot inspect JNI DLL: ${error}") + endif() + if(headers MATCHES "DLL Name: [^\n]*(ssl|crypto|mimalloc)[^\n]*\\.dll") + message(FATAL_ERROR "JNI must not depend on shared OpenSSL or mimalloc DLLs") + endif() + if(headers MATCHES "DLL Name: [^\n]*(libstdc\\+\\+|libgcc_s|libc\\+\\+)[^\n]*\\.dll") + message(FATAL_ERROR "Desktop JNI must embed its C++ runtime") + endif() + set(imports "${headers}") + foreach(export_marker IN ITEMS "The Export Tables" "[Ordinal/Name Pointer] Table") + string(FIND "${imports}" "${export_marker}" export_start) + if(NOT export_start EQUAL -1) + string(SUBSTRING "${imports}" 0 ${export_start} imports) + endif() + endforeach() + if(imports MATCHES "[ \t](malloc|calloc|realloc|free|_?strdup)[\r\n]") + message(FATAL_ERROR "Desktop JNI must resolve C heap requests inside the private library") + endif() + string(REPLACE "\n" ";" lines "${headers}") + set(exports "") + set(in_names FALSE) + foreach(line IN LISTS lines) + if(line MATCHES "^\\[Ordinal/Name Pointer\\] Table") + set(in_names TRUE) + elseif(in_names AND line MATCHES "^[ \t]*Ordinal[ \t]+Hint[ \t]+Name[ \t\r]*$") + # Recent binutils prints column headings and an explicit ordinal base. + continue() + elseif(in_names AND line MATCHES "^[ \t]*\\[[ \t]*[0-9]+\\][ \t]+\\+base\\[[ \t]*[0-9]+\\][ \t]+[0-9a-fA-F]+[ \t]+([^ \t\r]+)") + list(APPEND exports "${CMAKE_MATCH_1}") + elseif(in_names AND line MATCHES "^[ \t]*\\[[ \t]*[0-9]+\\][ \t]+([^ \t\r]+)") + list(APPEND exports "${CMAKE_MATCH_1}") + elseif(in_names AND NOT line MATCHES "^[ \t\r]*$") + break() + endif() + endforeach() +else() + if(PLATFORM MATCHES "Darwin|iOS") + set(flags -gUm) + set(macho TRUE) + if(NOT STATIC_ARCHIVE) + execute_process(COMMAND "${OTOOL_TOOL}" -L "${INPUT_FILE}" + RESULT_VARIABLE rc OUTPUT_VARIABLE headers ERROR_VARIABLE error) + if(NOT rc EQUAL 0 OR headers MATCHES "lib(ssl|crypto|[^/ \n]*mimalloc|c\\+\\+)[^\n]*\\.dylib") + message(FATAL_ERROR "JNI must embed OpenSSL and mimalloc: ${error} ${headers}") + endif() + endif() + if(STATIC_ARCHIVE AND PLATFORM STREQUAL "iOS") + set(flags -m) + endif() + else() + set(flags -D --defined-only) + execute_process(COMMAND "${OBJDUMP_TOOL}" -p "${INPUT_FILE}" + RESULT_VARIABLE rc OUTPUT_VARIABLE headers ERROR_VARIABLE error) + if(NOT rc EQUAL 0 OR headers MATCHES "NEEDED[^\n]*(ssl|crypto|mimalloc)") + message(FATAL_ERROR "JNI must embed OpenSSL and mimalloc: ${error} ${headers}") + endif() + if(headers MATCHES "FLAGS[^\n]*STATIC_TLS") + message(FATAL_ERROR "JNI requires dlopen-compatible thread-local storage, not initial-exec TLS") + endif() + if(PLATFORM STREQUAL "Linux" AND headers MATCHES "NEEDED[^\n]*(libstdc\\+\\+|libc\\+\\+|libgcc_s)") + message(FATAL_ERROR "Desktop JNI must embed its C++ runtime") + endif() + endif() + execute_process(COMMAND "${NM_TOOL}" ${flags} "${INPUT_FILE}" + RESULT_VARIABLE rc OUTPUT_VARIABLE symbols ERROR_VARIABLE error) + if(NOT rc EQUAL 0) + message(FATAL_ERROR "Cannot inspect JNI exports: ${error}") + endif() + if(PLATFORM STREQUAL "Android") + execute_process(COMMAND "${NM_TOOL}" --defined-only "${INPUT_FILE}" + RESULT_VARIABLE allocator_rc OUTPUT_VARIABLE allocator_symbols) + if(NOT allocator_rc EQUAL 0 OR allocator_symbols MATCHES "[ \t]_?mi_[A-Za-z0-9_]+([ \t\r\n]|$)") + message(FATAL_ERROR "Android must use the platform allocator without mimalloc") + endif() + endif() + if(STATIC_ARCHIVE AND symbols MATCHES "\\(common\\)") + message(FATAL_ERROR "Static JNI must materialize common definitions before making them private") + endif() + if(PLATFORM STREQUAL "iOS" AND symbols MATCHES "[ \t]_+mi_[A-Za-z0-9_]+([ \t\r\n]|$)") + message(FATAL_ERROR "iOS must use the platform allocator without mimalloc") + endif() + string(REPLACE "\n" ";" lines "${symbols}") + set(exports "") + foreach(line IN LISTS lines) + # Darwin nm also prints private-external definitions with -g. + if(macho AND line MATCHES "[ \t]external[ \t]" AND NOT line MATCHES "private external|\\(undefined\\)") + string(REGEX REPLACE ".*[ \t]([^ \t\r]+)$" "\\1" symbol "${line}") + list(APPEND exports "${symbol}") + elseif(NOT macho AND line MATCHES "^[0-9a-fA-F]+[ \t]+[A-Za-z][ \t]+([^ \t\r]+)") + list(APPEND exports "${CMAKE_MATCH_1}") + endif() + endforeach() +endif() +if(NOT exports) + message(FATAL_ERROR "No JNI exports found; refusing an unverified native artifact") +endif() +foreach(symbol IN LISTS exports) + if(NOT symbol MATCHES "^_?(Java_.+|JNI_OnLoad|JNI_OnUnload|get_jni_env_from_jvm)(@[0-9]+)?$") + message(FATAL_ERROR "Non-JNI symbol escapes the private native library: ${symbol}") + endif() +endforeach() +message(STATUS "Private dependencies verified: no shared OpenSSL/mimalloc and only JNI exports") diff --git a/jni/conanfile.py b/jni/conanfile.py index ca28123..af38152 100644 --- a/jni/conanfile.py +++ b/jni/conanfile.py @@ -1,19 +1,18 @@ from conan import ConanFile -statically_linked_platforms = { - "Windows", - "Macos", - "Android", - "iOS", -} - - class LibDataChannel(ConanFile): settings = "os", "arch", "compiler", "build_type" requires = "openssl/3.6.0" generators = "CMakeDeps" def configure(self): - self.options["openssl"].shared = f"{self.settings.os}" not in statically_linked_platforms + self.options["openssl"].shared = False + if str(self.settings.os) != "Windows": + self.options["openssl"].fPIC = True self.options["openssl"].no_apps = True self.options["openssl"].no_zlib = True + self.options["openssl"].no_module = True + self.options["openssl"].no_dso = True + self.options["openssl"].no_engine = True + self.options["openssl"].no_fips = True + self.options["openssl"].no_autoload_config = True diff --git a/jni/libdatachannel b/jni/libdatachannel index a02b751..6b1e2e6 160000 --- a/jni/libdatachannel +++ b/jni/libdatachannel @@ -1 +1 @@ -Subproject commit a02b751917ac8afc8c58dc6f4461d25ff9465d48 +Subproject commit 6b1e2e620f1e37f0eafeee702eaea0043cb305fd diff --git a/jni/src/global_jvm.h b/jni/src/global_jvm.h index 6d4f3c0..87b4a6f 100644 --- a/jni/src/global_jvm.h +++ b/jni/src/global_jvm.h @@ -1,3 +1,20 @@ +#ifndef LIBDATACHANNEL_GLOBAL_JVM_H +#define LIBDATACHANNEL_GLOBAL_JVM_H + #include -JNIEnv* get_jni_env(); \ No newline at end of file +#ifdef __cplusplus +extern "C" { +#endif + +JNIEnv* get_jni_env(void); +JNIEXPORT JNIEnv* get_jni_env_from_jvm(JavaVM* jvm); +int initialize_jni_thread_tracking(void); +int register_jni_thread(JavaVM* jvm); +void release_jni_thread_tracking(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/jni/src/init.c b/jni/src/init.c index d430a49..fef7283 100644 --- a/jni/src/init.c +++ b/jni/src/init.c @@ -1,7 +1,6 @@ #include "global_jvm.h" #include "jni-c-to-java.h" #include -#include #include #ifdef __APPLE__ #include @@ -10,31 +9,19 @@ #define JNI_VERSION JNI_VERSION_1_6 static JavaVM* global_JVM; -static pthread_key_t thread_key; -static pthread_once_t thread_key_once = PTHREAD_ONCE_INIT; static volatile jint jvm_unloading = 0; static volatile jint jvm_loaded = 0; -void detach_thread(); - -static void create_thread_key() { - pthread_key_create(&thread_key, detach_thread); -} - -void detach_thread() { - JavaVM* jvm = pthread_getspecific(thread_key); - if (jvm != NULL) { - (*jvm)->DetachCurrentThread(jvm); - } -} - JNIEnv* get_jni_env_from_jvm(JavaVM* jvm) { JNIEnv* env; jint result = (*jvm)->GetEnv(jvm, (void**) &env, JNI_VERSION); if (result == JNI_EDETACHED) { result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void**) &env, NULL); if (result == JNI_OK) { - pthread_setspecific(thread_key, jvm); + if (!register_jni_thread(jvm)) { + (*jvm)->DetachCurrentThread(jvm); + return NULL; + } } } if (result != JNI_OK) { @@ -62,10 +49,14 @@ void logger_callback(rtcLogLevel level, const char* message) { } static jint initialize_jni(JavaVM* jvm) { +#ifdef LIBDATACHANNEL_PRIVATE_ALLOCATOR + extern int initialize_private_allocator(void); + if (!initialize_private_allocator()) return JNI_ERR; +#endif if (jvm_loaded && global_JVM == jvm && !jvm_unloading) { return JNI_VERSION; } - pthread_once(&thread_key_once, create_thread_key); + if (!initialize_jni_thread_tracking()) return JNI_ERR; global_JVM = jvm; jvm_unloading = 0; JNIEnv* env = get_jni_env_from_jvm(jvm); @@ -111,4 +102,5 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* jvm, void* reserved) { module_OnUnload(env); global_JVM = NULL; jvm_loaded = 0; + release_jni_thread_tracking(); } diff --git a/jni/src/mimalloc_prefix_symbols.h b/jni/src/mimalloc_prefix_symbols.h deleted file mode 100644 index 52faa23..0000000 --- a/jni/src/mimalloc_prefix_symbols.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef LIBDATACHANNEL_JNI_MIMALLOC_PREFIX_SYMBOLS_H -#define LIBDATACHANNEL_JNI_MIMALLOC_PREFIX_SYMBOLS_H - -#define mi_aligned_alloc libdatachannel_mi_aligned_alloc -#define mi_aligned_offset_recalloc libdatachannel_mi_aligned_offset_recalloc -#define mi_aligned_recalloc libdatachannel_mi_aligned_recalloc -#define mi_arena_area libdatachannel_mi_arena_area -#define mi_calloc libdatachannel_mi_calloc -#define mi_calloc_aligned libdatachannel_mi_calloc_aligned -#define mi_calloc_aligned_at libdatachannel_mi_calloc_aligned_at -#define mi_cfree libdatachannel_mi_cfree -#define mi_check_owned libdatachannel_mi_check_owned -#define mi_collect libdatachannel_mi_collect -#define mi_debug_show_arenas libdatachannel_mi_debug_show_arenas -#define mi_dupenv_s libdatachannel_mi_dupenv_s -#define mi__expand libdatachannel_mi__expand -#define mi_expand libdatachannel_mi_expand -#define mi_free libdatachannel_mi_free -#define mi_free_aligned libdatachannel_mi_free_aligned -#define mi_free_size libdatachannel_mi_free_size -#define mi_free_size_aligned libdatachannel_mi_free_size_aligned -#define mi_good_size libdatachannel_mi_good_size -#define mi_heap_alloc_new libdatachannel_mi_heap_alloc_new -#define mi_heap_alloc_new_n libdatachannel_mi_heap_alloc_new_n -#define mi_heap_calloc libdatachannel_mi_heap_calloc -#define mi_heap_calloc_aligned libdatachannel_mi_heap_calloc_aligned -#define mi_heap_calloc_aligned_at libdatachannel_mi_heap_calloc_aligned_at -#define mi_heap_check_owned libdatachannel_mi_heap_check_owned -#define mi_heap_collect libdatachannel_mi_heap_collect -#define mi_heap_contains_block libdatachannel_mi_heap_contains_block -#define mi_heap_delete libdatachannel_mi_heap_delete -#define mi_heap_destroy libdatachannel_mi_heap_destroy -#define mi_heap_get_backing libdatachannel_mi_heap_get_backing -#define mi_heap_get_default libdatachannel_mi_heap_get_default -#define mi_heap_malloc libdatachannel_mi_heap_malloc -#define mi_heap_malloc_aligned libdatachannel_mi_heap_malloc_aligned -#define mi_heap_malloc_aligned_at libdatachannel_mi_heap_malloc_aligned_at -#define mi_heap_mallocn libdatachannel_mi_heap_mallocn -#define mi_heap_malloc_small libdatachannel_mi_heap_malloc_small -#define mi_heap_new libdatachannel_mi_heap_new -#define mi_heap_new_in_arena libdatachannel_mi_heap_new_in_arena -#define mi_heap_realloc libdatachannel_mi_heap_realloc -#define mi_heap_realloc_aligned libdatachannel_mi_heap_realloc_aligned -#define mi_heap_realloc_aligned_at libdatachannel_mi_heap_realloc_aligned_at -#define mi_heap_reallocf libdatachannel_mi_heap_reallocf -#define mi_heap_reallocn libdatachannel_mi_heap_reallocn -#define mi_heap_realpath libdatachannel_mi_heap_realpath -#define mi_heap_recalloc libdatachannel_mi_heap_recalloc -#define mi_heap_recalloc_aligned libdatachannel_mi_heap_recalloc_aligned -#define mi_heap_recalloc_aligned_at libdatachannel_mi_heap_recalloc_aligned_at -#define mi_heap_rezalloc libdatachannel_mi_heap_rezalloc -#define mi_heap_rezalloc_aligned libdatachannel_mi_heap_rezalloc_aligned -#define mi_heap_rezalloc_aligned_at libdatachannel_mi_heap_rezalloc_aligned_at -#define mi_heap_set_default libdatachannel_mi_heap_set_default -#define mi_heap_strdup libdatachannel_mi_heap_strdup -#define mi_heap_strndup libdatachannel_mi_heap_strndup -#define mi_heap_try_new libdatachannel_mi_heap_try_new -#define mi_heap_visit_blocks libdatachannel_mi_heap_visit_blocks -#define mi_heap_zalloc libdatachannel_mi_heap_zalloc -#define mi_heap_zalloc_aligned libdatachannel_mi_heap_zalloc_aligned -#define mi_heap_zalloc_aligned_at libdatachannel_mi_heap_zalloc_aligned_at -#define mi_is_in_heap_region libdatachannel_mi_is_in_heap_region -#define mi_is_redirected libdatachannel_mi_is_redirected -#define mi_malloc libdatachannel_mi_malloc -#define mi_malloc_aligned libdatachannel_mi_malloc_aligned -#define mi_malloc_aligned_at libdatachannel_mi_malloc_aligned_at -#define mi_malloc_good_size libdatachannel_mi_malloc_good_size -#define mi_mallocn libdatachannel_mi_mallocn -#define mi_malloc_size libdatachannel_mi_malloc_size -#define mi_malloc_small libdatachannel_mi_malloc_small -#define mi_malloc_usable_size libdatachannel_mi_malloc_usable_size -#define mi_manage_os_memory libdatachannel_mi_manage_os_memory -#define mi_manage_os_memory_ex libdatachannel_mi_manage_os_memory_ex -#define mi_mbsdup libdatachannel_mi_mbsdup -#define mi_memalign libdatachannel_mi_memalign -#define mi_new libdatachannel_mi_new -#define mi_new_aligned libdatachannel_mi_new_aligned -#define mi_new_aligned_nothrow libdatachannel_mi_new_aligned_nothrow -#define mi_new_n libdatachannel_mi_new_n -#define mi_new_nothrow libdatachannel_mi_new_nothrow -#define mi_new_realloc libdatachannel_mi_new_realloc -#define mi_new_reallocn libdatachannel_mi_new_reallocn -#define mi_option_disable libdatachannel_mi_option_disable -#define mi_option_enable libdatachannel_mi_option_enable -#define mi_option_get libdatachannel_mi_option_get -#define mi_option_get_clamp libdatachannel_mi_option_get_clamp -#define mi_option_get_size libdatachannel_mi_option_get_size -#define mi_option_is_enabled libdatachannel_mi_option_is_enabled -#define mi_option_set libdatachannel_mi_option_set -#define mi_option_set_default libdatachannel_mi_option_set_default -#define mi_option_set_enabled libdatachannel_mi_option_set_enabled -#define mi_option_set_enabled_default libdatachannel_mi_option_set_enabled_default -#define mi_posix_memalign libdatachannel_mi_posix_memalign -#define mi_process_info libdatachannel_mi_process_info -#define mi_process_init libdatachannel_mi_process_init -#define mi_pvalloc libdatachannel_mi_pvalloc -#define mi_realloc libdatachannel_mi_realloc -#define mi_realloc_aligned libdatachannel_mi_realloc_aligned -#define mi_realloc_aligned_at libdatachannel_mi_realloc_aligned_at -#define mi_reallocarr libdatachannel_mi_reallocarr -#define mi_reallocarray libdatachannel_mi_reallocarray -#define mi_reallocf libdatachannel_mi_reallocf -#define mi_reallocn libdatachannel_mi_reallocn -#define mi_realpath libdatachannel_mi_realpath -#define mi_recalloc libdatachannel_mi_recalloc -#define mi_recalloc_aligned libdatachannel_mi_recalloc_aligned -#define mi_recalloc_aligned_at libdatachannel_mi_recalloc_aligned_at -#define mi_register_deferred_free libdatachannel_mi_register_deferred_free -#define mi_register_error libdatachannel_mi_register_error -#define mi_register_output libdatachannel_mi_register_output -#define mi_reserve_huge_os_pages libdatachannel_mi_reserve_huge_os_pages -#define mi_reserve_huge_os_pages_at libdatachannel_mi_reserve_huge_os_pages_at -#define mi_reserve_huge_os_pages_at_ex libdatachannel_mi_reserve_huge_os_pages_at_ex -#define mi_reserve_huge_os_pages_interleave libdatachannel_mi_reserve_huge_os_pages_interleave -#define mi_reserve_os_memory libdatachannel_mi_reserve_os_memory -#define mi_reserve_os_memory_ex libdatachannel_mi_reserve_os_memory_ex -#define mi_rezalloc libdatachannel_mi_rezalloc -#define mi_rezalloc_aligned libdatachannel_mi_rezalloc_aligned -#define mi_rezalloc_aligned_at libdatachannel_mi_rezalloc_aligned_at -#define mi_stats_merge libdatachannel_mi_stats_merge -#define mi_stats_print libdatachannel_mi_stats_print -#define mi_stats_print_out libdatachannel_mi_stats_print_out -#define mi_stats_reset libdatachannel_mi_stats_reset -#define mi_strdup libdatachannel_mi_strdup -#define mi_strndup libdatachannel_mi_strndup -#define mi_thread_done libdatachannel_mi_thread_done -#define mi_thread_init libdatachannel_mi_thread_init -#define mi_thread_stats_print_out libdatachannel_mi_thread_stats_print_out -#define mi_usable_size libdatachannel_mi_usable_size -#define mi_valloc libdatachannel_mi_valloc -#define mi_version libdatachannel_mi_version -#define mi_wcsdup libdatachannel_mi_wcsdup -#define mi_wdupenv_s libdatachannel_mi_wdupenv_s -#define mi_zalloc libdatachannel_mi_zalloc -#define mi_zalloc_aligned libdatachannel_mi_zalloc_aligned -#define mi_zalloc_aligned_at libdatachannel_mi_zalloc_aligned_at -#define mi_zalloc_small libdatachannel_mi_zalloc_small - -#endif diff --git a/jni/src/mimalloc_redirect.h b/jni/src/mimalloc_redirect.h index b532d82..839f698 100644 --- a/jni/src/mimalloc_redirect.h +++ b/jni/src/mimalloc_redirect.h @@ -1,14 +1,20 @@ #ifndef LIBDATACHANNEL_JNI_MIMALLOC_REDIRECT_H #define LIBDATACHANNEL_JNI_MIMALLOC_REDIRECT_H -#if !defined(__ANDROID__) -#include "mimalloc_prefix_symbols.h" -#include - -#define malloc mi_malloc -#define calloc mi_calloc -#define realloc mi_realloc -#define free mi_free +#if defined(LIBDATACHANNEL_PRIVATE_ALLOCATOR) && !defined(LIBDATACHANNEL_ALLOCATOR_IMPLEMENTATION) +/* Parse the platform declarations before the function-like redirect macros. */ +#if defined(_WIN32) && !defined(_CRT_RAND_S) +/* usrsctp enables rand_s before its normal stdlib include. Preserve that + * declaration when this forced preamble includes stdlib first. */ +#define _CRT_RAND_S 1 +#endif +#include +#include +#include +#ifdef _WIN32 +#include +#endif +#include #endif #endif diff --git a/jni/src/private_allocator.cpp b/jni/src/private_allocator.cpp new file mode 100644 index 0000000..b8fb9b1 --- /dev/null +++ b/jni/src/private_allocator.cpp @@ -0,0 +1,19 @@ +#include +#include + +namespace { +void* crypto_malloc(size_t size, const char*, int) { return mi_malloc(size); } +void* crypto_realloc(void* memory, size_t size, const char*, int) { + return mi_realloc(memory, size); +} +void crypto_free(void* memory, const char*, int) { mi_free(memory); } +} + +extern "C" int initialize_private_allocator(void) { + CRYPTO_malloc_fn allocate; + CRYPTO_realloc_fn resize; + CRYPTO_free_fn release; + CRYPTO_get_mem_functions(&allocate, &resize, &release); + if (allocate == crypto_malloc && resize == crypto_realloc && release == crypto_free) return 1; + return CRYPTO_set_mem_functions(crypto_malloc, crypto_realloc, crypto_free); +} diff --git a/jni/src/private_malloc.c b/jni/src/private_malloc.c new file mode 100644 index 0000000..60238bb --- /dev/null +++ b/jni/src/private_malloc.c @@ -0,0 +1,67 @@ +#define LIBDATACHANNEL_ALLOCATOR_IMPLEMENTATION 1 +#include + +#ifdef __APPLE__ +#include +#include +#include +#include +#endif + +/* These definitions are localized by the JNI export allow-list. They also + * resolve allocation calls in the embedded static C++ runtime and OpenSSL. */ +void* malloc(size_t size) { return mi_malloc(size); } +void* calloc(size_t count, size_t size) { return mi_calloc(count, size); } +void* realloc(void* memory, size_t size) { return mi_realloc(memory, size); } +void free(void* memory) { mi_free(memory); } +char* strdup(const char* text) { return mi_strdup(text); } +char* strndup(const char* text, size_t size) { return mi_strndup(text, size); } +void* aligned_alloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); } +int posix_memalign(void** memory, size_t alignment, size_t size) { + return mi_posix_memalign(memory, alignment, size); +} + +#ifdef __APPLE__ +/* libc++ uses this for floating-point formatting. Its result is released by + * the private C++ runtime, so the allocation must have the same owner. */ +int asprintf_l(char** output, locale_t locale, const char* format, ...) { + *output = NULL; + va_list arguments; + va_start(arguments, format); + va_list measure; + va_copy(measure, arguments); + int size = vsnprintf_l(NULL, 0, locale, format, measure); + va_end(measure); + if (size < 0) { + va_end(arguments); + return -1; + } + char* memory = mi_malloc((size_t) size + 1); + if (!memory) { + va_end(arguments); + errno = ENOMEM; + return -1; + } + int written = vsnprintf_l(memory, (size_t) size + 1, locale, format, arguments); + va_end(arguments); + if (written < 0 || written > size) { + mi_free(memory); + return -1; + } + *output = memory; + return written; +} +#endif + +#ifdef _WIN32 +/* MinGW's static runtime can also use CRT import-address references. + * Resolve its import-address symbols at link time inside this DLL; no process + * import table or CRT implementation is changed. */ +void* (__cdecl* __imp_malloc)(size_t) = mi_malloc; +void* (__cdecl* __imp_calloc)(size_t, size_t) = mi_calloc; +void* (__cdecl* __imp_realloc)(void*, size_t) = mi_realloc; +void (__cdecl* __imp_free)(void*) = mi_free; +char* _strdup(const char* text) { return mi_strdup(text); } +char* (__cdecl* __imp__strdup)(const char*) = _strdup; +char* (__cdecl* __imp_strdup)(const char*) = strdup; +#endif diff --git a/jni/src/thread_attachment.cpp b/jni/src/thread_attachment.cpp new file mode 100644 index 0000000..5fe31ae --- /dev/null +++ b/jni/src/thread_attachment.cpp @@ -0,0 +1,93 @@ +#include "global_jvm.h" +#ifdef _WIN32 +#include +#include +#endif + +namespace { + +#ifdef _WIN32 +DWORD attachment_key = FLS_OUT_OF_INDEXES; + +struct ThreadAttachment { + JavaVM* vm; + DWORD owner; +}; + +void NTAPI detach_thread(void* value) { + auto* attachment = static_cast(value); + // FlsFree can invoke callbacks for other fibers/threads on its caller. + // Only the owning thread may detach its JVM attachment. + if (attachment->owner == GetCurrentThreadId()) { + JNIEnv* env = nullptr; + if (attachment->vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_OK) { + attachment->vm->DetachCurrentThread(); + } + } + delete attachment; +} +#else +struct ThreadAttachment { + JavaVM* vm = nullptr; + + ~ThreadAttachment() { + if (vm != nullptr) { + vm->DetachCurrentThread(); + } + } +}; +#endif + +} // namespace + +extern "C" int initialize_jni_thread_tracking(void) { +#ifdef _WIN32 + if (attachment_key != FLS_OUT_OF_INDEXES) return 1; + attachment_key = FlsAlloc(detach_thread); + return attachment_key != FLS_OUT_OF_INDEXES; +#else + return 1; +#endif +} + +extern "C" int register_jni_thread(JavaVM* jvm) { +#ifdef _WIN32 + // MinGW runs C++ thread_local destructors from DLL_THREAD_DETACH under + // the loader lock. JNI detachment can wait for a JVM safepoint, creating + // a lock cycle with JVM threads that are exiting. FLS callbacks avoid + // that DLL notification path. Keep the context independent of CRT TLS. + if (attachment_key == FLS_OUT_OF_INDEXES) return 0; + auto* attachment = static_cast(FlsGetValue(attachment_key)); + if (attachment != nullptr) { + attachment->vm = jvm; + return 1; + } + attachment = new (std::nothrow) ThreadAttachment{jvm, GetCurrentThreadId()}; + if (attachment == nullptr) return 0; + if (!FlsSetValue(attachment_key, attachment)) { + delete attachment; + return 0; + } +#else + // C++ thread-local destruction runs before pthread-key cleanup on Darwin. + // A pthread destructor is too late there: HotSpot has already cleared its + // thread-local JVM state, so DetachCurrentThread cannot release the thread. + // Register only attachments created by this library; Java-owned threads + // and attachments belonging to another native caller keep their owner. + static thread_local ThreadAttachment attachment; + attachment.vm = jvm; +#endif + return 1; +} + +extern "C" void release_jni_thread_tracking(void) { +#ifdef _WIN32 + // JNI_OnUnload first stops native workers, then unregisters the callback + // while this DLL and its private allocator are still loaded. + if (attachment_key != FLS_OUT_OF_INDEXES) { + DWORD key = attachment_key; + attachment_key = FLS_OUT_OF_INDEXES; + FlsFree(key); + } +#endif +} diff --git a/jni/src/util.c b/jni/src/util.c index 4f9114c..5ccf46b 100644 --- a/jni/src/util.c +++ b/jni/src/util.c @@ -2,7 +2,7 @@ #include #include #include -#if !defined(__ANDROID__) +#ifdef LIBDATACHANNEL_PRIVATE_ALLOCATOR #include #endif #include @@ -72,15 +72,15 @@ JNIEXPORT void JNICALL Java_tel_schich_libdatachannel_LibDataChannel_freeMemory( JNIEXPORT jstring JNICALL Java_tel_schich_libdatachannel_LibDataChannel_getInnerAllocatorNative(JNIEnv* env, jclass clazz) { (void) clazz; -#if !defined(__ANDROID__) +#ifdef LIBDATACHANNEL_PRIVATE_ALLOCATOR const int mimalloc_version = mi_version(); const int major = mimalloc_version / 100; const int minor = (mimalloc_version / 10) % 10; const int patch = mimalloc_version % 10; char allocator_name[64]; - int written = snprintf(allocator_name, sizeof(allocator_name), "mimalloc+%d.%d.%d", major, minor, patch); + int written = snprintf(allocator_name, sizeof(allocator_name), "mimalloc-secure+%d.%d.%d", major, minor, patch); if (written <= 0 || written >= (int) sizeof(allocator_name)) { - return (*env)->NewStringUTF(env, "mimalloc"); + return (*env)->NewStringUTF(env, "mimalloc-secure"); } return (*env)->NewStringUTF(env, allocator_name); #else diff --git a/jni/src/windows_entry.c b/jni/src/windows_entry.c new file mode 100644 index 0000000..e2feb70 --- /dev/null +++ b/jni/src/windows_entry.c @@ -0,0 +1,10 @@ +#include + +// JNI owns its CRT entry point; embedded dependencies use their static initialization. +// JVM initialization belongs in JNI_OnLoad, after the loader has finished. +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) { + (void) instance; + (void) reason; + (void) reserved; + return TRUE; +} diff --git a/jni/tests/allocator_diagnostics.c b/jni/tests/allocator_diagnostics.c new file mode 100644 index 0000000..aa08007 --- /dev/null +++ b/jni/tests/allocator_diagnostics.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +/* No tracking callback allocates, logs, unwinds stacks or calls the JVM. These + * counters are private to this JNI image, including before JNI_OnLoad. */ +static _Atomic uint64_t allocations; +static _Atomic uint64_t releases; +static _Atomic uint64_t requested_bytes; +static _Atomic uint64_t violations; +static _Atomic uint64_t alignments; + +static void check_owner(const void* memory) { + if (memory && !mi_is_in_heap_region(memory)) + atomic_fetch_add_explicit(&violations, 1, memory_order_relaxed); +} + +void libdatachannel_trace_allocate(const void* memory, size_t requested) { + if (!memory) return; + check_owner(memory); + atomic_fetch_add_explicit(&allocations, 1, memory_order_relaxed); + atomic_fetch_add_explicit(&requested_bytes, requested, memory_order_relaxed); +} + +void libdatachannel_trace_free(const void* memory) { + if (!memory) return; + check_owner(memory); + atomic_fetch_add_explicit(&releases, 1, memory_order_relaxed); +} + +void libdatachannel_trace_align(const void* memory) { + check_owner(memory); + atomic_fetch_add_explicit(&alignments, 1, memory_order_relaxed); +} + +JNIEXPORT jlongArray JNICALL +Java_tel_schich_libdatachannel_NativeAllocatorDiagnosticsTest_snapshot(JNIEnv* env, jclass type) { + (void) type; + jlong values[] = { + (jlong) atomic_load_explicit(&allocations, memory_order_relaxed), + (jlong) atomic_load_explicit(&releases, memory_order_relaxed), + (jlong) atomic_load_explicit(&requested_bytes, memory_order_relaxed), + (jlong) atomic_load_explicit(&violations, memory_order_relaxed), + (jlong) atomic_load_explicit(&alignments, memory_order_relaxed) + }; + jlongArray result = (*env)->NewLongArray(env, 5); + if (result) (*env)->SetLongArrayRegion(env, result, 0, 5, values); + return result; +} + +JNIEXPORT void JNICALL +Java_tel_schich_libdatachannel_NativeAllocatorDiagnosticsTest_checkForeignBuffer( + JNIEnv* env, jclass type, jobject memory) { + (void) type; + /* Deliberately inject an ownership violation without freeing JVM memory. */ + check_owner((*env)->GetDirectBufferAddress(env, memory)); +} + +JNIEXPORT void JNICALL +Java_tel_schich_libdatachannel_NativeAllocatorDiagnosticsTest_finish(JNIEnv* env, jclass type) { + (void) env; + (void) type; + rtcCleanup(); +} diff --git a/jni/tests/allocator_diagnostics_hooks.h b/jni/tests/allocator_diagnostics_hooks.h new file mode 100644 index 0000000..7560db8 --- /dev/null +++ b/jni/tests/allocator_diagnostics_hooks.h @@ -0,0 +1,31 @@ +#ifndef LIBDATACHANNEL_ALLOCATOR_DIAGNOSTICS_HOOKS_H +#define LIBDATACHANNEL_ALLOCATOR_DIAGNOSTICS_HOOKS_H + +/* Load the pinned allocator's definitions before adapting its documented + * tracking macros. This header is forced only into the diagnostic Mi target. */ +#include + +#ifdef __cplusplus +extern "C" { +#endif +void libdatachannel_trace_allocate(const void* memory, size_t requested); +void libdatachannel_trace_free(const void* memory); +void libdatachannel_trace_align(const void* memory); +#ifdef __cplusplus +} +#endif + +#undef MI_TRACK_ENABLED +#undef MI_TRACK_HEAP_DESTROY +#undef MI_TRACK_TOOL +#undef mi_track_malloc_size +#undef mi_track_free_size +#undef mi_track_align +#define MI_TRACK_ENABLED 1 +#define MI_TRACK_HEAP_DESTROY 1 +#define MI_TRACK_TOOL "libdatachannel-ci" +#define mi_track_malloc_size(p, requested, size, zero) libdatachannel_trace_allocate(p, requested) +#define mi_track_free_size(p, size) libdatachannel_trace_free(p) +#define mi_track_align(p, aligned, offset, size) libdatachannel_trace_align(aligned) + +#endif diff --git a/jni/tests/c_allocator_probe.c b/jni/tests/c_allocator_probe.c new file mode 100644 index 0000000..cec0209 --- /dev/null +++ b/jni/tests/c_allocator_probe.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int LIBDATACHANNEL_ALLOCATOR_PROBE(void) { + unsigned char* memory = malloc(4096); + unsigned char* zeros = calloc(32, 64); + char* text = strdup("allocation ownership"); + if (!memory || !zeros || !text) abort(); + int valid = mi_is_in_heap_region(memory) && mi_is_in_heap_region(zeros) && mi_is_in_heap_region(text); + memset(memory, 0x5a, 4096); + memory = realloc(memory, 8192); + if (!memory) abort(); + valid = valid && mi_is_in_heap_region(memory) && memory[4095] == 0x5a && zeros[2047] == 0; + free(memory); + free(zeros); + free(text); + return valid; +} diff --git a/jni/tests/dependency_collision.c b/jni/tests/dependency_collision.c new file mode 100644 index 0000000..17f417c --- /dev/null +++ b/jni/tests/dependency_collision.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +#ifdef _WIN32 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT __attribute__((visibility("default"))) +#endif + +/* An unrelated library owns these names. Calling them from JNI is a failure. */ +static void collision(void) { + fputs("FOREIGN_CRYPTO_CALLED_BY_JNI\n", stderr); + _Exit(86); +} +EXPORT unsigned long OpenSSL_version_num(void) { return 0x7eed0001UL; } +EXPORT unsigned long foreign_crypto_self_check(void) { return OpenSSL_version_num(); } +EXPORT int OPENSSL_init_ssl(uint64_t options, const void* settings) { + (void) options; (void) settings; collision(); return 0; +} +EXPORT int OPENSSL_init_crypto(uint64_t options, const void* settings) { + (void) options; (void) settings; collision(); return 0; +} +EXPORT void* SSL_CTX_new(const void* method) { (void) method; collision(); return NULL; } + +/* A foreign allocator must remain independent in both library load orders. */ +static _Thread_local int allocator_self_test; +static _Thread_local unsigned int allocator_calls; + +static void check_allocator_call(void) { + if (!allocator_self_test) { + fputs("FOREIGN_ALLOCATOR_CALLED_BY_JNI\n", stderr); + _Exit(87); + } + ++allocator_calls; +} +EXPORT void* mi_malloc(size_t size) { + check_allocator_call(); return malloc(size); +} +EXPORT void* mi_calloc(size_t count, size_t size) { + check_allocator_call(); return calloc(count, size); +} +EXPORT void* mi_realloc(void* pointer, size_t size) { + check_allocator_call(); return realloc(pointer, size); +} +EXPORT void mi_free(void* pointer) { + check_allocator_call(); free(pointer); +} +EXPORT unsigned long foreign_allocator_self_check(void) { + allocator_self_test = 1; + allocator_calls = 0; + unsigned char* memory = mi_malloc(16); + unsigned char* zeroed = mi_calloc(4, 8); + if (memory == NULL || zeroed == NULL) _Exit(88); + memset(memory, 0x5a, 16); + memory = mi_realloc(memory, 32); + if (memory == NULL) _Exit(88); + int valid = 1; + for (size_t i = 0; i < 32; ++i) { + if (zeroed[i] != 0 || (i < 16 && memory[i] != 0x5a)) valid = 0; + } + mi_free(memory); + mi_free(zeroed); + allocator_self_test = 0; + return valid && allocator_calls == 5 ? 0x7eed0002UL : 0; +} diff --git a/jni/tests/dependency_cpp_collision.cpp b/jni/tests/dependency_cpp_collision.cpp new file mode 100644 index 0000000..4d4c890 --- /dev/null +++ b/jni/tests/dependency_cpp_collision.cpp @@ -0,0 +1,44 @@ +#include +#include +#include + +#ifdef _WIN32 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT __attribute__((visibility("default"))) +#endif + +static thread_local bool self_test; +static thread_local unsigned calls; + +static void check_call() { + if (!self_test) { + std::fputs("FOREIGN_CPP_ALLOCATOR_CALLED_BY_JNI\n", stderr); + std::_Exit(89); + } + ++calls; +} + +EXPORT void* operator new(std::size_t size) { + check_call(); + if (void* memory = std::malloc(size)) return memory; + throw std::bad_alloc(); +} +EXPORT void* operator new[](std::size_t size) { return ::operator new(size); } +EXPORT void operator delete(void* memory) noexcept { check_call(); std::free(memory); } +EXPORT void operator delete[](void* memory) noexcept { ::operator delete(memory); } +EXPORT void operator delete(void* memory, std::size_t) noexcept { ::operator delete(memory); } +EXPORT void operator delete[](void* memory, std::size_t) noexcept { ::operator delete(memory); } + +extern "C" EXPORT unsigned long foreign_cpp_allocator_self_check() { + self_test = true; + calls = 0; + // Explicit calls make the checks observable even with allocation elision. + void* memory = ::operator new(8192); + void* array = ::operator new[](16384); + ::operator delete(memory); + ::operator delete[](array); + bool valid = calls == 4; + self_test = false; + return valid ? 0x7eed0003UL : 0; +} diff --git a/jni/tests/native_allocator_probe.cpp b/jni/tests/native_allocator_probe.cpp new file mode 100644 index 0000000..f9b6445 --- /dev/null +++ b/jni/tests/native_allocator_probe.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" int datachannel_java_allocator_probe(void); +extern "C" int juice_static_allocator_probe(void); +extern "C" int usrsctp_allocator_probe(void); +extern "C" int private_allocator_secure_level(void); + +extern "C" JNIEXPORT jint JNICALL +Java_tel_schich_libdatachannel_NativeAllocatorTest_allocationCoverage(JNIEnv* env, jclass, jobject application_buffer) { + try { + int result = 0; + if (datachannel_java_allocator_probe()) result |= 1; + auto scalar = std::make_unique(); + auto array = std::make_unique(8192); + auto aligned = ::operator new(4096, std::align_val_t(128), std::nothrow); + if (scalar && array && aligned && mi_is_in_heap_region(scalar.get()) && + mi_is_in_heap_region(array.get()) && mi_is_in_heap_region(aligned)) result |= 2; + ::operator delete(aligned, std::align_val_t(128), std::nothrow); + auto message = rtc::make_message(65536); + if (mi_is_in_heap_region(message.get()) && mi_is_in_heap_region(message->data())) result |= 4; + std::string text(4096, 'x'); + bool owned = mi_is_in_heap_region(text.data()); + for (int i = 0; i < 20; ++i) text.append(5000, 'y'); + std::stringstream stream; + stream << text; + std::vector copies(20, stream.str()); + owned = owned && mi_is_in_heap_region(text.data()) && mi_is_in_heap_region(copies.data()) && mi_is_in_heap_region(copies[0].data()); + auto shared = std::make_shared(copies[0]); + std::function callback = [shared, copies] { return shared->size() + copies.size(); }; + // Exceed libc++'s stack formatting buffer and exercise asprintf_l on Mac. + std::ostringstream numbers; + numbers << std::fixed << std::setprecision(512) << 1.25; + const std::string formatted = numbers.str(); + if (owned && mi_is_in_heap_region(shared.get()) && callback() == text.size() + copies.size() && + formatted.size() == 514 && formatted.substr(0, 4) == "1.25" && + formatted.find_first_not_of('0', 4) == std::string::npos && + mi_is_in_heap_region(formatted.data())) result |= 8; + if (juice_static_allocator_probe()) result |= 16; + if (usrsctp_allocator_probe()) result |= 32; + void* crypto = OPENSSL_malloc(4096); + bool crypto_owned = crypto && mi_is_in_heap_region(crypto); + crypto = OPENSSL_realloc(crypto, 8192); + if (crypto_owned && crypto && mi_is_in_heap_region(crypto)) result |= 64; + OPENSSL_free(crypto); + if (private_allocator_secure_level() >= 4) result |= 128; + void* application_memory = env->GetDirectBufferAddress(application_buffer); + if (application_memory && !mi_is_in_heap_region(application_memory)) result |= 256; + volatile size_t impossible_size = std::numeric_limits::max(); + bool throws_bad_alloc = false; + try { + void* impossible = ::operator new(impossible_size); + ::operator delete(impossible); + } catch (const std::bad_alloc& error) { + throws_bad_alloc = true; + if (mi_is_in_heap_region(&error)) result |= 1024; + } + void* nothrow_result = ::operator new(impossible_size, std::nothrow); + if (throws_bad_alloc && !nothrow_result) result |= 512; + ::operator delete(nothrow_result, std::nothrow); + return result; + } catch (const std::exception& error) { + env->ThrowNew(env->FindClass("java/lang/IllegalStateException"), error.what()); + return 0; + } +} diff --git a/jni/tests/secure_allocator_probe.c b/jni/tests/secure_allocator_probe.c new file mode 100644 index 0000000..a728ea5 --- /dev/null +++ b/jni/tests/secure_allocator_probe.c @@ -0,0 +1,5 @@ +/* Compiled as part of mimalloc-static, with that target's actual definitions. */ +#ifndef MI_SECURE +#error The private desktop allocator must be compiled in secure mode +#endif +int private_allocator_secure_level(void) { return MI_SECURE; } diff --git a/jni/tests/thread_attachment_probe.cpp b/jni/tests/thread_attachment_probe.cpp new file mode 100644 index 0000000..ec6843a --- /dev/null +++ b/jni/tests/thread_attachment_probe.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace { + +using EnvironmentFunction = JNIEnv* (*)(JavaVM*); + +EnvironmentFunction environment_function(JNIEnv* env, jstring library) { + const char* path = env->GetStringUTFChars(library, nullptr); + if (path == nullptr) return nullptr; + EnvironmentFunction function = nullptr; +#ifdef _WIN32 + HMODULE module = GetModuleHandleA(path); + FARPROC symbol = module == nullptr ? nullptr : GetProcAddress(module, "get_jni_env_from_jvm"); +#else + void* module = dlopen(path, RTLD_NOW | RTLD_NOLOAD); + void* symbol = module == nullptr ? nullptr : dlsym(module, "get_jni_env_from_jvm"); +#endif + static_assert(sizeof(function) == sizeof(symbol), "Native function pointer width"); + std::memcpy(&function, &symbol, sizeof(function)); +#ifndef _WIN32 + if (module != nullptr) dlclose(module); +#endif + env->ReleaseStringUTFChars(library, path); + return function; +} + +} // namespace + +extern "C" JNIEXPORT jlong JNICALL +Java_tel_schich_libdatachannel_NativeLibraryLoadingTest_00024Probe_loadForeignLibrary( + JNIEnv* env, jclass target, jstring library) { + (void) target; + const char* path = env->GetStringUTFChars(library, nullptr); + if (path == nullptr) return 0; +#ifdef _WIN32 + auto module = LoadLibraryA(path); +#else + auto module = dlopen(path, RTLD_NOW | RTLD_GLOBAL); +#endif + env->ReleaseStringUTFChars(library, path); + return static_cast(reinterpret_cast(module)); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_tel_schich_libdatachannel_NativeLibraryLoadingTest_00024Probe_foreignDependencyCheck( + JNIEnv* env, jclass target, jlong handle, jstring check) { + (void) target; + if (handle == 0) return 0; + const char* name = env->GetStringUTFChars(check, nullptr); + if (name == nullptr) return 0; +#ifdef _WIN32 + auto symbol = GetProcAddress(reinterpret_cast(static_cast(handle)), + name); +#else + auto symbol = dlsym(reinterpret_cast(static_cast(handle)), + name); +#endif + env->ReleaseStringUTFChars(check, name); + unsigned long (*version)() = nullptr; + static_assert(sizeof(version) == sizeof(symbol), "Native function pointer width"); + std::memcpy(&version, &symbol, sizeof(version)); + return version == nullptr ? 0 : static_cast(version()); +} + +extern "C" JNIEXPORT jint JNICALL +Java_tel_schich_libdatachannel_NativeThreadAttachmentTest_capture(JNIEnv* env, jclass target, + jstring library, jint count) { + if (count <= 0 || count > 32) return -1; + EnvironmentFunction environment = environment_function(env, library); + JavaVM* vm = nullptr; + if (environment == nullptr || env->GetJavaVM(&vm) != JNI_OK) return -2; + jclass callback_class = static_cast(env->NewGlobalRef(target)); + if (callback_class == nullptr) return -3; + jmethodID callback = env->GetStaticMethodID(target, "captureCurrent", "()V"); + if (callback == nullptr) { + env->DeleteGlobalRef(callback_class); + return -4; + } + std::atomic failures{0}; + std::vector threads; + try { + for (int i = 0; i < count; ++i) { + threads.emplace_back([&]() { + JNIEnv* current = environment(vm); + if (current == nullptr || environment(vm) != current) { + ++failures; + return; + } + current->CallStaticVoidMethod(callback_class, callback); + if (current->ExceptionCheck()) { + current->ExceptionClear(); + ++failures; + } + }); + } + } catch (...) { + ++failures; + } + for (std::thread& thread : threads) thread.join(); + env->DeleteGlobalRef(callback_class); + return failures == 0 && threads.size() == static_cast(count) ? count : -5; +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_tel_schich_libdatachannel_NativeThreadAttachmentTest_alreadyAttached(JNIEnv* env, jclass target, + jstring library) { + (void) target; + EnvironmentFunction environment = environment_function(env, library); + JavaVM* vm = nullptr; + return environment != nullptr && env->GetJavaVM(&vm) == JNI_OK && environment(vm) == env + ? JNI_TRUE : JNI_FALSE; +} diff --git a/licenses/GCC-GPL-3.0.txt b/licenses/GCC-GPL-3.0.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/licenses/GCC-GPL-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/licenses/GCC-Runtime-Exception-3.1.txt b/licenses/GCC-Runtime-Exception-3.1.txt new file mode 100644 index 0000000..e1b3c69 --- /dev/null +++ b/licenses/GCC-Runtime-Exception-3.1.txt @@ -0,0 +1,73 @@ +GCC RUNTIME LIBRARY EXCEPTION + +Version 3.1, 31 March 2009 + +Copyright (C) 2009 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +This GCC Runtime Library Exception ("Exception") is an additional +permission under section 7 of the GNU General Public License, version +3 ("GPLv3"). It applies to a given file (the "Runtime Library") that +bears a notice placed by the copyright holder of the file stating that +the file is governed by GPLv3 along with this Exception. + +When you use GCC to compile a program, GCC may combine portions of +certain GCC header files and runtime libraries with the compiled +program. The purpose of this Exception is to allow compilation of +non-GPL (including proprietary) programs to use, in this way, the +header files and runtime libraries covered by this Exception. + +0. Definitions. + +A file is an "Independent Module" if it either requires the Runtime +Library for execution after a Compilation Process, or makes use of an +interface provided by the Runtime Library, but is not otherwise based +on the Runtime Library. + +"GCC" means a version of the GNU Compiler Collection, with or without +modifications, governed by version 3 (or a specified later version) of +the GNU General Public License (GPL) with the option of using any +subsequent versions published by the FSF. + +"GPL-compatible Software" is software whose conditions of propagation, +modification and use would permit combination with GCC in accord with +the license of GCC. + +"Target Code" refers to output from any compiler for a real or virtual +target processor architecture, in executable form or suitable for +input to an assembler, loader, linker and/or execution +phase. Notwithstanding that, Target Code does not include data in any +format that is used as a compiler intermediate representation, or used +for producing a compiler intermediate representation. + +The "Compilation Process" transforms code entirely represented in +non-intermediate languages designed for human-written code, and/or in +Java Virtual Machine byte code, into Target Code. Thus, for example, +use of source code generators and preprocessors need not be considered +part of the Compilation Process, since the Compilation Process can be +understood as starting with the output of the generators or +preprocessors. + +A Compilation Process is "Eligible" if it is done using GCC, alone or +with other GPL-compatible software, or if it is done without using any +work based on GCC. For example, using non-GPL-compatible Software to +optimize any GCC intermediate representations would not qualify as an +Eligible Compilation Process. + +1. Grant of Additional Permission. + +You have permission to propagate a work of Target Code formed by +combining the Runtime Library with Independent Modules, even if such +propagation would otherwise violate the terms of GPLv3, provided that +all Target Code was generated by Eligible Compilation Processes. You +may then convey such a combination under terms of your choice, +consistent with the licensing of the Independent Modules. + +2. No Weakening of GCC Copyleft. + +The availability of this Exception does not imply any general +presumption that third-party software is unaffected by the copyleft +requirements of the license of GCC. + diff --git a/licenses/OpenSSL-3.6.0-Apache-2.0.txt b/licenses/OpenSSL-3.6.0-Apache-2.0.txt new file mode 100644 index 0000000..49cc83d --- /dev/null +++ b/licenses/OpenSSL-3.6.0-Apache-2.0.txt @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/licenses/json-MIT.txt b/licenses/json-MIT.txt new file mode 100644 index 0000000..a1dacc8 --- /dev/null +++ b/licenses/json-MIT.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2025 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/libcxx-20.1.8-Apache-2.0-LLVM.txt b/licenses/libcxx-20.1.8-Apache-2.0-LLVM.txt new file mode 100644 index 0000000..e159d28 --- /dev/null +++ b/licenses/libcxx-20.1.8-Apache-2.0-LLVM.txt @@ -0,0 +1,311 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== + +The libc++ library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/libcxxabi-20.1.8-Apache-2.0-LLVM.txt b/licenses/libcxxabi-20.1.8-Apache-2.0-LLVM.txt new file mode 100644 index 0000000..b75c044 --- /dev/null +++ b/licenses/libcxxabi-20.1.8-Apache-2.0-LLVM.txt @@ -0,0 +1,311 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== + +The libc++abi library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/libdatachannel-MPL-2.0.txt b/licenses/libdatachannel-MPL-2.0.txt new file mode 100644 index 0000000..14e2f77 --- /dev/null +++ b/licenses/libdatachannel-MPL-2.0.txt @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/licenses/libjuice-MPL-2.0.txt b/licenses/libjuice-MPL-2.0.txt new file mode 100644 index 0000000..14e2f77 --- /dev/null +++ b/licenses/libjuice-MPL-2.0.txt @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/licenses/libsrtp-BSD.txt b/licenses/libsrtp-BSD.txt new file mode 100644 index 0000000..af0a2ac --- /dev/null +++ b/licenses/libsrtp-BSD.txt @@ -0,0 +1,35 @@ +/* + * + * Copyright (c) 2001-2017 Cisco Systems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of the Cisco Systems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ diff --git a/licenses/llvm-libc-20.1.8-Apache-2.0-LLVM.txt b/licenses/llvm-libc-20.1.8-Apache-2.0-LLVM.txt new file mode 100644 index 0000000..24806ab --- /dev/null +++ b/licenses/llvm-libc-20.1.8-Apache-2.0-LLVM.txt @@ -0,0 +1,278 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2007-2019 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/licenses/mimalloc-2.5.2-MIT.txt b/licenses/mimalloc-2.5.2-MIT.txt new file mode 100644 index 0000000..53315eb --- /dev/null +++ b/licenses/mimalloc-2.5.2-MIT.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2025 Microsoft Corporation, Daan Leijen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/plog-MIT.txt b/licenses/plog-MIT.txt new file mode 100644 index 0000000..8a91a0a --- /dev/null +++ b/licenses/plog-MIT.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Sergey Podobry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/usrsctp-BSD.txt b/licenses/usrsctp-BSD.txt new file mode 100644 index 0000000..a2d1f98 --- /dev/null +++ b/licenses/usrsctp-BSD.txt @@ -0,0 +1,27 @@ +Copyright (c) 2015, Randall Stewart and Michael Tuexen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of usrsctp nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/winpthreads-MIT-BSD.txt b/licenses/winpthreads-MIT-BSD.txt new file mode 100644 index 0000000..3507701 --- /dev/null +++ b/licenses/winpthreads-MIT-BSD.txt @@ -0,0 +1,57 @@ +Copyright (c) 2011 mingw-w64 project + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +/* + * Parts of this library are derived by: + * + * Posix Threads library for Microsoft Windows + * + * Use at own risk, there is no implied warranty to this code. + * It uses undocumented features of Microsoft Windows that can change + * at any time in the future. + * + * (C) 2010 Lockless Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Lockless Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/settings.gradle.kts b/settings.gradle.kts index b10f272..b1855e8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,3 +14,13 @@ include("ios") project(":ios").name = "libdatachannel-java-ios" includeBuild("example/java") + +providers.gradleProperty("nativeBootstrapDir").orNull?.let { sourceDirectory -> + includeBuild(sourceDirectory) { + dependencySubstitution { + for (moduleName in listOf("native-common", "native-directories", "native-loader", "native-os")) { + substitute(module("org.jmonkeyengine:$moduleName")).using(project(":$moduleName")) + } + } + } +} diff --git a/src/main/java/tel/schich/libdatachannel/DataChannelReliability.java b/src/main/java/tel/schich/libdatachannel/DataChannelReliability.java index 8795b7f..c595264 100644 --- a/src/main/java/tel/schich/libdatachannel/DataChannelReliability.java +++ b/src/main/java/tel/schich/libdatachannel/DataChannelReliability.java @@ -33,7 +33,7 @@ public boolean isUnordered() { } public DataChannelReliability withUnordered(boolean unordered) { - return new DataChannelReliability(unordered, unordered, maxPacketLifeTime, maxRetransmits); + return new DataChannelReliability(unordered, unreliable, maxPacketLifeTime, maxRetransmits); } /** @@ -59,7 +59,7 @@ public Duration maxPacketLifeTime() { } public DataChannelReliability withMaxPacketLifeTime(Duration maxPacketLifeTime) { - return new DataChannelReliability(unordered, unordered, maxPacketLifeTime.toMillis(), maxRetransmits); + return new DataChannelReliability(unordered, unreliable, maxPacketLifeTime.toMillis(), maxRetransmits); } /** @@ -72,6 +72,6 @@ public int maxRetransmits() { } public DataChannelReliability withMaxRetransmits(int maxRetransmits) { - return new DataChannelReliability(unordered, unordered, maxPacketLifeTime, maxRetransmits); + return new DataChannelReliability(unordered, unreliable, maxPacketLifeTime, maxRetransmits); } } diff --git a/src/main/java/tel/schich/libdatachannel/PeerConnection.java b/src/main/java/tel/schich/libdatachannel/PeerConnection.java index 3b3ecd4..fe33965 100644 --- a/src/main/java/tel/schich/libdatachannel/PeerConnection.java +++ b/src/main/java/tel/schich/libdatachannel/PeerConnection.java @@ -114,7 +114,7 @@ public static PeerConnection createPeer(PeerConnectionConfiguration config) { } String bindAddress = null; if (config.bindAddress != null) { - bindAddress = config.bindAddress.toString(); + bindAddress = config.bindAddress.getHostAddress(); } int result = rtcCreatePeerConnection( iceUrisToStrings(config.iceServers), diff --git a/src/main/java/tel/schich/libdatachannel/Platform.java b/src/main/java/tel/schich/libdatachannel/Platform.java index 4558452..01b7073 100644 --- a/src/main/java/tel/schich/libdatachannel/Platform.java +++ b/src/main/java/tel/schich/libdatachannel/Platform.java @@ -6,9 +6,7 @@ import com.jme3.nativebootstrap.loader.NativeResource; import com.jme3.nativebootstrap.os.OperatingSystems; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.logging.Level; @@ -56,7 +54,6 @@ public static void loadNativeLibrary(String name, Class base) { String explicitLibraryPath = System.getProperty(PATH_PROP_PREFIX + name.toLowerCase(Locale.ROOT) + PATH_PROP_FS_PATH); if (explicitLibraryPath != null) { LOGGER.log(Level.FINEST, "Loading native library {0} from {1}", new Object[]{name, explicitLibraryPath}); - loadSiblingDependencies(name, Path.of(explicitLibraryPath).getParent()); System.load(explicitLibraryPath); return; } @@ -116,29 +113,12 @@ public static String libraryFilename(String name) { return libName + ".so"; } - private static List dependentLibraryFilenames(String name) { - if (!LibDataChannel.LIB_NAME.equals(name)) return List.of(); - switch (getOS()) { - case WINDOWS: return List.of("libdatachannel_mimalloc.dll"); - case MACOS: return List.of("libdatachannel_mimalloc.dylib"); - case LINUX: return List.of("libdatachannel_mimalloc.so"); - default: return List.of(); - } - } - private static void loadFromExtractionRoots(String name, Class base, String classPath) { String source = absoluteClassPath(base, classPath); - List binaries = new ArrayList<>(); - for (String dependency : dependentLibraryFilenames(name)) { - String dependencyPath = siblingClassPath(source, dependency); - // Some Linux/Windows packages link the allocator statically; macOS needs its sibling dylib. - if (base.getResource(dependencyPath) == null && !isMacOS()) continue; - binaries.add(NativeResource.fromClasspath(base, dependencyPath)); - } - binaries.add(NativeResource.fromClasspath(base, source)); Path directory = new NativeLoader(path -> System.load(path.toString()), library -> System.loadLibrary(systemLibraryName(library))).load( - NativeDirectories.candidates(name, Platform::getOS), binaries); + NativeDirectories.candidates(name, Platform::getOS), + List.of(NativeResource.fromClasspath(base, source))); if (directory == null) { LOGGER.log(Level.FINEST, "Loaded native library {0} from library path", name); } else { @@ -153,28 +133,9 @@ private static String absoluteClassPath(Class base, String classPath) { } static String systemLibraryName(String name) { - // Windows classifiers use lib-prefixed DLLs, unlike System.mapLibraryName. - return isWindows() && name.startsWith("lib") ? name.substring(3) : name; - } - - private static void loadSiblingDependencies(String name, Path directory) { - if (directory == null) { - return; - } - for (String dependency : dependentLibraryFilenames(name)) { - final Path dependencyPath = directory.resolve(dependency); - if (Files.exists(dependencyPath)) { - System.load(dependencyPath.toAbsolutePath().toString()); - } - } - } - - private static String siblingClassPath(String classPath, String fileName) { - final int slashIndex = classPath.lastIndexOf('/'); - if (slashIndex < 0) { - return fileName; - } - return classPath.substring(0, slashIndex + 1) + fileName; + OperatingSystem os = getOS(); + // Keep the JNI library's existing logical Windows name. + return os == OperatingSystem.WINDOWS && name.startsWith("lib") ? name.substring(3) : name; } } diff --git a/src/test/java/tel/schich/libdatachannel/DataChannelReliabilityTest.java b/src/test/java/tel/schich/libdatachannel/DataChannelReliabilityTest.java new file mode 100644 index 0000000..e30038f --- /dev/null +++ b/src/test/java/tel/schich/libdatachannel/DataChannelReliabilityTest.java @@ -0,0 +1,80 @@ +package tel.schich.libdatachannel; + +import java.time.Duration; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; + +/** + * Pure transport-value contracts: no native library or RTC connection is needed. + */ +class DataChannelReliabilityTest { + @Test + void changingOrderingPreservesReliabilityAndBothLimits() { + for (boolean unordered : new boolean[]{false, true}) { + for (boolean unreliable : new boolean[]{false, true}) { + DataChannelReliability original = new DataChannelReliability(unordered, unreliable, 250, 7); + DataChannelReliability changed = original.withUnordered(!unordered); + assertNotSame(original, changed); + assertProfile(changed, !unordered, unreliable, 250, 7); + assertProfile(original, unordered, unreliable, 250, 7); + } + } + } + + @Test + void changingRetransmitLimitPreservesOrderingReliabilityAndLifetime() { + for (boolean unordered : new boolean[]{false, true}) { + for (boolean unreliable : new boolean[]{false, true}) { + DataChannelReliability original = new DataChannelReliability(unordered, unreliable, 250, 7); + for (int limit : new int[]{0, 3}) { + DataChannelReliability changed = original.withMaxRetransmits(limit); + assertNotSame(original, changed); + assertProfile(changed, unordered, unreliable, 250, limit); + } + assertProfile(original, unordered, unreliable, 250, 7); + } + } + } + + @Test + void changingLifetimePreservesOrderingReliabilityAndRetransmitLimit() { + for (boolean unordered : new boolean[]{false, true}) { + for (boolean unreliable : new boolean[]{false, true}) { + DataChannelReliability original = new DataChannelReliability(unordered, unreliable, 250, 7); + for (long millis : new long[]{0, 40}) { + DataChannelReliability changed = original.withMaxPacketLifeTime(Duration.ofMillis(millis)); + assertNotSame(original, changed); + assertProfile(changed, unordered, unreliable, millis, 7); + } + assertProfile(original, unordered, unreliable, 250, 7); + } + } + } + + @Test + void reliableUnorderedAndOtherTransportProfilesSurviveEitherLimitSetterOrder() { + for (boolean unordered : new boolean[]{false, true}) { + for (boolean unreliable : new boolean[]{false, true}) { + DataChannelReliability limitsLast = DataChannelReliability.DEFAULT + .withUnordered(unordered).withUnreliable(unreliable) + .withMaxRetransmits(0).withMaxPacketLifeTime(Duration.ZERO); + DataChannelReliability orderingLast = DataChannelReliability.DEFAULT + .withMaxPacketLifeTime(Duration.ZERO).withMaxRetransmits(0) + .withUnreliable(unreliable).withUnordered(unordered); + assertProfile(limitsLast, unordered, unreliable, 0, 0); + assertProfile(orderingLast, unordered, unreliable, 0, 0); + } + } + assertProfile(DataChannelReliability.DEFAULT, false, false, 0, 0); + } + + private static void assertProfile(DataChannelReliability profile, boolean unordered, boolean unreliable, + long lifetimeMillis, int retransmits) { + assertEquals(unordered, profile.isUnordered(), "message ordering changed unexpectedly"); + assertEquals(unreliable, profile.isUnreliable(), "delivery reliability changed unexpectedly"); + assertEquals(Duration.ofMillis(lifetimeMillis), profile.maxPacketLifeTime(), "lifetime changed unexpectedly"); + assertEquals(retransmits, profile.maxRetransmits(), "retransmit limit changed unexpectedly"); + } +} diff --git a/src/test/java/tel/schich/libdatachannel/NativeAllocatorDiagnosticsTest.java b/src/test/java/tel/schich/libdatachannel/NativeAllocatorDiagnosticsTest.java new file mode 100644 index 0000000..1d1bc6d --- /dev/null +++ b/src/test/java/tel/schich/libdatachannel/NativeAllocatorDiagnosticsTest.java @@ -0,0 +1,135 @@ +package tel.schich.libdatachannel; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.ArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Runs only in the dedicated diagnostic task, using disposable JVMs so that + * the deliberately injected foreign pointer cannot taint the positive run. + */ +class NativeAllocatorDiagnosticsTest { + private static native long[] snapshot(); + private static native void checkForeignBuffer(ByteBuffer buffer); + private static native void finish(); + + @Test + void auditsRealTransfersAndRejectsForeignMemory() throws Exception { + Path reports = Path.of(System.getProperty("native.test.allocator.reports")); + Files.createDirectories(reports); + runChild("positive", reports, 0); + runChild("negative", reports, 1); + assertTrue(Files.readString(reports.resolve("positive.jsonl")).contains("\"phase\":\"cleanup\"")); + assertTrue(Files.readString(reports.resolve("negative.jsonl")).contains("\"violations\":1")); + assertTrue(Files.readString(reports.resolve("negative.log")).contains("FOREIGN_POINTER_REJECTED")); + } + + private static void runChild(String mode, Path reports, int expectedExit) throws Exception { + String executable = Platform.isWindows() ? "java.exe" : "java"; + List command = List.of( + Path.of(System.getProperty("java.home"), "bin", executable).toString(), + "-Dlibdatachannel.native.datachannel-java.path=" + System.getProperty("native.test.library.path"), + "-Dlibdatachannel.native.tests=true", "-cp", System.getProperty("native.test.classpath"), + NativeAllocatorDiagnosticsTest.class.getName(), mode, reports.resolve(mode + ".jsonl").toString()); + Path log = reports.resolve(mode + ".log"); + Process process = new ProcessBuilder(command).redirectErrorStream(true).redirectOutput(log.toFile()).start(); + try { + assertTrue(process.waitFor(240, TimeUnit.SECONDS), "Diagnostic JVM timed out: " + log); + assertEquals(expectedExit, process.exitValue(), () -> { + try { return Files.readString(log); } + catch (Exception failure) { return failure.toString(); } + }); + } finally { + if (process.isAlive()) { + process.destroyForcibly(); + process.waitFor(10, TimeUnit.SECONDS); + } + } + } + + public static void main(String[] arguments) throws Exception { + LibDataChannel.initialize(); + assertTrue(LibDataChannel.getInnerAllocator().startsWith("mimalloc-secure+")); + try (PrintWriter report = new PrintWriter(new File(arguments[1]))) { + long[] baseline = snapshot(); + assertTrue(baseline[0] > 0, "Actual allocator hooks must run during initialization"); + record(report, "initialization", new long[5], baseline); + if (arguments[0].equals("negative")) { + checkForeignBuffer(ByteBuffer.allocateDirect(8192)); + long[] rejected = snapshot(); + record(report, "foreign-pointer", baseline, rejected); + assertEquals(1, rejected[3]); + System.err.println("FOREIGN_POINTER_REJECTED"); + throw new AssertionError("Foreign allocation must fail the zero-violations rule"); + } + assertEquals(0, baseline[3]); + phase(report, "ownership-probes", () -> new NativeAllocatorTest() + .routesDesktopNativeAllocationsThroughThePrivateSecureAllocator()); + for (int round = 0; round < 3; ++round) { + phase(report, "small-" + round, () -> transfer(256, 64)); + phase(report, "medium-" + round, () -> transfer(128, 4096)); + phase(report, "fragmented-" + round, () -> transfer(16, 65536)); + } + phase(report, "concurrent-peers", () -> { + ExecutorService executor = Executors.newFixedThreadPool(4); + try { + List> transfers = new ArrayList<>(); + for (int index = 0; index < 4; ++index) { + transfers.add(executor.submit(() -> { + try { transfer(64, 16384); } + catch (Exception failure) { throw new RuntimeException(failure); } + })); + } + for (Future transfer : transfers) transfer.get(60, TimeUnit.SECONDS); + } finally { + executor.shutdownNow(); + assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS)); + } + }); + long[] before = snapshot(); + finish(); + long[] after = snapshot(); + record(report, "cleanup", before, after); + record(report, "total", new long[5], after); + assertEquals(0, after[3], "Foreign pointer observed through native cleanup"); + System.out.println("ALLOCATOR_DIAGNOSTICS_OK"); + } + } + + private static void transfer(int messages, int bytes) throws Exception { + PeerConnectionBindAddressTest.transferOrderedBytesBothWays(messages, bytes); + } + + private static void phase(PrintWriter report, String name, Workload workload) throws Exception { + long[] before = snapshot(); + workload.run(); + long[] after = snapshot(); + record(report, name, before, after); + assertTrue(after[0] > before[0], name + " did not exercise actual Mi allocations"); + assertTrue(after[1] > before[1], name + " did not exercise actual Mi frees"); + assertEquals(0, after[3], name + " observed foreign native memory"); + } + + private static void record(PrintWriter report, String phase, long[] before, long[] after) { + report.printf("{\"phase\":\"%s\",\"allocations\":%d,\"frees\":%d,\"requestedBytes\":%d,\"violations\":%d,\"alignmentAdjustments\":%d}%n", + phase, after[0] - before[0], after[1] - before[1], after[2] - before[2], after[3], after[4] - before[4]); + report.flush(); + } + + @FunctionalInterface + private interface Workload { + void run() throws Exception; + } +} diff --git a/src/test/java/tel/schich/libdatachannel/NativeAllocatorTest.java b/src/test/java/tel/schich/libdatachannel/NativeAllocatorTest.java new file mode 100644 index 0000000..071d7e7 --- /dev/null +++ b/src/test/java/tel/schich/libdatachannel/NativeAllocatorTest.java @@ -0,0 +1,22 @@ +package tel.schich.libdatachannel; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import java.nio.ByteBuffer; + +class NativeAllocatorTest { + private static native int allocationCoverage(ByteBuffer applicationBuffer); + + @Test + void routesDesktopNativeAllocationsThroughThePrivateSecureAllocator() { + LibDataChannel.initialize(); + assertTrue(LibDataChannel.getInnerAllocator().startsWith("mimalloc-secure+")); + ByteBuffer applicationBuffer = ByteBuffer.allocateDirect(8192); + applicationBuffer.putInt(0, 0x12345678); + assertEquals(2047, allocationCoverage(applicationBuffer), + "Coverage bits: JNI C=1, C++ new=2, RTC message=4, C++ runtime=8, juice=16, SCTP=32, OpenSSL=64, secure build=128, application heap separate=256, C++ allocation failure=512, exception storage=1024"); + assertEquals(0x12345678, applicationBuffer.getInt(0)); + } +} diff --git a/src/test/java/tel/schich/libdatachannel/NativeDirectoryIntegrationTest.java b/src/test/java/tel/schich/libdatachannel/NativeDirectoryIntegrationTest.java index 67ee514..2b743b8 100644 --- a/src/test/java/tel/schich/libdatachannel/NativeDirectoryIntegrationTest.java +++ b/src/test/java/tel/schich/libdatachannel/NativeDirectoryIntegrationTest.java @@ -106,15 +106,15 @@ void preservesIosStaticLinkOverride() { } @Test - void mapsWindowsClassifierNamesToLogicalSystemNames() { + void mapsPlatformClassifierNamesToLogicalSystemNames() { String previous = System.getProperty("os.name"); try { System.setProperty("os.name", "Windows 11"); assertEquals("libdatachannel-java.dll", Platform.libraryFilename(LibDataChannel.LIB_NAME)); assertEquals("datachannel-java", Platform.systemLibraryName("libdatachannel-java")); - assertEquals("datachannel_mimalloc", Platform.systemLibraryName("libdatachannel_mimalloc")); System.setProperty("os.name", "Darwin"); assertEquals("datachannel-java", Platform.systemLibraryName("datachannel-java")); + System.setProperty("os.name", "Linux"); } finally { System.setProperty("os.name", previous); } diff --git a/src/test/java/tel/schich/libdatachannel/NativeLibraryLoadingTest.java b/src/test/java/tel/schich/libdatachannel/NativeLibraryLoadingTest.java index c3b1ec1..7399035 100644 --- a/src/test/java/tel/schich/libdatachannel/NativeLibraryLoadingTest.java +++ b/src/test/java/tel/schich/libdatachannel/NativeLibraryLoadingTest.java @@ -11,6 +11,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.ConsoleHandler; @@ -27,10 +28,54 @@ class NativeLibraryLoadingTest { @TempDir Path root; @Test - void loadsFromFreshTempAndCleansUpAtExit() throws Exception { + void classifierBundlesOnlyJni() throws Exception { + try (JarFile jar = new JarFile(System.getProperty("native.test.jar"))) { + List binaries = new ArrayList<>(); + jar.stream().filter(entry -> !entry.isDirectory() && entry.getName().startsWith("native/")) + .forEach(entry -> binaries.add(entry.getName())); + assertEquals(List.of("native/" + Platform.libraryFilename(LibDataChannel.LIB_NAME)), binaries); + } + } + + @Test + void keepsDependenciesPrivateWhenForeignSymbolsAreLoadedFirst() throws Exception { + assertPrivateDependencies("before"); + } + + @Test + void keepsDependenciesPrivateWhenForeignSymbolsAreLoadedAfterwards() throws Exception { + assertPrivateDependencies("after"); + } + + private void assertPrivateDependencies(String order) throws Exception { + // Windows shutdown used to deadlock intermittently in DLL thread detach. + int repetitions = Platform.isWindows() ? 20 : 1; + for (int iteration = 0; iteration < repetitions; ++iteration) { + Path temp = Files.createTempDirectory(root, "temp-"); + // Each of the three actual transfers has its own 30-second deadline. + String output = runProbe(temp, root, null, 0, Path.of(System.getProperty("native.test.jar")), 120, + "-Dnative.test.foreign.dependencies=" + order, + "-Dnative.test.probe.path=" + System.getProperty("native.test.probe.path"), + "-Dnative.test.dependency.collision.path=" + System.getProperty("native.test.dependency.collision.path")); + assertTrue(output.contains("PRIVATE_DEPENDENCIES_OK"), output); + } + } + + @Test + void loadsFromFreshTempAndReleasesExtractedLibrariesAtExit() throws Exception { Path temp = Files.createDirectory(root.resolve("temp")); String output = runProbe(temp, root, null, 0); assertTrue(output.contains(temp.toRealPath().toString()), output); + if (Platform.isWindows()) { + // Mapped DLLs are locked during DeleteOnExit. The exited child must + // release them so the parent can remove its extraction fixture. + Path[] remaining; + try (var paths = Files.walk(temp)) { + remaining = paths.filter(path -> !path.equals(temp)) + .sorted(Comparator.reverseOrder()).toArray(Path[]::new); + } + for (Path path : remaining) Files.delete(path); + } try (var children = Files.list(temp)) { assertEquals(0, children.count(), output); } @@ -80,8 +125,14 @@ void preservesEveryExtractionFailureWhenNoRootWorks() throws Exception { @Test void loadsWithoutMountMetadataOrUserNameLookup() throws Exception { Path temp = Files.createDirectory(root.resolve("temp")); + List options = new ArrayList<>(); + if (Runtime.version().feature() >= 18) { + options.add("-Djava.security.manager=allow"); + } + options.add("-Dnative.test.deny-filestore=true"); + options.add("-Duser.name=?"); String output = runProbe(temp, root, null, 0, Path.of(System.getProperty("native.test.jar")), - "-Djava.security.manager=allow", "-Dnative.test.deny-filestore=true", "-Duser.name=?"); + options.toArray(new String[0])); assertTrue(output.contains(temp.toRealPath().toString()), output); } @@ -116,33 +167,14 @@ private Path nativeJarWithPrefix(String prefix) throws Exception { return jar; } - @Test - void rejectsMacClassifierWithoutAllocatorBeforeLoadingJni() throws Exception { - assumeTrue(Platform.isMacOS()); - Path incomplete = root.resolve("incomplete.jar"); - String resource = "native/" + Platform.libraryFilename(LibDataChannel.LIB_NAME); - try (JarFile original = new JarFile(System.getProperty("native.test.jar")); - ZipOutputStream archive = new ZipOutputStream(Files.newOutputStream(incomplete))) { - archive.putNextEntry(new ZipEntry(resource)); - try (var input = original.getInputStream(original.getJarEntry(resource))) { - input.transferTo(archive); - } - archive.closeEntry(); - } - Path temp = Files.createDirectory(root.resolve("temp")); - String output = runProbe(temp, root, null, 1, incomplete); - assertTrue(output.contains("datachannel_mimalloc"), output); - try (var children = Files.list(temp)) { - assertEquals(0, children.count(), output); - } - } - @Test void fallsBackToSystemLibrariesWhenClasspathResourcesAreAbsent() throws Exception { Path blocked = Files.write(root.resolve("blocked"), new byte[]{1}); Path system = systemLibraries(); - String output = runProbe(blocked, blocked, null, 0, null, "-Djava.library.path=" + system); + String output = runProbe(blocked, blocked, null, 0, null, "-Djava.library.path=" + system, + "-Dnative.test.expected-library-dir=" + system); assertTrue(output.contains("from library path"), output); + if (Platform.isLinux()) assertTrue(output.contains("RELOCATED_NATIVE_OK"), output); } @Test @@ -170,9 +202,11 @@ void disablingOsLibrariesRejectsMissingClasspathResources() throws Exception { } @Test - void preservesExplicitNativePathAndSiblingAllocator() throws Exception { + void preservesExplicitNativePathWithoutLoadingLegacyAllocator() throws Exception { Path blocked = Files.write(root.resolve("blocked"), new byte[]{1}); Path system = systemLibraries(); + String extension = Platform.isWindows() ? ".dll" : Platform.isMacOS() ? ".dylib" : ".so"; + Files.writeString(system.resolve("libdatachannel_mimalloc" + extension), "Unselected legacy allocator"); runProbe(blocked, blocked, null, 0, null, "-Dlibdatachannel.native.datachannel-java.path=" + system.resolve(Platform.libraryFilename(LibDataChannel.LIB_NAME)), "-Dnatives.withOsLibraries=false"); @@ -215,9 +249,16 @@ private String runProbe(Path temp, Path home, String classPathOverride, int expe } private String runProbe(Path temp, Path home, String classPathOverride, int expectedExit, Path nativeJar, String... vmOptions) throws Exception { + return runProbe(temp, home, classPathOverride, expectedExit, nativeJar, 30, vmOptions); + } + + private String runProbe(Path temp, Path home, String classPathOverride, int expectedExit, Path nativeJar, + int timeoutSeconds, String... vmOptions) throws Exception { // Gradle workers use their own classloader; construct the child classpath explicitly. List classPath = new ArrayList<>(); - for (Class type : List.of(LibDataChannel.class, Probe.class, NativeLoader.class, NativeDirectories.class, OperatingSystem.class, OperatingSystems.class)) { + for (Class type : List.of(LibDataChannel.class, Probe.class, NativeLoader.class, NativeDirectories.class, + OperatingSystem.class, OperatingSystems.class, org.junit.jupiter.api.Assertions.class, + org.opentest4j.AssertionFailedError.class, org.junit.platform.commons.util.Preconditions.class)) { classPath.add(Path.of(type.getProtectionDomain().getCodeSource().getLocation().toURI()).toString()); } if (nativeJar != null) classPath.add(nativeJar.toString()); @@ -236,18 +277,48 @@ private String runProbe(Path temp, Path home, String classPathOverride, int expe builder.environment().remove("LOCALAPPDATA"); Process process = builder.start(); try { - assertTrue(process.waitFor(30, TimeUnit.SECONDS), "Native loading probe timed out"); + assertTrue(process.waitFor(timeoutSeconds, TimeUnit.SECONDS), + () -> "Native loading probe timed out: " + readProbeLog(log)); String output = Files.readString(log); assertEquals(expectedExit, process.exitValue(), output); if (expectedExit == 0) assertTrue(output.contains("NATIVE_OK"), output); return output; } finally { process.destroyForcibly(); + assertTrue(process.waitFor(10, TimeUnit.SECONDS), "Native loading probe did not exit"); + } + } + + private static String readProbeLog(Path log) { + try { + return Files.readString(log); + } catch (java.io.IOException failure) { + return failure.toString(); } } public static class Probe { - public static void main(String[] args) { + private static native long loadForeignLibrary(String library); + private static native long foreignDependencyCheck(long handle, String check); + + private static void checkForeignDependencies(long handle) { + if (foreignDependencyCheck(handle, "foreign_crypto_self_check") != 0x7eed0001L + || foreignDependencyCheck(handle, "foreign_allocator_self_check") != 0x7eed0002L + || foreignDependencyCheck(handle, "foreign_cpp_allocator_self_check") != 0x7eed0003L) { + throw new IllegalStateException("Foreign dependency implementation was replaced"); + } + } + + public static void main(String[] args) throws Exception { + String dependencyOrder = System.getProperty("native.test.foreign.dependencies"); + long foreignLibrary = 0; + if (dependencyOrder != null) { + System.load(System.getProperty("native.test.probe.path")); + if (dependencyOrder.equals("before")) { + foreignLibrary = loadForeignLibrary(System.getProperty("native.test.dependency.collision.path")); + checkForeignDependencies(foreignLibrary); + } + } if (Boolean.getBoolean("native.test.android")) { System.setProperty("java.runtime.name", "Android Runtime"); System.setProperty("java.vm.name", "Dalvik"); @@ -271,6 +342,33 @@ public static void main(String[] args) { try (PeerConnection peer = PeerConnection.createPeer(PeerConnectionConfiguration.DEFAULT)) { System.out.println("NATIVE_OK " + LibDataChannel.getInnerAllocator()); } + if (dependencyOrder != null) { + if (dependencyOrder.equals("after")) + foreignLibrary = loadForeignLibrary(System.getProperty("native.test.dependency.collision.path")); + checkForeignDependencies(foreignLibrary); + // Repeated peer teardown exercises JNI allocation/free across callbacks + // while a global library owns the same OpenSSL and mimalloc names. + for (int cycle = 0; cycle < 3; ++cycle) { + System.out.println("PRIVATE_DEPENDENCIES_CYCLE_START " + cycle); + new PeerConnectionBindAddressTest().explicitIpv4BindingTransfersOrderedBytesBothWays(); + checkForeignDependencies(foreignLibrary); + System.out.println("PRIVATE_DEPENDENCIES_CYCLE_OK " + cycle); + } + System.out.println("PRIVATE_DEPENDENCIES_OK " + dependencyOrder); + } + String expectedDirectory = System.getProperty("native.test.expected-library-dir"); + if (Platform.isLinux() && expectedDirectory != null) { + Path directory = Path.of(expectedDirectory).toRealPath(); + List mappings = Files.readAllLines(Path.of("/proc/self/maps")); + for (String name : List.of("libdatachannel-java.so")) { + Path library = directory.resolve(name); + String suffix = " " + library.toRealPath(); + if (mappings.stream().noneMatch(line -> line.endsWith(suffix))) { + throw new IllegalStateException("Native dependency loaded outside installation: " + library); + } + } + System.out.println("RELOCATED_NATIVE_OK " + directory); + } } } } diff --git a/src/test/java/tel/schich/libdatachannel/NativeThreadAttachmentTest.java b/src/test/java/tel/schich/libdatachannel/NativeThreadAttachmentTest.java new file mode 100644 index 0000000..287838f --- /dev/null +++ b/src/test/java/tel/schich/libdatachannel/NativeThreadAttachmentTest.java @@ -0,0 +1,65 @@ +package tel.schich.libdatachannel; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.*; + +class NativeThreadAttachmentTest { + private static final ConcurrentLinkedQueue CAPTURED = new ConcurrentLinkedQueue<>(); + + @BeforeAll + static void loadProbe() { + LibDataChannel.initialize(); + System.load(System.getProperty("native.test.probe.path")); + } + + private static void captureCurrent() { + CAPTURED.add(Thread.currentThread()); + } + + private static native int capture(String library, int count); + + private static native boolean alreadyAttached(String library); + + @Test + void nativeThreadsDetachBeforeTheirPhysicalReturnCompletes() { + CAPTURED.clear(); + assertEquals(32, capture(System.getProperty("native.test.library.path"), 32)); + assertEquals(32, CAPTURED.size()); + Set identities = new HashSet<>(); + for (Thread thread : CAPTURED) { + assertTrue(identities.add(thread.getId())); + assertTrue(thread.isDaemon()); + assertFalse(thread.isAlive(), "Returned native callback must release its Java thread"); + assertEquals(Thread.State.TERMINATED, thread.getState()); + } + } + + @Test + void javaOwnedAttachmentsRemainWithTheirOriginalThreads() throws InterruptedException { + String library = System.getProperty("native.test.library.path"); + Thread caller = Thread.currentThread(); + assertTrue(alreadyAttached(library)); + assertSame(caller, Thread.currentThread()); + assertTrue(caller.isAlive()); + AtomicReference failure = new AtomicReference<>(); + Thread worker = new Thread(() -> { + try { + assertTrue(alreadyAttached(library)); + assertTrue(Thread.currentThread().isAlive()); + } catch (Throwable problem) { + failure.set(problem); + } + }); + worker.start(); + worker.join(10_000); + assertFalse(worker.isAlive(), "Java-owned callback timed out"); + assertNull(failure.get()); + } +} diff --git a/src/test/java/tel/schich/libdatachannel/PeerConnectionBindAddressTest.java b/src/test/java/tel/schich/libdatachannel/PeerConnectionBindAddressTest.java new file mode 100644 index 0000000..f0744d4 --- /dev/null +++ b/src/test/java/tel/schich/libdatachannel/PeerConnectionBindAddressTest.java @@ -0,0 +1,257 @@ +package tel.schich.libdatachannel; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.time.Duration; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Actual JNI/ICE tests, enabled with -Dlibdatachannel.native.tests=true and a built native library. + * No STUN/TURN servers or address rewriting are used. + */ +@EnabledIfSystemProperty(named = "libdatachannel.native.tests", matches = "true") +class PeerConnectionBindAddressTest { + private static final byte[] IPV4 = {127, 0, 0, 1}; + private static final int MESSAGES = 128; + private static final int BYTES = 1024; + + @Test + void nullBindAddressKeepsNativeDefault() { + assertNull(PeerConnectionConfiguration.DEFAULT.bindAddress); + try (PeerConnection peer = PeerConnection.createPeer(PeerConnectionConfiguration.DEFAULT)) { + assertNotNull(peer); + } + } + + @Test + void numericIpv4BindsAndGathersItsActualAddress() throws Exception { + assertBoundCandidate(InetAddress.getByAddress(IPV4)); + } + + @Test + void namedIpv4UsesItsAddressWithoutResolvingItsName() throws Exception { + assertBoundCandidate(InetAddress.getByAddress("unresolvable-bind.invalid", IPV4)); + } + + @Test + void numericIpv6BindsAndGathersItsActualAddress() throws Exception { + assertBoundCandidate(InetAddress.getByAddress(ipv6Loopback())); + } + + @Test + void namedIpv6UsesItsAddressWithoutResolvingItsName() throws Exception { + assertBoundCandidate(InetAddress.getByAddress("unresolvable-bind.invalid", ipv6Loopback())); + } + + private static byte[] ipv6Loopback() { + byte[] address = new byte[16]; + address[15] = 1; + return address; + } + + private static void assertBoundCandidate(InetAddress bindAddress) throws Exception { + PeerConnectionConfiguration configuration = PeerConnectionConfiguration.DEFAULT + .withBindAddress(bindAddress).withDisableAutoNegotiation(true); + assertTrue(configuration.iceServers().isEmpty()); + ArrayBlockingQueue candidates = new ArrayBlockingQueue<>(4); + AtomicReference failure = new AtomicReference<>(); + try (PeerConnection peer = PeerConnection.createPeer(configuration)) { + peer.onLocalCandidate.register((active, candidate, media) -> { + if (!candidates.offer(candidate)) { + failure.compareAndSet(null, new AssertionError("Candidate queue exceeded its bound")); + } + }); + try (DataChannel channel = peer.createDataChannel("bind-address-regression")) { + peer.setLocalDescription("offer"); + String candidate = candidates.poll(5, TimeUnit.SECONDS); + assertNotNull(candidate, "Explicit binding must gather its actual socket address"); + String[] fields = candidate.split(" "); + assertTrue(fields.length >= 8, candidate); + assertEquals("host", fields[7]); + assertArrayEquals(bindAddress.getAddress(), InetAddress.getByName(fields[4]).getAddress()); + assertTrue(Integer.parseInt(fields[5]) > 0); + assertNull(failure.get()); + } + } + } + + @Test + void explicitIpv4BindingTransfersOrderedBytesBothWays() throws Exception { + transferOrderedBytesBothWays(MESSAGES, BYTES); + } + + static void transferOrderedBytesBothWays(int messages, int bytesPerMessage) throws Exception { + PeerConnectionConfiguration configuration = PeerConnectionConfiguration.DEFAULT + .withBindAddress(InetAddress.getByAddress(IPV4)).withDisableAutoNegotiation(true); + assertTrue(configuration.iceServers().isEmpty()); + ArrayBlockingQueue signals = new ArrayBlockingQueue<>(64); + ArrayBlockingQueue echo = new ArrayBlockingQueue<>(messages); + AtomicReference failure = new AtomicReference<>(); + AtomicReference receiver = new AtomicReference<>(); + AtomicInteger received = new AtomicInteger(); + AtomicInteger returned = new AtomicInteger(); + AtomicInteger descriptions = new AtomicInteger(); + AtomicInteger candidates = new AtomicInteger(); + long deadline = System.nanoTime() + Duration.ofSeconds(30).toNanos(); + try (PeerConnection left = PeerConnection.createPeer(configuration); + PeerConnection right = PeerConnection.createPeer(configuration)) { + left.onLocalDescription.register((peer, data, type) -> { + descriptions.incrementAndGet(); + enqueue(signals, new Signal(right, data, type, null), failure); + }); + right.onLocalDescription.register((peer, data, type) -> { + descriptions.incrementAndGet(); + enqueue(signals, new Signal(left, data, type, null), failure); + }); + left.onLocalCandidate.register((peer, data, media) -> { + candidates.incrementAndGet(); + enqueue(signals, new Signal(right, data, null, media), failure); + }); + right.onLocalCandidate.register((peer, data, media) -> { + candidates.incrementAndGet(); + enqueue(signals, new Signal(left, data, null, media), failure); + }); + right.onDataChannel.register((peer, channel) -> { + if (!receiver.compareAndSet(null, channel)) { + failure.compareAndSet(null, new AssertionError("Unexpected extra data channel")); + return; + } + channel.onError.register((active, message) -> + failure.compareAndSet(null, new AssertionError(message))); + channel.onMessage.register(DataChannelCallback.Message.handleBinary((active, bytes) -> { + try { + byte[] copy = checkMessage(bytes, received.get(), messages, bytesPerMessage); + received.incrementAndGet(); + assertTrue(echo.offer(copy), "Echo queue exceeded its bound"); + } catch (Throwable problem) { + failure.compareAndSet(null, problem); + } + })); + }); + try (DataChannel sender = left.createDataChannel("explicit-loopback-regression")) { + sender.onError.register((active, message) -> + failure.compareAndSet(null, new AssertionError(message))); + sender.onMessage.register(DataChannelCallback.Message.handleBinary((active, bytes) -> { + try { + checkMessage(bytes, returned.get(), messages, bytesPerMessage); + returned.incrementAndGet(); + } catch (Throwable problem) { + failure.compareAndSet(null, problem); + } + })); + left.setLocalDescription("offer"); + boolean sent = false; + while (System.nanoTime() < deadline && returned.get() < messages) { + assertNull(failure.get(), "Native callback failed"); + Signal signal; + while ((signal = signals.poll()) != null) { + if (signal.type != null) { + signal.target.setRemoteDescription(signal.data, signal.type); + if (signal.type == SessionDescriptionType.OFFER) { + signal.target.setLocalDescription("answer"); + } + } else { + signal.target.addRemoteCandidate(signal.data, signal.mediaId); + } + } + DataChannel target = receiver.get(); + if (!sent && sender.isOpen() && target != null && target.isOpen()) { + assertLoopback(left.localAddress()); + assertLoopback(left.remoteAddress()); + assertLoopback(right.localAddress()); + assertLoopback(right.remoteAddress()); + for (int index = 0; index < messages; index++) { + sender.sendMessage(direct(payload(index, bytesPerMessage))); + } + sent = true; + } + byte[] next; + while (target != null && target.isOpen() && (next = echo.poll()) != null) { + target.sendMessage(direct(next)); + } + Thread.sleep(2); + } + assertTrue(sent, "Explicitly bound native channel did not open within its bound"); + assertEquals(messages, received.get()); + assertEquals(messages, returned.get()); + assertTrue(descriptions.get() >= 2); + assertTrue(candidates.get() >= 2); + assertNull(failure.get()); + assertEquals(0, sender.bufferedAmount()); + assertEquals(0, receiver.get().bufferedAmount()); + } + } + } + + private static void assertLoopback(InetSocketAddress endpoint) throws Exception { + assertNotNull(endpoint); + // The API intentionally returns unresolved socket addresses; inspect the numeric literal. + String address = endpoint.getHostString(); + assertTrue(address.equals("127.0.0.1") || address.equals("::1") || + address.equals("0:0:0:0:0:0:0:1"), address); + assertTrue(InetAddress.getByName(address).isLoopbackAddress()); + assertTrue(endpoint.getPort() > 0); + } + + private static byte[] checkMessage(ByteBuffer bytes, int expectedIndex, int messages, int bytesPerMessage) { + assertEquals(bytesPerMessage, bytes.remaining()); + byte[] copy = new byte[bytesPerMessage]; + bytes.duplicate().get(copy); + int index = ByteBuffer.wrap(copy).order(ByteOrder.BIG_ENDIAN).getInt(); + assertEquals(expectedIndex, index, "Reliable ordered native delivery changed"); + assertTrue(index < messages); + assertArrayEquals(payload(index, bytesPerMessage), copy); + return copy; + } + + private static byte[] payload(int index, int bytesPerMessage) { + ByteBuffer bytes = ByteBuffer.allocate(bytesPerMessage).order(ByteOrder.BIG_ENDIAN); + bytes.putInt(index); + for (int position = 4; position < bytesPerMessage; position++) { + bytes.put((byte) ((index * 31 + position * 17) ^ 0x5a)); + } + return bytes.array(); + } + + private static ByteBuffer direct(byte[] bytes) { + ByteBuffer result = ByteBuffer.allocateDirect(bytes.length); + result.put(bytes); + result.flip(); + return result; + } + + private static void enqueue(ArrayBlockingQueue signals, Signal signal, + AtomicReference failure) { + if (!signals.offer(signal)) { + failure.compareAndSet(null, new AssertionError("Signaling queue exceeded its bound")); + } + } + + private static final class Signal { + private final PeerConnection target; + private final String data; + private final SessionDescriptionType type; + private final String mediaId; + + private Signal(PeerConnection target, String data, SessionDescriptionType type, String mediaId) { + this.target = target; + this.data = data; + this.type = type; + this.mediaId = mediaId; + } + } +} diff --git a/tools/audit_allocator_imports.py b/tools/audit_allocator_imports.py new file mode 100644 index 0000000..a9fd4a8 --- /dev/null +++ b/tools/audit_allocator_imports.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Reject external desktop heap entry points and verify CI instrumentation isolation.""" +import argparse +import json +from pathlib import Path +import re +import subprocess +import sys + + +HEAP_APIS = { + "malloc", "calloc", "realloc", "reallocarray", "free", "cfree", "strdup", "strndup", + "wcsdup", "_strdup", "_wcsdup", "aligned_alloc", "posix_memalign", "memalign", "valloc", "pvalloc", + "_aligned_malloc", "_aligned_realloc", "_aligned_recalloc", "_aligned_free", "_recalloc", "_expand", + "asprintf", "vasprintf", "asprintf_l", "vasprintf_l", "__libc_malloc", "__libc_calloc", + "__libc_realloc", "__libc_free", "__cxa_allocate_exception", "__cxa_free_exception", + "__cxa_allocate_dependent_exception", "__cxa_free_dependent_exception", + "malloc_size", "malloc_usable_size", "_msize", "_aligned_msize", "_aligned_offset_malloc", + "_aligned_offset_realloc", "_aligned_offset_recalloc", "_malloc_base", "_calloc_base", + "_realloc_base", "_free_base", +} + + +def symbols_from_nm(output, macho=False): + symbols = [] + for line in output.splitlines(): + fields = line.split() + if not fields: + continue + symbol = fields[-1].split("@", 1)[0] + if macho and symbol.startswith("_"): + symbol = symbol[1:] + symbols.append(symbol) + return symbols + + +def symbols_from_pe(output): + symbols = [] + in_dll = False + for line in output.splitlines(): + if re.search(r"(?:The Export Tables|Export Table:|\[Ordinal/Name Pointer\] Table)", line): + break + if "DLL Name:" in line: + in_dll = True + continue + if not in_dll: + continue + # Recent GNU adds an ordinal column before the hint. Older GNU has + # address/hint/name, while LLVM has hint/name without an address. + name = r"([A-Za-z_?][^\s]*)" + match = re.match(r"^\s+[0-9a-fA-F]+\s+(?:[0-9a-fA-F]+|)\s+[0-9a-fA-F]+\s+" + name, line) + if not match: + match = re.match(r"^\s+[0-9a-fA-F]+\s+[0-9a-fA-F]+\s+" + name, line) + if not match: + match = re.match(r"^\s+[0-9]+\s+" + name + r"\s*$", line) + if match: + symbols.append(match.group(1)) + return symbols + + +def forbidden(symbols): + return sorted({symbol for symbol in symbols + if symbol in HEAP_APIS or symbol.startswith(("_Zn", "_Zd", "malloc_zone_"))}) + + +def inspect(binary, nm="nm", objdump="objdump", mode="diagnostics"): + def run(*command): + return subprocess.run(command, check=True, text=True, capture_output=True).stdout + + if binary.suffix.lower() == ".dll": + exports = run(objdump, "-p", str(binary)) + imports = symbols_from_pe(exports) + else: + macho = binary.suffix == ".dylib" + imports = symbols_from_nm(run(nm, "-u", str(binary)), macho) + exports = run(nm, "-gU" if macho else "-D", str(binary)) + if not imports: + raise ValueError("No imports decoded; refusing an unverified native image") + escaped = forbidden(imports) + if escaped: + raise ValueError("External heap entry points: " + ", ".join(escaped)) + diagnostic = "Java_tel_schich_libdatachannel_NativeAllocatorDiagnosticsTest_" in exports + if diagnostic != (mode == "diagnostics"): + raise ValueError("Unexpected allocator diagnostic instrumentation for mode " + mode) + return {"binary": binary.name, "mode": mode, "externalHeapImports": escaped, + "imports": sorted(set(imports)), "diagnosticEntryPoints": diagnostic} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("binary", type=Path) + parser.add_argument("--nm", default="nm") + parser.add_argument("--objdump", default="objdump") + parser.add_argument("--mode", choices=("diagnostics", "normal"), default="diagnostics") + parser.add_argument("--output", type=Path) + arguments = parser.parse_args() + try: + result = inspect(arguments.binary, arguments.nm, arguments.objdump, arguments.mode) + encoded = json.dumps(result, indent=2) + "\n" + if arguments.output: + arguments.output.parent.mkdir(parents=True, exist_ok=True) + arguments.output.write_text(encoded, encoding="utf-8") + print(encoded, end="") + except (OSError, ValueError, subprocess.CalledProcessError) as failure: + print(str(failure), file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/package_native_sources.py b/tools/package_native_sources.py new file mode 100644 index 0000000..7f17f70 --- /dev/null +++ b/tools/package_native_sources.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +"""Package the binding and its initialized native submodules with exact file hashes.""" + +import argparse +import hashlib +import json +import subprocess +import zipfile +from pathlib import Path + + +def git(directory, *arguments): + return subprocess.check_output(["git", "-C", str(directory), *arguments]) + + +def repositories(root, relative=Path(".")): + directory = root / relative + actual = Path(git(directory, "rev-parse", "--show-toplevel").decode().strip()).resolve() + if actual != directory.resolve(): + raise ValueError("Native submodule is not initialized: " + str(relative)) + yield relative, directory + modules = directory / ".gitmodules" + if modules.exists(): + result = subprocess.run(["git", "config", "--file", str(modules), "--get-regexp", + r"submodule\..*\.path"], capture_output=True, text=True, check=False) + if result.returncode not in (0, 1): + raise ValueError("Cannot read submodule paths in " + str(modules)) + for line in result.stdout.splitlines(): + yield from repositories(root, relative / line.split(None, 1)[1]) + + +def package(root, output, require_clean=False): + root = root.resolve() + contents = {} + revisions = {} + for relative, directory in repositories(root): + status = git(directory, "status", "--porcelain=v1", "--untracked-files=normal").decode() + if require_clean and status: + raise ValueError("Release source must be clean and committed: " + str(relative)) + revisions[relative.as_posix()] = { + "commit": git(directory, "rev-parse", "HEAD").decode().strip(), + "modified": bool(status), + } + names = set(git(directory, "ls-files", "--cached", "-z").decode().split("\0")) + if relative == Path("."): + # Include new binding sources without collecting unrelated local files. + source_roots = ("src/", "jni/src/", "jni/tests/", "jni/cmake/", "tools/", "licenses/", + "conventions/src/", "arch-detect/src/", "android/src/", "ios/src/", "example/") + source_files = {"SOURCE-BUILD.txt", "SOURCE-NOTICES.txt", "build.gradle.kts", + "settings.gradle.kts", "gradle.properties"} + others = git(directory, "ls-files", "--others", "--exclude-standard", "-z").decode().split("\0") + names.update(name for name in others if name.startswith(source_roots) or name in source_files) + for name in names: + if not name: + continue + source = directory / name + if source.is_dir(): + continue + if not source.resolve().is_relative_to(root): + raise ValueError("Source link escapes the checkout: " + str(source)) + if not source.is_file(): + # Tracked deletions are represented by absence in the working source. + continue + contents[(relative / name).as_posix()] = source + generated = root / "jni/generated" + if generated.is_dir(): + for source in generated.rglob("*"): + if source.is_file(): + contents[source.relative_to(root).as_posix()] = source + manifest = {"repositories": revisions, "sha256": {}} + output.parent.mkdir(parents=True, exist_ok=True) + temporary = output.with_suffix(output.suffix + ".tmp") + try: + with zipfile.ZipFile(temporary, "w", zipfile.ZIP_DEFLATED) as archive: + for name, source in sorted(contents.items()): + data = source.read_bytes() + manifest["sha256"][name] = hashlib.sha256(data).hexdigest() + entry = zipfile.ZipInfo(name, (2020, 1, 1, 0, 0, 0)) + entry.compress_type = zipfile.ZIP_DEFLATED + entry.external_attr = (0o100755 if source.stat().st_mode & 0o111 else 0o100644) << 16 + archive.writestr(entry, data) + archive.writestr(zipfile.ZipInfo("SOURCE-REVISIONS.json", (2020, 1, 1, 0, 0, 0)), + json.dumps(manifest, sort_keys=True, indent=2) + "\n") + temporary.replace(output) + finally: + temporary.unlink(missing_ok=True) + return manifest + + +def verify_clean(root): + for relative, directory in repositories(root.resolve()): + if git(directory, "status", "--porcelain=v1", "--untracked-files=normal").strip(): + raise ValueError("Release source must be clean and committed: " + str(relative)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--output", type=Path) + parser.add_argument("--require-clean", action="store_true") + parser.add_argument("--check-clean", action="store_true") + args = parser.parse_args() + if args.check_clean: + verify_clean(args.root) + print("Release sources are clean and initialized") + else: + if args.output is None: + parser.error("--output is required when creating an archive") + result = package(args.root, args.output, args.require_clean) + print("Packaged", len(result["sha256"]), "source files from", len(result["repositories"]), "repositories") diff --git a/tools/test_allocator_audit.py b/tools/test_allocator_audit.py new file mode 100644 index 0000000..23f515e --- /dev/null +++ b/tools/test_allocator_audit.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Exercise heap import rejection, including a real foreign-heap shared library.""" +import os +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile +import unittest + +from audit_allocator_imports import forbidden, inspect, symbols_from_nm, symbols_from_pe + + +class AllocatorAuditTest(unittest.TestCase): + def test_elf_versions_and_cpp_operators(self): + imports = symbols_from_nm(" U malloc@GLIBC_2.2.5\n U _Znwm@GLIBCXX_3.4\n U _ZdlPv\n U freeaddrinfo\n") + self.assertEqual(["_ZdlPv", "_Znwm", "malloc"], forbidden(imports)) + + def test_macho_formatting_and_exception_storage(self): + imports = symbols_from_nm("_asprintf_l\n___cxa_allocate_exception\n_freeifaddrs\n", macho=True) + self.assertEqual(["__cxa_allocate_exception", "asprintf_l"], forbidden(imports)) + + def test_pe_does_not_confuse_private_exports_with_imports(self): + headers = ("The Import Tables\n DLL Name: msvcrt.dll\n 1234 0000 _aligned_malloc\n 2345 0001 HeapFree\n" + "The Export Tables\n[Ordinal/Name Pointer] Table\n [ 0] malloc\n") + self.assertEqual(["_aligned_malloc"], forbidden(symbols_from_pe(headers))) + + def test_llvm_pe_hint_name_columns_and_exports(self): + headers = ("The Import Tables:\n DLL Name: msvcrt.dll\n Hint/Ord Name\n" + " 1025 malloc\n 932 free\nExport Table:\n" + " 1 0a000 foreign_heap\n 2 0a010 not_an_import\n") + self.assertEqual(["free", "malloc"], forbidden(symbols_from_pe(headers))) + self.assertEqual(["malloc", "free"], symbols_from_pe(headers)) + + def test_gnu_pe_ordinal_and_hint_columns(self): + headers = ("The Import Tables\n DLL Name: msvcrt.dll\n" + " vma: Ordinal Hint Member-Name Bound-To\n" + " 00a7ba00 001f malloc\n" + " 00a7ba08 00c2 free\n" + " 00a7b014 00a7b108 00000000 00000000 00a7d4bc 00a7ba50\n" + "The Export Tables\n") + self.assertEqual(["malloc", "free"], symbols_from_pe(headers)) + self.assertEqual(["free", "malloc"], forbidden(symbols_from_pe(headers))) + + def test_export_only_output_is_not_mistaken_for_imports(self): + headers = "Export Table:\n 1 0a000 malloc\n 2 0a010 free\n" + self.assertEqual([], symbols_from_pe(headers)) + + def test_actual_foreign_heap_library_is_rejected(self): + compiler = shutil.which("gcc" if os.name == "nt" else "cc") + self.assertIsNotNone(compiler, "A native compiler is required for the negative control") + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + source = root / "foreign.c" + source.write_text("#include \n" + "#if defined(_WIN32)\n__declspec(dllexport)\n#endif\n" + "void* foreign_heap(size_t size) { return malloc(size); }\n") + suffix = ".dll" if os.name == "nt" else ".dylib" if sys.platform == "darwin" else ".so" + binary = root / ("foreign" + suffix) + subprocess.run([compiler, "-dynamiclib" if sys.platform == "darwin" else "-shared", + "-O0", str(source), "-o", str(binary)], check=True) + try: + result = inspect(binary, mode="normal") + except ValueError as failure: + self.assertRegex(str(failure), "External heap entry points:.*malloc") + else: + self.fail("Foreign heap library was accepted: " + str(result)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/test_macos_native.py b/tools/test_macos_native.py index f4f252f..281a832 100644 --- a/tools/test_macos_native.py +++ b/tools/test_macos_native.py @@ -6,7 +6,7 @@ import tempfile import unittest import zipfile -from verify_macos_native import verify, inspect, MAIN, ALLOCATOR +from verify_macos_native import verify, inspect, MAIN def command(kind, text): @@ -17,14 +17,21 @@ def command(kind, text): return struct.pack('<3I', kind, size, minimum) + bytes(minimum - 12) + data + bytes(size - minimum - len(data)) -def image(name, dependencies=(), architecture='arm64', rpath=True): +def image(name, dependencies=(), architecture='arm64', rpath=True, exports=('_JNI_OnLoad',)): commands = [command(0xD, '@rpath/' + name)] commands += [command(0xC, value) for value in dependencies] if rpath: commands.append(command(0x8000001C, '@loader_path')) + symbol_offset = 32 + sum(map(len, commands)) + 24 + strings = b'\0' + symbols = b'' + for symbol in exports: + symbols += struct.pack(' end: raise ValueError('Truncated Mach-O command') command, size = struct.unpack_from('<2I', data, offset) if size < 8 or size % 8 or size > end - offset: raise ValueError('Invalid Mach-O command size') + if command == 0x2: + if size != 24 or symbol_table is not None: + raise ValueError('Invalid Mach-O symbol table command') + symbol_table = struct.unpack_from('<4I', data, offset + 8) if command in (0xD, 0xC, 0x80000018, 0x8000001F, 0x80000023, 0x8000001C): minimum = 12 if command == 0x8000001C else 24 if size < minimum: @@ -55,14 +59,29 @@ def inspect(data, architecture): offset += size if offset != end or len(identifiers) != 1: raise ValueError('Mach-O command size or install ID mismatch') - return {'id': identifiers[0], 'dependencies': dependencies, 'rpaths': paths} + exports = [] + if symbol_table is not None: + symbols, count, strings, string_bytes = symbol_table + if symbols < end or count > (len(data) - symbols) // 16 or strings < end or string_bytes > len(data) - strings: + raise ValueError('Mach-O symbol table bounds') + for index in range(count): + name, kind, section, description, value = struct.unpack_from('= string_bytes: + raise ValueError('Mach-O symbol name bounds') + finish = data.find(b'\0', strings + name, strings + string_bytes) + if finish < 0: + raise ValueError('Unterminated Mach-O symbol name') + exports.append(data[strings + name:finish].decode('utf-8', errors='strict')) + return {'id': identifiers[0], 'dependencies': dependencies, 'rpaths': paths, 'exports': exports} def images(path, architecture): if path.is_dir(): result = {} for item in path.iterdir(): - if item.name.endswith('.dylib'): + if item.name == MAIN: if item.is_symlink() or not item.is_file() or item.stat().st_size > MAX_IMAGE: raise ValueError('Invalid staged dylib: ' + item.name) result[item.name] = item.read_bytes() @@ -81,16 +100,51 @@ def images(path, architecture): return result -def verify(path, architecture): - binaries = images(Path(path), architecture) - for required in (MAIN, ALLOCATOR): - if required not in binaries: - raise ValueError('Missing required Mac native dependency: ' + required) +def prebuilt_images(jni_path, selected_files): + """Apply the packaging JNI alias to exactly the files selected by Gradle.""" + if jni_path.is_symlink() or not jni_path.is_file(): + raise ValueError('Invalid selected prebuilt JNI') + jni_source = jni_path.resolve() + result = {} + selected_jni = False + for item in map(Path, selected_files): + is_jni = item.resolve() == jni_source + name = MAIN if is_jni else item.name + if not is_jni and not name.endswith('.dylib'): + continue + if item.is_symlink() or not item.is_file() or item.stat().st_size > MAX_IMAGE: + raise ValueError('Invalid selected prebuilt library: ' + item.name) + if name in result: + raise ValueError('Duplicate prebuilt native resource: ' + name) + result[name] = item.read_bytes() + selected_jni |= is_jni + if not selected_jni: + raise ValueError('Selected prebuilt JNI is not packaged') + return result + + +def verify(path, architecture, selected_files=None): + binaries = (images(Path(path), architecture) if selected_files is None else + prebuilt_images(Path(path), selected_files)) + if MAIN not in binaries: + raise ValueError('Missing required Mac JNI library') + if set(binaries) != {MAIN}: + raise ValueError('Unexpected shared native dependency in Mac package') decoded = {name: inspect(data, architecture) for name, data in binaries.items()} + exports = decoded[MAIN]['exports'] + if not exports: + raise ValueError('Missing public JNI symbol metadata') + for symbol in exports: + if not (symbol.startswith('_Java_') or symbol in ('_JNI_OnLoad', '_JNI_OnUnload', '_get_jni_env_from_jvm')): + raise ValueError('Non-JNI public export: ' + symbol) for name, metadata in decoded.items(): if metadata['id'] != '@rpath/' + name: raise ValueError('Noncanonical Mac install name: ' + name + ' -> ' + metadata['id']) for dependency in metadata['dependencies']: + if 'libc++' in dependency.rsplit('/', 1)[-1]: + raise ValueError('Desktop JNI must embed its C++ runtime: ' + dependency) + if any(name in dependency.rsplit('/', 1)[-1].lower() for name in ('libssl', 'libcrypto', 'mimalloc')): + raise ValueError('OpenSSL and mimalloc must be private: ' + dependency) if dependency.startswith(('/usr/lib/', '/System/Library/Frameworks/')): continue if not dependency.startswith(('@rpath/', '@loader_path/')): @@ -100,19 +154,19 @@ def verify(path, architecture): raise ValueError('Unclosed Mac native dependency: ' + dependency) if dependency.startswith('@rpath/') and '@loader_path' not in metadata['rpaths']: raise ValueError('Bundled @rpath dependency lacks @loader_path: ' + name) - if '@rpath/' + ALLOCATOR not in decoded[MAIN]['dependencies']: - raise ValueError('JNI must bind the canonical private allocator') - return {'architecture': architecture, 'nativeLibraries': sorted(decoded), 'staticDependencyClosure': True, + return {'architecture': architecture, 'nativeLibraries': sorted(decoded), 'staticDependencyClosure': True, 'privateCrypto': True, 'privateAllocator': True, 'nativeExecution': False} def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('architecture', choices=CPUS) - parser.add_argument('path', type=Path, help='Staged native directory or final classifier JAR') + parser.add_argument('path', type=Path, help='Staged native directory, final classifier JAR or selected prebuilt JNI') + parser.add_argument('--prebuilt', nargs='*', type=Path, metavar='FILE', + help='Exact packaging file selection; alias the selected JNI to its canonical resource name') arguments = parser.parse_args() try: - print(json.dumps(verify(arguments.path, arguments.architecture))) + print(json.dumps(verify(arguments.path, arguments.architecture, arguments.prebuilt))) except (OSError, ValueError, zipfile.BadZipFile, struct.error, UnicodeError) as problem: parser.exit(1, str(problem) + '\n')