
com.oracle.truffle.js.shell.JSFuzzilliRunner Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must 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.
*/
package com.oracle.truffle.js.shell;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.function.Consumer;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Source;
class JSFuzzilliRunner {
private static final int TIMEOUT_EXIT_CODE = 254;
private static final int OUT_OF_MEMORY_EXIT_CODE = 253;
private static final LogLevel logLevel = LogLevel.severe;
private enum LogLevel {
none,
severe,
info,
debug
}
public static int runFuzzilliREPRL(Context.Builder contextBuilder) {
try (RandomAccessFile controlReadF = new RandomAccessFile(new File("/dev/fd/100"), "rws");
RandomAccessFile controlWriteF = new RandomAccessFile(new File("/dev/fd/101"), "rws");
RandomAccessFile dataReadF = new RandomAccessFile(new File("/dev/fd/102"), "rws")) {
log(LogLevel.info, "GraalJS Fuzzilli REPRL started");
Consumer fuzzout = (String s) -> {
try {
try (RandomAccessFile dataWriteF = new RandomAccessFile(new File("/dev/fd/103"), "rws")) {
dataWriteF.write(s.getBytes(StandardCharsets.UTF_8));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
};
Runnable crash = () -> {
throw new ThreadDeath();
};
final byte[] helo = new byte[]{'H', 'E', 'L', 'O'};
final byte[] response = new byte[8];
final byte[] writeExitCode = new byte[4];
log(LogLevel.debug, "writing HELO...");
controlWriteF.write(helo);
if (!expectResponse(controlReadF, response, "HELO")) {
log(LogLevel.severe, "Invalid HELO response from parent: " + Arrays.toString(response));
return -1;
}
int status = -1;
String script = null;
Timer timer = new Timer(true);
for (;;) {
try {
if (!expectResponse(controlReadF, response, "exec")) {
log(LogLevel.severe, "Unknown action: " + Arrays.toString(response));
return -1;
}
log(LogLevel.debug, "trying to read scriptSize");
controlReadF.read(response);
int scriptSize = (int) ByteBuffer.wrap(response).order(ByteOrder.LITTLE_ENDIAN).getLong();
log(LogLevel.debug, "got scriptSize: " + scriptSize);
log(LogLevel.debug, "Reading script of length " + scriptSize);
byte[] scriptChars = new byte[scriptSize];
int n = dataReadF.read(scriptChars);
if (n != scriptSize) {
log(LogLevel.severe, String.format("ERROR: read %d bytes, but scriptSize was %d", n, scriptSize));
} else {
log(LogLevel.debug, String.format("got %d bytes", n));
}
script = new String(scriptChars, StandardCharsets.UTF_8);
log(LogLevel.debug, "Got script:");
log(LogLevel.debug, script);
try (Context context = contextBuilder.build()) {
context.getBindings("js").putMember("__fuzzout__", fuzzout);
context.getBindings("js").putMember("crash", crash);
TimerTask canceller = new TimerTask() {
@Override
public void run() {
context.close(true);
}
};
timer.schedule(canceller, 1000);
context.eval(Source.newBuilder("js", script, "fuzzilliInput").build());
canceller.cancel();
}
status = 0;
} catch (PolyglotException e) {
if (e.isExit()) {
// normal exit() calls are fine and not to be treated as errors
status = 0;
} else if (e.isCancelled()) {
log(LogLevel.info, "TIMEOUT");
// special exit code for timeout
status = TIMEOUT_EXIT_CODE;
} else if (e.isSyntaxError()) {
status = 7;
} else if (!e.isInternalError()) {
status = 7;
} else {
if (e.getMessage().startsWith("java.lang.OutOfMemoryError")) {
// special exit code for OutOfMemoryErrors
sendExitCode(controlWriteF, writeExitCode, OUT_OF_MEMORY_EXIT_CODE);
return 1;
}
log(LogLevel.severe, "INTERNAL ERROR POLYGLOT EXCEPTION");
printStackTrace(e);
// e is an internal error, treat as crash in the fuzzer
status = -1;
}
} catch (OutOfMemoryError e) {
// special exit code for OutOfMemoryErrors
sendExitCode(controlWriteF, writeExitCode, OUT_OF_MEMORY_EXIT_CODE);
return 1;
} catch (Throwable t) {
log(LogLevel.severe, "NON-POLYGLOT EXCEPTION");
printStackTrace(t);
// all non-polyglot exceptions should be treated as crashes by the fuzzer
status = -1;
}
log(LogLevel.debug, "Sending exit code " + status);
sendExitCode(controlWriteF, writeExitCode, status);
}
} catch (Throwable t) {
log(LogLevel.severe, "OUTER THROWABLE");
printStackTrace(t);
return -1;
}
}
private static void sendExitCode(final RandomAccessFile controlWriteF, final byte[] writeExitCode, int statusCode) throws IOException {
int status = statusCode << 8;
writeExitCode[0] = (byte) (status & 0xff);
writeExitCode[1] = (byte) ((status >>> 8) & 0xff);
writeExitCode[2] = (byte) ((status >>> 16) & 0xff);
writeExitCode[3] = (byte) ((status >>> 24) & 0xff);
controlWriteF.write(writeExitCode);
}
private static boolean expectResponse(RandomAccessFile source, final byte[] buf, String expected) throws IOException {
log(LogLevel.debug, "trying to read " + expected + "...");
int bytesRead = source.read(buf, 0, expected.length());
if (bytesRead != expected.length()) {
log(LogLevel.severe, "fuzzilliExpectResponse: no input available, got " + bytesRead + " bytes");
return false;
}
log(LogLevel.debug, "fuzzilliExpectResponse: got response: " + new String(buf, 0, expected.length(), StandardCharsets.UTF_8));
for (int i = 0; i < expected.length(); i++) {
if (buf[i] != expected.charAt(i)) {
return false;
}
}
return true;
}
private static void printStackTrace(Throwable t) {
t.printStackTrace(System.out);
System.out.flush();
}
private static void log(LogLevel level, String msg) {
if (logLevel.ordinal() >= level.ordinal()) {
System.out.println(msg);
System.out.flush();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy