Skip to content

Java: Add flow steps through methods of java.nio.Buffer and its subclasses #4743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions java/ql/src/semmle/code/java/dataflow/FlowSteps.qll
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,74 @@ private class StringBuilderTaintPreservingCallable extends TaintPreservingCallab
sink = -1
}
}

/**
* Flow steps through methods of classes that extend `java.nio.Buffer`, such as `ByteBuffer` and `CharBuffer`.
*/
private module Buffers {
class BufferType extends RefType {
BufferType() { this.getASourceSupertype*().hasQualifiedName("java.nio", "Buffer") }
}

class BufferWrapMethod extends TaintPreservingCallable {
BufferWrapMethod() {
this.getDeclaringType() instanceof BufferType and
// public static ByteBuffer wrap(byte[] array)
// public static ByteBuffer wrap(byte[] array, int offset, int length)
this.hasName("wrap") and
this.isStatic()
}

override predicate returnsTaintFrom(int arg) { arg = 0 }
}

class BufferArrayMethod extends TaintPreservingCallable {
BufferArrayMethod() {
this.getDeclaringType() instanceof BufferType and
// public abstract Object array() [in Buffer]
this.hasName("array")
}

override predicate returnsTaintFrom(int arg) { arg = -1 }
}

class BufferChainMethod extends TaintPreservingCallable {
BufferChainMethod() {
this.getDeclaringType() instanceof BufferType and
this.getReturnType() instanceof BufferType and
not this.hasName("clear")
}

override predicate returnsTaintFrom(int arg) { arg = -1 }
}

class BufferBulkGetMethod extends BufferChainMethod {
int idx;

BufferBulkGetMethod() {
// public ByteBuffer get(byte[] dst)
// public ByteBuffer get(byte[] dst, int offset, int length)
// public ByteBuffer get(int index, byte[] dst, int offset, int length)
this.hasName("get") and
this.getParameterType(idx) instanceof Array
}

override predicate transfersTaint(int src, int sink) { src = -1 and sink = idx }
}

class BufferBulkPutMethod extends BufferChainMethod {
int idx;

BufferBulkPutMethod() {
// public final ByteBuffer put(byte[] src)
// public ByteBuffer put(byte[] src, int offset, int length)
// public ByteBuffer put(int index, byte[] src, int offset, int length)
// public ByteBuffer put(ByteBuffer src)
// public final CharBuffer put(String src)
this.hasName("put") and
this.getParameterType(idx) instanceof RefType
}

override predicate transfersTaint(int src, int sink) { src = idx and sink = -1 }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,6 @@ private predicate taintPreservingQualifierToMethod(Method m) {
m.getDeclaringType().hasQualifiedName("javax.xml.transform.stream", "StreamSource") and
m.hasName("getInputStream")
or
m.getDeclaringType().hasQualifiedName("java.nio", "ByteBuffer") and
m.hasName("get")
or
m.getDeclaringType() instanceof TypeFile and
m.hasName("toPath")
or
Expand Down
38 changes: 38 additions & 0 deletions java/ql/test/library-tests/dataflow/taint-buffers/A.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import java.nio.ByteBuffer;
import java.nio.CharBuffer;

class A {
Object taint() { return null; }

void sink(Object x) {}

void test1() {
byte[] data = (byte[]) taint();

ByteBuffer buf = ByteBuffer.allocate(1024);

buf.put(data);
buf.rewind();

byte[] data2 = new byte[64];
buf.get(data2);
sink(data2);
}

void test2() {
byte[] data = (byte[]) taint();

ByteBuffer buf = ByteBuffer.wrap(data);

sink(buf.duplicate().flip().compact().asCharBuffer().array());
}

void test3() {
String text = (String) taint();

CharBuffer buf = CharBuffer.allocate(1024);
buf.put(text);

sink(buf);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
| A.java:10:32:10:38 | taint(...) | A.java:19:14:19:18 | data2 |
| A.java:23:32:23:38 | taint(...) | A.java:27:14:27:68 | array(...) |
| A.java:31:32:31:38 | taint(...) | A.java:36:14:36:16 | buf |
21 changes: 21 additions & 0 deletions java/ql/test/library-tests/dataflow/taint-buffers/test.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import java
import semmle.code.java.dataflow.TaintTracking

class Conf extends TaintTracking::Configuration {
Conf() { this = "qltest:dataflow:buffers" }

override predicate isSource(DataFlow::Node n) {
n.asExpr().(MethodAccess).getMethod().hasName("taint")
}

override predicate isSink(DataFlow::Node n) {
exists(MethodAccess ma |
ma.getMethod().hasName("sink") and
ma.getAnArgument() = n.asExpr()
)
}
}

from DataFlow::Node src, DataFlow::Node sink, Conf conf
where conf.hasFlow(src, sink)
select src, sink