From 0314a487ff492ab2302604f4c566c864e2aeca89 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 30 Oct 2018 15:50:22 +0100 Subject: [PATCH] compiler: check slice cap instead of len during slicing When doing a slice operation on a slice, use the capacity value instead of the length. Of course, for strings and arrays, the slice operation checks the length because there is no capacity. But according to the spec, this check should be based on cap for slice instead of len: > For slices, the upper index bound is the slice capacity cap(a) rather > than the length. https://golang.org/ref/spec#Slice_expressions Fixes: https://github.com/aykevl/tinygo/issues/65 --- compiler/compiler.go | 8 ++++---- src/runtime/panic.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index f78407f4..f3fcd618 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -2237,7 +2237,7 @@ func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value, ind } } -func (c *Compiler) emitSliceBoundsCheck(frame *Frame, length, low, high llvm.Value) { +func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.Value) { if frame.fn.IsNoBounds() { // The //go:nobounds pragma was added to the function to avoid bounds // checking. @@ -2251,9 +2251,9 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, length, low, high llvm.Val if high.Type().IntTypeWidth() < 64 { high = c.builder.CreateSExt(high, c.ctx.Int64Type(), "") } - c.createRuntimeCall("sliceBoundsCheckLong", []llvm.Value{length, low, high}, "") + c.createRuntimeCall("sliceBoundsCheckLong", []llvm.Value{capacity, low, high}, "") } else { - c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{length, low, high}, "") + c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{capacity, low, high}, "") } } @@ -2684,7 +2684,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { high = oldLen } - c.emitSliceBoundsCheck(frame, oldLen, low, high) + c.emitSliceBoundsCheck(frame, oldCap, low, high) if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.lenType) { low = c.builder.CreateTrunc(low, c.lenType, "") diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 53ea81d1..cac51733 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -38,15 +38,15 @@ func lookupBoundsCheckLong(length lenType, index int64) { } // Check for bounds in *ssa.Slice. -func sliceBoundsCheck(length lenType, low, high uint) { - if !(0 <= low && low <= high && high <= uint(length)) { +func sliceBoundsCheck(capacity lenType, low, high uint) { + if !(0 <= low && low <= high && high <= uint(capacity)) { runtimePanic("slice out of range") } } // Check for bounds in *ssa.Slice. Supports 64-bit indexes. -func sliceBoundsCheckLong(length lenType, low, high uint64) { - if !(0 <= low && low <= high && high <= uint64(length)) { +func sliceBoundsCheckLong(capacity lenType, low, high uint64) { + if !(0 <= low && low <= high && high <= uint64(capacity)) { runtimePanic("slice out of range") } }