Update spec and two related changes (detailed)

* Added LEB128 validation (ref: https://github.com/WebAssembly/spec/pull/750)
* Rename memory instructions (ref: https://github.com/WebAssembly/spec/pull/720)
This commit is contained in:
Chad Retz 2018-05-07 15:39:31 -05:00
parent 368ab300fa
commit f24342959d
6 changed files with 48 additions and 29 deletions

View File

@ -223,8 +223,8 @@ sealed class Node {
data class I64Store8(override val align: Int, override val offset: Long) : Instr(), Args.AlignOffset
data class I64Store16(override val align: Int, override val offset: Long) : Instr(), Args.AlignOffset
data class I64Store32(override val align: Int, override val offset: Long) : Instr(), Args.AlignOffset
data class CurrentMemory(override val reserved: Boolean) : Instr(), Args.Reserved
data class GrowMemory(override val reserved: Boolean) : Instr(), Args.Reserved
data class MemorySize(override val reserved: Boolean) : Instr(), Args.Reserved
data class MemoryGrow(override val reserved: Boolean) : Instr(), Args.Reserved
// Constants
data class I32Const(override val value: Int) : Instr(), Args.Const<Int>
@ -511,8 +511,8 @@ sealed class Node {
opMapEntry("i64.store8", 0x3c, ::MemOpAlignOffsetArg, Instr::I64Store8, Instr.I64Store8::class)
opMapEntry("i64.store16", 0x3d, ::MemOpAlignOffsetArg, Instr::I64Store16, Instr.I64Store16::class)
opMapEntry("i64.store32", 0x3e, ::MemOpAlignOffsetArg, Instr::I64Store32, Instr.I64Store32::class)
opMapEntry("current_memory", 0x3f, ::MemOpReservedArg, Instr::CurrentMemory, Instr.CurrentMemory::class)
opMapEntry("grow_memory", 0x40, ::MemOpReservedArg, Instr::GrowMemory, Instr.GrowMemory::class)
opMapEntry("memory.size", 0x3f, ::MemOpReservedArg, Instr::MemorySize, Instr.MemorySize::class)
opMapEntry("memory.grow", 0x40, ::MemOpReservedArg, Instr::MemoryGrow, Instr.MemoryGrow::class)
opMapEntry("i32.const", 0x41, ::ConstOpIntArg, Instr::I32Const, Instr.I32Const::class)
opMapEntry("i64.const", 0x42, ::ConstOpLongArg, Instr::I64Const, Instr.I64Const::class)

View File

@ -148,10 +148,10 @@ open class FuncBuilder {
is Node.Instr.I32Store8, is Node.Instr.I32Store16, is Node.Instr.I64Store8, is Node.Instr.I64Store16,
is Node.Instr.I64Store32 ->
applyStoreOp(ctx, fn, i as Node.Instr.Args.AlignOffset, index)
is Node.Instr.CurrentMemory ->
applyCurrentMemory(ctx, fn)
is Node.Instr.GrowMemory ->
applyGrowMemory(ctx, fn)
is Node.Instr.MemorySize ->
applyMemorySize(ctx, fn)
is Node.Instr.MemoryGrow ->
applyMemoryGrow(ctx, fn)
is Node.Instr.I32Const ->
fn.addInsns(i.value.const).push(Int::class.ref)
is Node.Instr.I64Const ->
@ -1062,14 +1062,14 @@ open class FuncBuilder {
).push(Int::class.ref)
}
fun applyGrowMemory(ctx: FuncContext, fn: Func) =
fun applyMemoryGrow(ctx: FuncContext, fn: Func) =
// Grow mem is a special case where the memory ref is already pre-injected on
// the stack before this call. Result is an int.
ctx.cls.assertHasMemory().let {
ctx.cls.mem.growMemory(ctx, fn)
}
fun applyCurrentMemory(ctx: FuncContext, fn: Func) =
fun applyMemorySize(ctx: FuncContext, fn: Func) =
// Curr mem is not specially injected, so we have to put the memory on the
// stack since we need it
ctx.cls.assertHasMemory().let {

View File

@ -194,7 +194,7 @@ open class InsnReworker {
is Node.Instr.I64Store32 ->
injectBeforeLastStackCount(Insn.MemNeededOnStack, 2)
// Grow memory requires "mem" before the single param
is Node.Instr.GrowMemory ->
is Node.Instr.MemoryGrow ->
injectBeforeLastStackCount(Insn.MemNeededOnStack, 1)
else -> { }
}
@ -239,8 +239,8 @@ open class InsnReworker {
is Node.Instr.I32Store, is Node.Instr.I64Store, is Node.Instr.F32Store, is Node.Instr.F64Store,
is Node.Instr.I32Store8, is Node.Instr.I32Store16, is Node.Instr.I64Store8, is Node.Instr.I64Store16,
is Node.Instr.I64Store32 -> POP_PARAM
is Node.Instr.CurrentMemory -> PUSH_RESULT
is Node.Instr.GrowMemory -> POP_PARAM + PUSH_RESULT
is Node.Instr.MemorySize -> PUSH_RESULT
is Node.Instr.MemoryGrow -> POP_PARAM + PUSH_RESULT
is Node.Instr.I32Const, is Node.Instr.I64Const,
is Node.Instr.F32Const, is Node.Instr.F64Const -> PUSH_RESULT
is Node.Instr.I32Add, is Node.Instr.I32Sub, is Node.Instr.I32Mul, is Node.Instr.I32DivS,
@ -288,12 +288,12 @@ open class InsnReworker {
val inc =
if (lastCouldHaveMem) 0
else if (insn == Insn.MemNeededOnStack) 1
else if (insn is Insn.Node && insn.insn is Node.Instr.CurrentMemory) 1
else if (insn is Insn.Node && insn.insn is Node.Instr.MemorySize) 1
else 0
val couldSetMemNext = if (insn !is Insn.Node) false else when (insn.insn) {
is Node.Instr.I32Store, is Node.Instr.I64Store, is Node.Instr.F32Store, is Node.Instr.F64Store,
is Node.Instr.I32Store8, is Node.Instr.I32Store16, is Node.Instr.I64Store8, is Node.Instr.I64Store16,
is Node.Instr.I64Store32, is Node.Instr.GrowMemory -> true
is Node.Instr.I64Store32, is Node.Instr.MemoryGrow -> true
else -> false
}
(count + inc) to couldSetMemNext

View File

@ -1,12 +1,12 @@
package asmble.io
import asmble.util.toIntExact
import asmble.util.toUnsignedBigInt
import asmble.util.toUnsignedLong
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.math.BigInteger
abstract class ByteReader {
abstract val isEof: Boolean
@ -34,27 +34,30 @@ abstract class ByteReader {
}
fun readVarInt7() = readSignedLeb128().let {
require(it >= Byte.MIN_VALUE.toLong() && it <= Byte.MAX_VALUE.toLong())
if (it < Byte.MIN_VALUE.toLong() || it > Byte.MAX_VALUE.toLong()) throw IoErr.InvalidLeb128Number()
it.toByte()
}
fun readVarInt32() = readSignedLeb128().toIntExact()
fun readVarInt32() = readSignedLeb128().let {
if (it < Int.MIN_VALUE.toLong() || it > Int.MAX_VALUE.toLong()) throw IoErr.InvalidLeb128Number()
it.toInt()
}
fun readVarInt64() = readSignedLeb128()
fun readVarInt64() = readSignedLeb128(9)
fun readVarUInt1() = readUnsignedLeb128().let {
require(it == 1 || it == 0)
if (it != 1 && it != 0) throw IoErr.InvalidLeb128Number()
it == 1
}
fun readVarUInt7() = readUnsignedLeb128().let {
require(it <= 255)
if (it > 255) throw IoErr.InvalidLeb128Number()
it.toShort()
}
fun readVarUInt32() = readUnsignedLeb128().toUnsignedLong()
protected fun readUnsignedLeb128(): Int {
protected fun readUnsignedLeb128(maxCount: Int = 4): Int {
// Taken from Android source, Apache licensed
var result = 0
var cur: Int
@ -63,12 +66,12 @@ abstract class ByteReader {
cur = readByte().toInt() and 0xff
result = result or ((cur and 0x7f) shl (count * 7))
count++
} while (cur and 0x80 == 0x80 && count < 5)
if (cur and 0x80 == 0x80) throw NumberFormatException()
} while (cur and 0x80 == 0x80 && count <= maxCount)
if (cur and 0x80 == 0x80) throw IoErr.InvalidLeb128Number()
return result
}
private fun readSignedLeb128(): Long {
private fun readSignedLeb128(maxCount: Int = 4): Long {
// Taken from Android source, Apache licensed
var result = 0L
var cur: Int
@ -79,8 +82,20 @@ abstract class ByteReader {
result = result or ((cur and 0x7f).toLong() shl (count * 7))
signBits = signBits shl 7
count++
} while (cur and 0x80 == 0x80 && count < 10)
if (cur and 0x80 == 0x80) throw NumberFormatException()
} while (cur and 0x80 == 0x80 && count <= maxCount)
if (cur and 0x80 == 0x80) throw IoErr.InvalidLeb128Number()
// Check for 64 bit invalid, taken from Apache/MIT licensed:
// https://github.com/paritytech/parity-wasm/blob/2650fc14c458c6a252c9dc43dd8e0b14b6d264ff/src/elements/primitives.rs#L351
// TODO: probably need 32 bit checks too, but meh, not in the suite
if (count > maxCount && maxCount == 9) {
if (cur and 0b0100_0000 == 0b0100_0000) {
if ((cur or 0b1000_0000).toByte() != (-1).toByte()) throw IoErr.InvalidLeb128Number()
} else if (cur != 0) {
throw IoErr.InvalidLeb128Number()
}
}
if ((signBits shr 1) and result != 0L) result = result or signBits
return result
}

View File

@ -1,7 +1,6 @@
package asmble.io
import asmble.AsmErr
import java.math.BigInteger
sealed class IoErr(message: String, cause: Throwable? = null) : RuntimeException(message, cause), AsmErr {
class UnexpectedEnd : IoErr("Unexpected EOF") {
@ -119,4 +118,9 @@ sealed class IoErr(message: String, cause: Throwable? = null) : RuntimeException
class InvalidUtf8Encoding : IoErr("Some byte sequence was not UTF-8 compatible") {
override val asmErrString get() = "invalid UTF-8 encoding"
}
class InvalidLeb128Number : IoErr("Invalid LEB128 number") {
override val asmErrString get() = "integer representation too long"
override val asmErrStrings get() = listOf(asmErrString, "integer too large")
}
}

@ -1 +1 @@
Subproject commit 98b90e2ab22053559ded143768d6559d2ba8d2fd
Subproject commit 1f00d57d009ec4098b60fb6ca138e8e2787accef