WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.6% of users enabled 2FA.

Commit 18eb5b94 authored by Kunshan Wang's avatar Kunshan Wang
Browse files

Work-around errno problem.

parent d5bf3607
......@@ -531,7 +531,7 @@ trait InstructionExecutor extends InterpreterActions with CommInstExecutor {
val argBoxes = argList.map(boxOf)
val shouldIncrementPC = curStack.callNative(sig, addr, argBoxes)
val shouldIncrementPC = curStack.callNative(sig, addr, argBoxes, errnoBox)
if (shouldIncrementPC) {
continueNormally()
}
......
......@@ -54,6 +54,17 @@ class InterpreterThread(val id: MuInternalID, initialStack: InterpreterStack, in
}
}
/**
* This box holds the per-mu-thread errno. It's a Hack.
* <p>
* Every InterpreterThread holds an ErrnoBox.
* Before every CCALL, set errno to savedLastError; after every CCALL, set savedLastError to errno. In this way,
* native programs see a "continuum" where the errno is never observed to change inside the micro VM.
*/
class ErrnoBox {
var errno: Int = 0
}
/**
* The states of an interpreter thread.
*/
......@@ -78,6 +89,9 @@ trait InterpreterThreadState {
/** Thread-local object reference. */
val threadLocal = BoxRef(0L)
/** The per-thread errno. */
val errnoBox = new ErrnoBox
protected def curStack = stack.get
protected def top: InterpreterFrame = curStack.top
......
......@@ -210,11 +210,11 @@ class InterpreterStack(val id: MuInternalID, val stackMemory: StackMemory, stack
}
/** Mu calling a native function. */
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox]): Boolean = {
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox], errnoBox: ErrnoBox): Boolean = {
assert(top.isInstanceOf[MuFrame])
ensureNSK()
pushNativeFrame(sig, func)
val result = maybeNSK.get.callNative(sig, func, args)
val result = maybeNSK.get.callNative(sig, func, args, errnoBox)
handleNativeCallResult(result)
}
......
......@@ -225,11 +225,20 @@ class NativeCallHelper {
* to Mu (to JVM).
*/
val currentNativeStackKeeper = new ThreadLocal[NativeStackKeeper]()
/**
* The saved errno.
* <p>
* Before every CCALL, set errno to savedLastError; after every CCALL, set savedLastError to errno. In this way,
* native programs see a "continuum" where the errno is never observed to change inside the micro VM.
*/
var savedLastError: Int = 0
/**
* Call a native function. Must be called by a NativeStackKeeper.Slave thread.
*/
def callNative(nsk: NativeStackKeeper, sig: FuncSig, func: Word, args: Seq[ValueBox]): Option[ValueBox] = {
def callNative(nsk: NativeStackKeeper, sig: FuncSig, func: Word, args: Seq[ValueBox],
errnoBox: ErrnoBox): Option[ValueBox] = {
assert(Thread.currentThread() == nsk.slaveThread)
val jFunc = jffiFuncPool((sig, func))
......@@ -244,53 +253,76 @@ class NativeCallHelper {
assert(currentNativeStackKeeper.get() == nsk)
val inv = Invoker.getInstance
logger.debug("Before CCALL, saved errno = %d".format(errnoBox.errno))
val maybeRvb = sig.retTys match {
case Seq() => {
NativeSupport.jffiLastError.set(errnoBox.errno)
inv.invokeLong(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
None
}
case Seq(t) => {
val b = t match {
case TypeInt(8) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeInt(jFunc, hib).toByte
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 8))
}
case TypeInt(16) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeInt(jFunc, hib).toShort
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 16))
}
case TypeInt(32) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeInt(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 32))
}
case TypeInt(64) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeLong(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 64))
}
case TypeFloat() => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeFloat(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxFloat(rv)
}
case TypeDouble() => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeDouble(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxDouble(rv)
}
case TypeStruct(flds) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeStruct(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
val buf = ByteBuffer.wrap(rv).order(ByteOrder.LITTLE_ENDIAN)
logger.debug("Hexdump:\n" + HexDump.dumpByteBuffer(buf))
val ptr = Pointer.wrap(NativeSupport.jnrRuntime, buf)
makeBoxFromPtr(ptr, 0, t)
}
case _: AbstractPointerType => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeAddress(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxPointer(rv)
}
}
Some(b)
}
}
logger.debug("After CCALL, saved errno = %d".format(errnoBox.errno))
currentNativeStackKeeper.remove()
maybeRvb
......
package uvm.refimpl.nat
import com.kenai.jffi.ClosureManager
import jnr.ffi.provider.jffi.NativeRuntime
import com.kenai.jffi.LastError
import com.kenai.jffi.MemoryIO
import jnr.ffi.Pointer
import jnr.ffi.provider.jffi.NativeRuntime
/**
* Holder of JNR-specific resources.
......@@ -17,6 +18,7 @@ object NativeSupport {
// This is from JFFI, not JNR-FFI.
val jffiClosureManager = ClosureManager.getInstance()
val jffiLastError = LastError.getInstance()
// Explicit memory management
......
......@@ -11,6 +11,7 @@ import uvm.{ Function => MFunc }
import uvm.refimpl.UvmRuntimeException
import uvm.refimpl.Word
import uvm.refimpl.itpr.ValueBox
import uvm.refimpl.itpr.ErrnoBox
object PoorManAgent {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
......@@ -70,7 +71,7 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
abstract class ToSlave
object ToSlave {
/** Mu wants to call a native function. */
case class CallNative(sig: FuncSig, func: Word, args: Seq[ValueBox]) extends ToSlave
case class CallNative(sig: FuncSig, func: Word, args: Seq[ValueBox], errnoBox: ErrnoBox) extends ToSlave
/** Mu wants to return to a native function. maybeRvb holds the return value. */
case class ReturnToNative(maybeRvb: Option[ValueBox]) extends ToSlave
/** Mu wants the slave to stop. */
......@@ -92,8 +93,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
return
}
received match {
case ToSlave.CallNative(sig, func, args) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args)
case ToSlave.CallNative(sig, func, args, errnoBox) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args, errnoBox)
master.send(NativeCallResult.ReturnToMu(maybeRvb))
receiving()
}
......@@ -129,8 +130,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
throw e
}
received match {
case ToSlave.CallNative(sig, func, args) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args)
case ToSlave.CallNative(sig, func, args, errnoBox) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args, errnoBox)
master.send(NativeCallResult.ReturnToMu(maybeRvb))
receiving()
}
......@@ -162,8 +163,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
val slaveThread = new Thread(slave)
slaveThread.start()
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox]): NativeCallResult = {
slave.send(ToSlave.CallNative(sig, func, args))
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox], errnoBox: ErrnoBox): NativeCallResult = {
slave.send(ToSlave.CallNative(sig, func, args, errnoBox))
val result = receive()
result
}
......
package uvm.refimpl.itpr
import org.scalatest._
import java.io.FileReader
import uvm._
import uvm.types._
import uvm.ssavariables._
import uvm.refimpl._
import uvm.refimpl.itpr._
import MemoryOrder._
import AtomicRMWOptr._
import uvm.refimpl.Word
import ch.qos.logback.classic.Level._
import uvm.refimpl.UvmBundleTesterBase
import com.kenai.jffi.Library
import jnr.posix.POSIXFactory
import uvm.refimpl.nat.NativeLibraryTestHelper
class UvmInterpreterNativeErrnoTest extends UvmBundleTesterBase {
setLogLevels(
ROOT_LOGGER_NAME -> INFO,
"uvm.refimpl.itpr" -> DEBUG,
"uvm.refimpl.nat" -> DEBUG
)
NativeLibraryTestHelper.loadTestLibraryToMicroVM(microVM, "simplecfuncs")
preloadBundles("tests/uvm-refimpl-test/primitives.uir", "tests/uvm-refimpl-test/native-errno-test.uir")
"The CCALL instruction" should "save errno to thread's errno box" in {
val ctx = microVM.newContext()
val func = ctx.handleFromFunc("@errno_test")
val a0 = ctx.handleFromInt(42, 32)
testFunc(ctx, func, Seq(a0)) { (ctx, th, st, wp) =>
val Seq(rv: MuIntValue) = ctx.dumpKeepalives(st, 0)
ctx.handleToSInt(rv) shouldEqual 42
returnFromTrap(st)
}
ctx.closeContext()
}
}
\ No newline at end of file
package uvm.refimpl.nat
import com.kenai.jffi.Library
import uvm.refimpl.MicroVM
object NativeLibraryTestHelper {
def loadTestLibrary(name: String): Library = {
def getLibPath(name: String): String = {
val relPath = s"tests/c-snippets/${name}.so"
if (!new java.io.File(relPath).isFile()) {
throw new RuntimeException(s"Need to compile the ${name}.so library. cd into tests/c-snippets and invoke 'make'.")
}
relPath
}
def loadTestLibrary(name: String): Library = {
val relPath = getLibPath(name)
Library.openLibrary(relPath, Library.NOW)
}
def loadTestLibraryToMicroVM(microVM: MicroVM, name: String): Unit = {
val relPath = getLibPath(name)
microVM.nativeLibraryHolder.loadLibrary(relPath)
}
}
\ No newline at end of file
......@@ -52,7 +52,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers {
val box1 = BoxInt(3)
val box2 = BoxInt(4)
val result = nsk.callNative(sig, addr, Seq(box1, box2))
val result = nsk.callNative(sig, addr, Seq(box1, box2), new ErrnoBox)
result shouldBeA[NativeCallResult.ReturnToMu] { its =>
its.maybeRvb shouldBe Some(BoxInt(7))
......@@ -84,7 +84,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers {
val b1 = BoxDouble(3.0)
val b2 = BoxPointer(mockClosAddr)
val r1 = nsk.callNative(sig, addr, Seq(b1, b2))
val r1 = nsk.callNative(sig, addr, Seq(b1, b2), new ErrnoBox)
println("Hello. I received r1")
......@@ -145,7 +145,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers {
val b2 = BoxPointer(closHandle.getAddress)
val br = BoxDouble(-1.0)
val result = nsk.callNative(sig, addr, Seq(b1, b2))
val result = nsk.callNative(sig, addr, Seq(b1, b2), new ErrnoBox)
closHandle.dispose()
......
#include<stdio.h>
#include<errno.h>
int hello_world(int n) {
printf("Hello world from C!\n");
return n + 1;
}
void set_errno(int n) {
printf("Hi! I am about to set errno to %d\n", n);
errno = n;
}
int get_errno() {
return errno;
}
// require "primitives.uir"
.funcsig @set_errno.sig = (@i32) -> ()
.funcsig @get_errno.sig = () -> (@i32)
.typedef @set_errno.fp = ufuncptr<@set_errno.sig>
.typedef @get_errno.fp = ufuncptr<@get_errno.sig>
.const @set_errno <@set_errno.fp> = EXTERN "set_errno"
.const @get_errno <@get_errno.fp> = EXTERN "get_errno"
.funcsig @errno_test.sig = (@i32) -> ()
.funcdef @errno_test VERSION %1 <@errno_test.sig> {
%entry(<@i32> %p0):
CCALL #DEFAULT <@set_errno.fp @set_errno.sig> @set_errno (%p0)
%rv = CCALL #DEFAULT <@get_errno.fp @get_errno.sig> @get_errno ()
[%trap] TRAP <> KEEPALIVE (%rv)
COMMINST @uvm.thread_exit
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment