From 7459bfa8a37a4fbd6ed5153bff76f49d372b4ace Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 30 Jul 2021 17:19:42 -0700 Subject: [PATCH] compiler, runtime: allow slice to array pointer conversion Panic if the slice is too short. For golang/go#395 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/338630 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 53 +++++++++++++++- gcc/go/gofrontend/runtime.def | 5 ++ gcc/go/gofrontend/types.cc | 7 +++ gcc/testsuite/go.test/test/convert4.go | 86 ++++++++++++++++++++++++++ libgo/go/runtime/error.go | 2 + libgo/go/runtime/panic.go | 7 +++ 7 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/go.test/test/convert4.go diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 19fbd64784077..95b9340b42db4 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -ad667e7c70cea9fa5730660d72ad891b5753eb62 +0a4d612e6b211780b294717503fc739bbd1f509c The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 46e71e5a13b73..15c9eabc6bf69 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3866,11 +3866,12 @@ Type_conversion_expression::do_traverse(Traverse* traverse) return TRAVERSE_CONTINUE; } -// Convert to a constant at lowering time. +// Convert to a constant at lowering time. Also lower conversions +// from slice to pointer-to-array, as they can panic. Expression* Type_conversion_expression::do_lower(Gogo*, Named_object*, - Statement_inserter*, int) + Statement_inserter* inserter, int) { Type* type = this->type_; Expression* val = this->expr_; @@ -3958,6 +3959,54 @@ Type_conversion_expression::do_lower(Gogo*, Named_object*, } } + if (type->points_to() != NULL + && type->points_to()->array_type() != NULL + && !type->points_to()->is_slice_type() + && val->type()->is_slice_type()) + { + Temporary_statement* val_temp = NULL; + if (!val->is_multi_eval_safe()) + { + val_temp = Statement::make_temporary(val->type(), NULL, location); + inserter->insert(val_temp); + val = Expression::make_set_and_use_temporary(val_temp, val, + location); + } + + Type* int_type = Type::lookup_integer_type("int"); + Temporary_statement* vallen_temp = + Statement::make_temporary(int_type, NULL, location); + inserter->insert(vallen_temp); + + Expression* arrlen = type->points_to()->array_type()->length(); + Expression* vallen = + Expression::make_slice_info(val, Expression::SLICE_INFO_LENGTH, + location); + vallen = Expression::make_set_and_use_temporary(vallen_temp, vallen, + location); + Expression* cond = Expression::make_binary(OPERATOR_GT, arrlen, vallen, + location); + + vallen = Expression::make_temporary_reference(vallen_temp, location); + Expression* panic = Runtime::make_call(Runtime::PANIC_SLICE_CONVERT, + location, 2, arrlen, vallen); + + Expression* nil = Expression::make_nil(location); + Expression* check = Expression::make_conditional(cond, panic, nil, + location); + + if (val_temp == NULL) + val = val->copy(); + else + val = Expression::make_temporary_reference(val_temp, location); + Expression* ptr = + Expression::make_slice_info(val, Expression::SLICE_INFO_VALUE_POINTER, + location); + ptr = Expression::make_unsafe_cast(type, ptr, location); + + return Expression::make_compound(check, ptr, location); + } + return this; } diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 231d92d2661ad..fad8cebc012d6 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -582,6 +582,11 @@ DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C, "runtime.goPanicExtendSlice3C", DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C_U, "runtime.goPanicExtendSlice3CU", P2(UINT64, INT), R0()) +// Panic for conversion of slice to pointer-to-array if the slice is +// too short. +DEF_GO_RUNTIME(PANIC_SLICE_CONVERT, "runtime.goPanicSliceConvert", + P2(INT, INT), R0()) + // Remove helper macros. #undef ABFT6 #undef ABFT2 diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index ab7166b8b1ba4..7c7b2eb82715d 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -842,6 +842,13 @@ Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason) return true; } + // A slice may be converted to a pointer-to-array. + if (rhs->is_slice_type() + && lhs->points_to() != NULL + && lhs->points_to()->array_type() != NULL + && !lhs->points_to()->is_slice_type()) + return true; + // An unsafe.Pointer type may be converted to any pointer type or to // a type whose underlying type is uintptr, and vice-versa. if (lhs->is_unsafe_pointer_type() diff --git a/gcc/testsuite/go.test/test/convert4.go b/gcc/testsuite/go.test/test/convert4.go new file mode 100644 index 0000000000000..2bc9c96a527fa --- /dev/null +++ b/gcc/testsuite/go.test/test/convert4.go @@ -0,0 +1,86 @@ +// run + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test conversion from slice to array pointer. + +package main + +func wantPanic(fn func(), s string) { + defer func() { + err := recover() + if err == nil { + panic("expected panic") + } + if got := err.(error).Error(); got != s { + panic("expected panic " + s + " got " + got) + } + }() + fn() +} + +func main() { + s := make([]byte, 8, 10) + if p := (*[8]byte)(s); &p[0] != &s[0] { + panic("*[8]byte conversion failed") + } + wantPanic( + func() { + _ = (*[9]byte)(s) + }, + "runtime error: cannot convert slice with length 8 to pointer to array with length 9", + ) + + var n []byte + if p := (*[0]byte)(n); p != nil { + panic("nil slice converted to *[0]byte should be nil") + } + + z := make([]byte, 0) + if p := (*[0]byte)(z); p == nil { + panic("empty slice converted to *[0]byte should be non-nil") + } + + // Test with named types + type Slice []int + type Int4 [4]int + type PInt4 *[4]int + ii := make(Slice, 4) + if p := (*Int4)(ii); &p[0] != &ii[0] { + panic("*Int4 conversion failed") + } + if p := PInt4(ii); &p[0] != &ii[0] { + panic("PInt4 conversion failed") + } +} + +// test static variable conversion + +var ( + ss = make([]string, 10) + s5 = (*[5]string)(ss) + s10 = (*[10]string)(ss) + + ns []string + ns0 = (*[0]string)(ns) + + zs = make([]string, 0) + zs0 = (*[0]string)(zs) +) + +func init() { + if &ss[0] != &s5[0] { + panic("s5 conversion failed") + } + if &ss[0] != &s10[0] { + panic("s5 conversion failed") + } + if ns0 != nil { + panic("ns0 should be nil") + } + if zs0 == nil { + panic("zs0 should not be nil") + } +} diff --git a/libgo/go/runtime/error.go b/libgo/go/runtime/error.go index 80655342f13c0..a8c82bbf8e8a7 100644 --- a/libgo/go/runtime/error.go +++ b/libgo/go/runtime/error.go @@ -175,6 +175,7 @@ const ( boundsSlice3B // s[?:x:y], 0 <= x <= y failed (but boundsSlice3A didn't happen) boundsSlice3C // s[x:y:?], 0 <= x <= y failed (but boundsSlice3A/B didn't happen) + boundsConvert // (*[x]T)(s), 0 <= x <= len(s) failed // Note: in the above, len(s) and cap(s) are stored in y ) @@ -190,6 +191,7 @@ var boundsErrorFmts = [...]string{ boundsSlice3Acap: "slice bounds out of range [::%x] with capacity %y", boundsSlice3B: "slice bounds out of range [:%x:%y]", boundsSlice3C: "slice bounds out of range [%x:%y:]", + boundsConvert: "cannot convert slice with length %y to pointer to array with length %x", } // boundsNegErrorFmts are overriding formats if x is negative. In this case there's no need to report y. diff --git a/libgo/go/runtime/panic.go b/libgo/go/runtime/panic.go index 11396b4123a0a..a4b9a83d7764c 100644 --- a/libgo/go/runtime/panic.go +++ b/libgo/go/runtime/panic.go @@ -38,6 +38,7 @@ import ( //go:linkname goPanicSlice3BU //go:linkname goPanicSlice3C //go:linkname goPanicSlice3CU +//go:linkname goPanicSliceConvert //go:linkname panicshift //go:linkname panicdivide //go:linkname panicmem @@ -175,6 +176,12 @@ func goPanicSlice3CU(x uint, y int) { panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C}) } +// failures in the conversion (*[x]T)s, 0 <= x <= y, x == cap(s) +func goPanicSliceConvert(x int, y int) { + panicCheck1(getcallerpc(), "slice length too short to convert to pointer to array") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsConvert}) +} + var shiftError = error(errorString("negative shift amount")) func panicshift() {