@@ -4,6 +4,8 @@ package compiler
44// required by the Go programming language.
55
66import (
7+ "fmt"
8+ "go/token"
79 "go/types"
810
911 "tinygo.org/x/go-llvm"
@@ -122,6 +124,77 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high, max l
122124 c .builder .SetInsertPointAtEnd (nextBlock )
123125}
124126
127+ // emitChanBoundsCheck emits a bounds check before creating a new channel to
128+ // check that the value is not too big for runtime.chanMake.
129+ func (c * Compiler ) emitChanBoundsCheck (frame * Frame , elementSize uint64 , bufSize llvm.Value , bufSizeType * types.Basic , pos token.Pos ) {
130+ if frame .fn .IsNoBounds () {
131+ // The //go:nobounds pragma was added to the function to avoid bounds
132+ // checking.
133+ return
134+ }
135+
136+ // Check whether the bufSize parameter must be cast to a wider integer for
137+ // comparison.
138+ if bufSize .Type ().IntTypeWidth () < c .uintptrType .IntTypeWidth () {
139+ if bufSizeType .Info ()& types .IsUnsigned != 0 {
140+ // Unsigned, so zero-extend to uint type.
141+ bufSizeType = types .Typ [types .Uint ]
142+ bufSize = c .builder .CreateZExt (bufSize , c .intType , "" )
143+ } else {
144+ // Signed, so sign-extend to int type.
145+ bufSizeType = types .Typ [types .Int ]
146+ bufSize = c .builder .CreateSExt (bufSize , c .intType , "" )
147+ }
148+ }
149+
150+ // Calculate (^uintptr(0)) >> 1, which is the max value that fits in an
151+ // uintptr if uintptrs were signed.
152+ maxBufSize := llvm .ConstLShr (llvm .ConstNot (llvm .ConstInt (c .uintptrType , 0 , false )), llvm .ConstInt (c .uintptrType , 1 , false ))
153+ if elementSize > maxBufSize .ZExtValue () {
154+ c .addError (pos , fmt .Sprintf ("channel element type is too big (%v bytes)" , elementSize ))
155+ return
156+ }
157+ // Avoid divide-by-zero.
158+ if elementSize == 0 {
159+ elementSize = 1
160+ }
161+ // Make the maxBufSize actually the maximum allowed value (in number of
162+ // elements in the channel buffer).
163+ maxBufSize = llvm .ConstUDiv (maxBufSize , llvm .ConstInt (c .uintptrType , elementSize , false ))
164+
165+ // Make sure maxBufSize has the same type as bufSize.
166+ if maxBufSize .Type () != bufSize .Type () {
167+ maxBufSize = llvm .ConstZExt (maxBufSize , bufSize .Type ())
168+ }
169+
170+ bufSizeTooBig := c .builder .CreateICmp (llvm .IntUGE , bufSize , maxBufSize , "" )
171+ // Check whether we can resolve this check at compile time.
172+ if ! bufSizeTooBig .IsAConstantInt ().IsNil () {
173+ val := bufSizeTooBig .ZExtValue ()
174+ if val == 0 {
175+ // Everything is constant so the check does not have to be emitted
176+ // in IR. This avoids emitting some redundant IR in the vast
177+ // majority of cases.
178+ return
179+ }
180+ }
181+
182+ faultBlock := c .ctx .AddBasicBlock (frame .fn .LLVMFn , "chan.outofbounds" )
183+ nextBlock := c .ctx .AddBasicBlock (frame .fn .LLVMFn , "chan.next" )
184+ frame .blockExits [frame .currentBlock ] = nextBlock // adjust outgoing block for phi nodes
185+
186+ // Now branch to the out-of-bounds or the regular block.
187+ c .builder .CreateCondBr (bufSizeTooBig , faultBlock , nextBlock )
188+
189+ // Fail: this channel is created with an invalid size parameter.
190+ c .builder .SetInsertPointAtEnd (faultBlock )
191+ c .createRuntimeCall ("chanMakePanic" , nil , "" )
192+ c .builder .CreateUnreachable ()
193+
194+ // Ok: this channel value is not too big.
195+ c .builder .SetInsertPointAtEnd (nextBlock )
196+ }
197+
125198// emitNilCheck checks whether the given pointer is nil, and panics if it is. It
126199// has no effect in well-behaved programs, but makes sure no uncaught nil
127200// pointer dereferences exist in valid Go code.
0 commit comments