Skip to content

Java: Add sinks for sun.misc.Unsafe #15247

Closed
@Marcono1234

Description

@Marcono1234

Description of the issue

The sun.misc.Unsafe class provides methods for directly accessing memory using memory addresses. It is sometimes used in libraries to achieve better performance compared to equivalent Java memory access.

From a security perspective Unsafe is interesting because if the memory address is incorrect you can crash the JVM (or possibly worse?). This also makes other bug patterns, such as numeric overflow, interesting in this context.

For example:

var field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
var unsafe = (sun.misc.Unsafe) field.get(null);

// Crashes the JVM
long badAddress = 1000000000L;
unsafe.getInt(badAddress);

What do you think about adding models and a CodeQL class which represents these sinks? Maybe not directly as part of a query (yet) but in a FlowSinks.qll library (similar to the existing FlowSources.qll).
There is already sun.misc.model.yml but it does not cover Unsafe yet.

Possibly this could also be combined with (future) sinks for other APIs which take native memory addresses as arguments.

Here is my hacky CodeQL code to match these sun.misc.Unsafe sinks:

UnsafeSink
class UnsafeSink extends DataFlow::Node {
  UnsafeSink() {
    exists(MethodAccess c, Method m, int argIndex |
      m = c.getMethod() and
      m.getDeclaringType().hasQualifiedName("sun.misc", "Unsafe") and
      c.getArgument(argIndex) = this.asExpr()
    |
      m.getName().matches(["get%", "put%"]) and
      (
        m.getParameterType(0) instanceof TypeObject and argIndex = 1
        or
        m.getParameterType(0).hasName("long") and argIndex = 0
      )
      or
      m.hasName("allocateMemory") and argIndex = 0
      or
      m.hasName("reallocateMemory") and argIndex = [0, 1]
      or
      m.hasName("setMemory") and
      (
        m.getParameterType(0) instanceof TypeObject and argIndex = [1, 2]
        or
        m.getParameterType(0).hasName("long") and argIndex = [0, 1]
      )
      or
      m.hasName("copyMemory") and
      (
        m.getParameterType(0) instanceof TypeObject and argIndex = [1, 3, 4]
        or
        m.getParameterType(0).hasName("long") and argIndex = [0, 1, 2]
      )
      or
      m.hasName("freeMemory") and argIndex = 0
      or
      m.getName().matches("compareAndSwap%") and argIndex = 1
      or
      m.getName().matches(["get%Volatile", "put%Volatile"]) and argIndex = 1
      or
      m.getName().matches("putOrdered%") and argIndex = 1
      or
      m.getName().matches(["getAndAdd%", "getAndSet%"]) and argIndex = 1
    )
  }
}

Affected methods (memory address)

getX(Object, ...) / putX(Object, ...):

Method Arg index
getInt(Object, ...) 1
putInt(Object, ...) 1
getObject(Object, ...) 1
putObject(Object, ...) 1
getBoolean(Object, ...) 1
putBoolean(Object, ...) 1
getByte(Object, ...) 1
putByte(Object, ...) 1
getShort(Object, ...) 1
putShort(Object, ...) 1
getChar(Object, ...) 1
putChar(Object, ...) 1
getLong(Object, ...) 1
putLong(Object, ...) 1
getFloat(Object, ...) 1
putFloat(Object, ...) 1
getDouble(Object, ...) 1
putDouble(Object, ...) 1

getX(long) / putX(long, ...):

Method Arg index
getByte(long) 0
putByte(long, ...) 0
getShort(long) 0
putShort(long, ...) 0
getChar(long) 0
putChar(long, ...) 0
getInt(long) 0
putInt(long, ...) 0
getLong(long) 0
putLong(long, ...) 0
getFloat(long) 0
putFloat(long, ...) 0
getDouble(long) 0
putDouble(long, ...) 0

getXVolatile / putXVolatile:

Method Arg index
getObjectVolatile 1
putObjectVolatile 1
getIntVolatile 1
putIntVolatile 1
getBooleanVolatile 1
putBooleanVolatile 1
getByteVolatile 1
putByteVolatile 1
getShortVolatile 1
putShortVolatile 1
getCharVolatile 1
putCharVolatile 1
getLongVolatile 1
putLongVolatile 1
getFloatVolatile 1
putFloatVolatile 1
getDoubleVolatile 1
putDoubleVolatile 1
Method Arg index
getAddress 0
putAddress 0
reallocateMemory 0
setMemory(Object, ...) 1
setMemory(long, ...) 0
copyMemory(Object, ...) 1, 3
copyMemory(long, ...) 0, 1
freeMemory 0
compareAndSwapObject 1
compareAndSwapInt 1
compareAndSwapLong 1
putOrderedObject 1
putOrderedInt 1
putOrderedLong 1
getAndAddInt 1
getAndAddLong 1
getAndSetInt 1
getAndSetLong 1
getAndSetObject 1

Affected methods (memory segment length)

These sinks specify the length of a memory segment to copy or set. Similarly to the memory address sinks above they also allow crashing the JVM, but maybe they cannot be used that easily for other attacks (?).

Method Arg index
setMemory(Object, ...) 2
setMemory(long, ...) 1
copyMemory(Object, ...) 4
copyMemory(long, ...) 2

Affected methods (memory allocation size)

Maybe this goes more in the direction of #45821, but on the other hand the Unsafe documentation says some safety checks might be omitted, so maybe for invalid values these methods can also cause other incorrect behavior?

Method Arg index
allocateMemory 1
reallocateMemory 1

Footnotes

  1. I am not completely sure about the status of that PR and whether these sinks have already been covered by the models in the meantime. Sorry if my review comments on that PR back then were too disruptive and prevented that PR from getting merged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions