-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 80cb8a6
Showing
61 changed files
with
8,760 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# | ||
# https://help.github.com/articles/dealing-with-line-endings/ | ||
# | ||
# Linux start script should use lf | ||
/gradlew text eol=lf | ||
|
||
# These are Windows script files and should use crlf | ||
*.bat text eol=crlf | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
--- | ||
name: Bug report | ||
about: Report an Unsafe Sanitizer bug. | ||
title: '' | ||
labels: bug | ||
assignees: '' | ||
|
||
--- | ||
|
||
### Unsafe Sanitizer version | ||
<!-- Version you are using, respectively Git commit hash, e.g. `ab12cd34` --> | ||
|
||
|
||
### Agent settings | ||
|
||
Agent installation: | ||
- [ ] At JVM start (`-javaagent`) | ||
- [ ] At runtime (`UnsafeSanitizer.installAgent(...)`) | ||
|
||
Settings: | ||
- instrumentation-logging: true | false | ||
- global-native-memory-sanitizer: true | false | ||
- uninitialized-memory-tracking: true | false | ||
- error-action: none | throw | print | print-skip | ||
- call-debug-logging: true | false | ||
|
||
|
||
### Java version | ||
<!-- Full output of `java -version` --> | ||
|
||
|
||
### Description | ||
<!-- Describe the bug you experienced --> | ||
|
||
|
||
### Expected behavior | ||
<!-- What behavior did you expect? --> | ||
|
||
|
||
### Actual behavior | ||
<!-- What happened instead? --> | ||
|
||
|
||
### Example code | ||
<!-- Provide a small self-contained code example for reproducing the bug --> | ||
<!-- Add comments in the code to indicate where an unexpected error was thrown / where an expected error was missing --> | ||
```java | ||
... | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
contact_links: | ||
- name: Usage question & discussion | ||
url: https://github.com/Marcono1234/unsafe-address-sanitizer/discussions | ||
about: Ask usage questions and discuss this Unsafe Sanitizer in GitHub Discussions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
--- | ||
name: Enhancement suggestion | ||
about: Suggest an enhancement for Unsafe Sanitizer. | ||
title: '' | ||
labels: enhancement | ||
assignees: '' | ||
|
||
--- | ||
|
||
### Problem solved by the enhancement | ||
<!-- Describe which problem the suggested enhancement solves --> | ||
|
||
|
||
### Enhancement description | ||
<!-- Describe the suggested enhancement --> | ||
|
||
|
||
### Alternatives / workarounds | ||
<!-- Describe alternatives or workarounds in case you are aware of any --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file | ||
|
||
version: 2 | ||
updates: | ||
- package-ecosystem: "maven" | ||
directory: "/" | ||
schedule: | ||
interval: "weekly" | ||
|
||
- package-ecosystem: "github-actions" | ||
directory: "/" | ||
schedule: | ||
interval: "weekly" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
name: Build | ||
|
||
on: | ||
push: | ||
branches-ignore: | ||
# Ignore Dependabot branches because it will also open a pull request, which would cause the | ||
# workflow to redundantly run twice | ||
- dependabot/** | ||
pull_request: | ||
|
||
|
||
permissions: | ||
contents: read # to fetch code (actions/checkout) | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Check out sources | ||
uses: actions/checkout@v4 | ||
|
||
- name: Validate Gradle wrapper | ||
uses: gradle/actions/wrapper-validation@v3 | ||
|
||
- name: Set up Java | ||
uses: actions/setup-java@v4 | ||
with: | ||
distribution: 'temurin' | ||
java-version: 17 | ||
|
||
- name: Set up Gradle | ||
uses: gradle/actions/setup-gradle@v3 | ||
|
||
- name: Build with Gradle | ||
run: ./gradlew build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Ignore Gradle project-specific cache directory | ||
.gradle | ||
|
||
# Ignore Gradle build output directory | ||
build | ||
|
||
# IntelliJ files | ||
/.idea | ||
/*.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 Marcono1234 | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
# Java `Unsafe` address sanitizer | ||
|
||
Java Agent which validates memory access performed using `sun.misc.Unsafe`. `Unsafe` is a semi-public JDK class | ||
which allows among others allocating native memory and directly accessing memory without bounds checks. It is | ||
sometimes used by libraries for better performance. | ||
|
||
The issue with `Unsafe` is that it does not detect out-of-bounds reads and writes and performs little to no argument | ||
validation. Therefore invalid arguments can break the correctness of an application or even represent a security | ||
vulnerability. | ||
|
||
Note that the memory access methods of `Unsafe` will probably be deprecated and removed in future JDK versions, see | ||
[JEP draft JDK-8323072](https://openjdk.org/jeps/8323072). Libraries targeting newer Java versions should | ||
prefer [`java.lang.foreign.MemorySegment`](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/foreign/MemorySegment.html), | ||
which is a safer alternative to `Unsafe`. | ||
|
||
### Why this sanitizer? | ||
|
||
Invalid `Unsafe` arguments can be noticeable when the JVM crashes due to `SIGSEGV` respectively | ||
`EXCEPTION_ACCESS_VIOLATION`. However, if a unit test or fuzzer runs in the same JVM, then it can most likely not | ||
properly report the failure in case of a JVM crash.\ | ||
And even if no JVM crash occurs, `Unsafe` might have performed out-of-bounds access in the memory of the Java | ||
process. Out-of-bounds reads can lead to non-deterministic behavior due to reading arbitrary data, or it could | ||
leak sensitive information from other parts of the memory. Out-of-bounds writes can corrupt data, which can | ||
lead to incorrect behavior or crashes at completely unrelated code locations later on. | ||
|
||
This sanitizer injects validation checks into the `Unsafe` methods, throwing errors when invalid arguments | ||
are provided. The following is detected: | ||
|
||
- Arrays: | ||
- Out-of-bounds reads and writes | ||
- Bad aligned access (e.g. reading in the middle of a `long` element of a `long[]`) | ||
- Fields: | ||
- No field at the specified offset | ||
- Out-of-bounds reads and writes | ||
- Native memory: | ||
- Out-of-bounds reads and writes ([CWE-125](https://cwe.mitre.org/data/definitions/125.html), [CWE-787](https://cwe.mitre.org/data/definitions/787.html)) | ||
- Reading uninitialized memory ([EXP33-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP33-C.+Do+not+read+uninitialized+memory)) | ||
- Double free ([CWE-415](https://cwe.mitre.org/data/definitions/415.html)) | ||
|
||
> [!WARNING] | ||
> This library is experimental and only intended for testing and fuzzing. Do not use it in production, especially do | ||
> not rely on it as security measure in production. | ||
## How does it work? | ||
|
||
This project is implemented as Java Agent which uses [instrumentation](https://docs.oracle.com/en/java/javase/17/docs/api/java.instrument/java/lang/instrument/package-summary.html) | ||
(more specifically the [Byte Buddy](https://github.com/raphw/byte-buddy) library) to instrument `sun.misc.Unsafe` and | ||
related classes. The `Unsafe` methods are transformed so that all calls are intercepted to first check if the memory | ||
access is valid, handling invalid access depending on the `ErrorAction` configured for the sanitizer. This allows the | ||
sanitizer to react to invalid memory access before it occurs, and before the JVM might crash. | ||
|
||
### Limitations | ||
|
||
- Only usage of the 'public' `sun.misc.Unsafe` is checked, usage of the JDK-internal `jdk.internal.misc.Unsafe` is not | ||
checked\ | ||
This is normally not an issue because third-party code does not (and in recent JDK versions cannot) access the | ||
JDK-internal `Unsafe` class. | ||
- Not all invalid memory access might be detected\ | ||
For example, if there is a dangling pointer but in the meantime another part of the application coincidentally | ||
allocates memory at that address, access with the originally dangling pointer would be considered valid. Similarly, | ||
if out-of-bounds access coincidentally happens to access another unrelated allocated memory section, it would be | ||
considered valid as well. | ||
- Sanitizer is unaware of allocations which occurred before it was installed, and memory which is allocated or freed | ||
through other means than `Unsafe` or `ByteBuffer#allocateDirect`\ | ||
If that allocated memory is accessed afterwards, the sanitizer will consider it invalid access. There are multiple | ||
ways to work around this, such as: | ||
- Installing the agent when the JVM starts, instead of at runtime | ||
- Disabling native memory access sanitization (`AgentSettings.withGlobalNativeMemorySanitizer(false)`), and | ||
optionally instead using `UnsafeSanitizer#withScopedNativeMemoryTracking` | ||
- Manually registering the allocated memory with `UnsafeSanitizer#registerAllocatedMemory` | ||
- This library has mainly been written for the Hotspot JVM\ | ||
It might not work for other JVMs, but bug reports for this are appreciated! | ||
|
||
## Usage | ||
|
||
> [!NOTE] | ||
> This library is currently not published to Maven Central. You have to build it locally, see the [Building](#building) | ||
> section. | ||
(requires Java 17 or newer) | ||
|
||
The sanitizer Java agent has to be installed once to become active. It can either be installed at runtime by calling | ||
`UnsafeSanitizer.installAgent(...)`, or when the JVM is started by adding `-javaagent` to the arguments: | ||
``` | ||
java -javaagent:unsafe-address-sanitizer-standalone-agent.jar -jar my-application.jar | ||
``` | ||
|
||
Using `-javaagent` should be preferred, if possible, because `UnsafeSanitizer.installAgent(...)` might not be supported | ||
by all JVMs and future JDK versions, and it might miss allocations which occurred before the sanitizer was installed, | ||
which could lead to spurious invalid memory access errors. | ||
|
||
When using `-javaagent`, invalid memory access will cause an error by default. The behavior can be customized; to view | ||
all possible options and examples, start the agent as regular JAR (without any additional arguments): | ||
``` | ||
java -jar unsafe-address-sanitizer-standalone-agent.jar | ||
``` | ||
|
||
### Usage with Jazzer | ||
|
||
This sanitizer can be used in combination with the Java fuzzing library [Jazzer](https://github.com/CodeIntelligenceTesting/jazzer), | ||
especially its [JUnit 5 integration](https://github.com/CodeIntelligenceTesting/jazzer?tab=readme-ov-file#junit-5). | ||
|
||
When installing the Unsafe Sanitizer at runtime using `UnsafeSanitizer.installAgent(...)`, it should be called in a | ||
`static { ... }` block in the test class, to only call it once and not for every executed test method. | ||
|
||
Jazzer itself internally uses `sun.misc.Unsafe`. If the Unsafe Sanitizer agent is installed at runtime it might | ||
therefore be necessary to disable sanitization of native memory by using `AgentSettings.withGlobalNativeMemorySanitizer(false)`.\ | ||
If the Unsafe Sanitizer agent has been installed using `-javaagent` this might not be a problem. However, the | ||
sanitizer might nonetheless decrease the Jazzer performance. So unless needed, it might be useful to disable native | ||
memory sanitization. | ||
|
||
## Building | ||
|
||
This project uses Gradle for building. JDK 17 is recommended, but Gradle toolchains are used, so any needed JDK | ||
is downloaded by Gradle automatically. | ||
|
||
``` | ||
./gradlew build | ||
``` | ||
|
||
This generates the file `build/libs/unsafe-address-sanitizer-<version>-standalone-agent.jar` which you can use with the | ||
`-javaagent` JVM argument. Or you can add it as JAR dependency to your project and then install the agent at runtime. | ||
|
||
You can use `./gradlew publishToMavenLocal` to [add the library to your local Maven repository](https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:install). | ||
The artifact coordinates are `marcono1234.unsafe_sanitizer:unsafe-address-sanitizer:<version>`. | ||
|
||
## Similar third-party projects | ||
|
||
- Project https://github.com/serkan-ozal/mysafe \ | ||
Offers more functionality for native memory access tracking, but does not validate array and field access. | ||
- Paper: "Use at your own risk: the Java unsafe API in the wild"\ | ||
Authors: Luis Mastrangelo, Luca Ponzanelli, Andrea Mocci, Michele Lanza, Matthias Hauswirth, Nathaniel Nystrom\ | ||
DOI: [10.1145/2858965.2814313](https://doi.org/10.1145/2858965.2814313) | ||
- Paper: "SafeCheck: safety enhancement of Java unsafe API"\ | ||
Authors: Shiyou Huang, Jianmei Guo, Sanhong Li, Xiang Li, Yumin Qi, Kingsum Chow, Jeff Huang\ | ||
DOI: [10.1109/ICSE.2019.00095](https://doi.org/10.1109/ICSE.2019.00095) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# agent-impl | ||
|
||
Contains the actual implementation of the agent, tracking memory and performing access checks. | ||
|
||
This is a separate sub-project which produces a JAR with dependencies which is then included in the main agent JAR. | ||
The main agent then adds the nested agent-impl JAR to the bootstrap classpath, which is necessary because the | ||
instrumented `Unsafe` methods can only access classes on the bootstrap classpath. | ||
|
||
See also [this Byte Buddy issue comment](https://github.com/raphw/byte-buddy/issues/597#issuecomment-458041738). | ||
|
||
The classes of this sub-project are not part of the public API (regardless of their visibility) and are normally | ||
not directly accessible by user code. Instead, all interaction goes through the public API of the agent. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
plugins { | ||
id("unsafe-sanitizer.java-conventions") | ||
alias(libs.plugins.shadow) | ||
} | ||
|
||
dependencies { | ||
implementation(libs.jetbrains.annotations) | ||
|
||
testImplementation(libs.junit) | ||
testRuntimeOnly(libs.junit.launcher) | ||
} | ||
|
||
tasks.test { | ||
useJUnitPlatform() | ||
} | ||
|
||
tasks.shadowJar { | ||
// Relocate all dependencies to not cause conflicts when the agent JAR is added to the bootstrap classpath | ||
isEnableRelocation = true | ||
relocationPrefix = "marcono1234.unsafe_sanitizer.agent_impl.deps" | ||
duplicatesStrategy = DuplicatesStrategy.FAIL | ||
|
||
// Include own `module-info.class`, see https://github.com/johnrengelman/shadow/issues/710 | ||
excludes.remove("module-info.class") | ||
|
||
// Exclude `module-info` from dependencies, see also https://github.com/johnrengelman/shadow/issues/729 | ||
exclude("META-INF/versions/*/module-info.class") | ||
|
||
// Note: Depending on the dependencies, might have to set `Multi-Release: true`, see https://github.com/johnrengelman/shadow/issues/449 | ||
} | ||
|
||
|
||
java { | ||
// Publish only sources to allow debugging; don't publish Javadoc because this is not public API | ||
withSourcesJar() | ||
} | ||
|
||
publishing { | ||
publications { | ||
create<MavenPublication>("maven") { | ||
// TODO: Maybe revert the following and only publish sources instead (`artifact(tasks["sourcesJar"])`)? | ||
// Would not actually be necessary to publish the JAR since it is included inside the agent, | ||
// and users are not expected to have direct dependency on it; but publish it anyway to allow | ||
// debugging through the code | ||
from(components["java"]) | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
agent-impl/src/main/java/marcono1234/unsafe_sanitizer/agent_impl/AgentErrorAction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package marcono1234.unsafe_sanitizer.agent_impl; | ||
|
||
/** | ||
* Action to perform in case of bad memory access. | ||
*/ | ||
public enum AgentErrorAction { | ||
NONE(true), | ||
THROW(true), // `executeOnError` does not matter since exception is thrown anyway | ||
PRINT(true), | ||
PRINT_SKIP(false), | ||
; | ||
|
||
/** Whether to perform the bad memory access; {@code false} if it should be skipped */ | ||
final boolean executeOnError; | ||
|
||
AgentErrorAction(boolean executeOnError) { | ||
this.executeOnError = executeOnError; | ||
} | ||
} |
Oops, something went wrong.