Skip to content

Commit a0c19b8

Browse files
committed
unify and imporve extract errors
1 parent ac68b9d commit a0c19b8

File tree

4 files changed

+35
-34
lines changed

4 files changed

+35
-34
lines changed

data/transactions/logic/box.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,10 @@ func opBoxExtract(cx *EvalContext) error {
7474
return err
7575
}
7676

77-
end := start + length
78-
if start > uint64(len(box)) || end > uint64(len(box)) {
79-
return errors.New("extract range beyond box")
80-
}
81-
82-
cx.stack[pprev].Bytes = []byte(box[start:end])
77+
bytes, err := extractCarefully([]byte(box), start, length)
78+
cx.stack[pprev].Bytes = bytes
8379
cx.stack = cx.stack[:prev]
84-
return nil
80+
return err
8581
}
8682

8783
func opBoxReplace(cx *EvalContext) error {

data/transactions/logic/box_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func TestBoxReadWrite(t *testing.T) {
6969
int 1`, ep)
7070

7171
logic.TestApp(t, `byte "self"; int 1; int 4; box_extract;
72-
byte 0x00000000; ==`, ep, "extract range")
72+
byte 0x00000000; ==`, ep, "extraction end 5")
7373

7474
// Replace some bytes until past the end, confirm when it fails.
7575
logic.TestApp(t, `byte "self"; int 1; byte 0x3031; box_replace;

data/transactions/logic/eval.go

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3494,39 +3494,43 @@ func opSetByte(cx *EvalContext) error {
34943494
return nil
34953495
}
34963496

3497-
func opExtractImpl(x []byte, start, length int) ([]byte, error) {
3497+
func extractCarefully(x []byte, start, length uint64) ([]byte, error) {
3498+
if start > uint64(len(x)) {
3499+
return nil, fmt.Errorf("extraction start %d beyond length: %d", start, len(x))
3500+
}
34983501
end := start + length
3499-
if start > len(x) || end > len(x) {
3500-
return nil, errors.New("extract range beyond length of string")
3502+
if end < start {
3503+
return nil, fmt.Errorf("extraction end exceeds uint64")
3504+
}
3505+
if end > uint64(len(x)) {
3506+
return nil, fmt.Errorf("extraction end %d beyond length: %d", end, len(x))
35013507
}
35023508
return x[start:end], nil
35033509
}
35043510

35053511
func opExtract(cx *EvalContext) error {
35063512
last := len(cx.stack) - 1
3507-
startIdx := cx.program[cx.pc+1]
3508-
lengthIdx := cx.program[cx.pc+2]
3513+
start := uint64(cx.program[cx.pc+1])
3514+
length := uint64(cx.program[cx.pc+2])
35093515
// Shortcut: if length is 0, take bytes from start index to the end
3510-
length := int(lengthIdx)
35113516
if length == 0 {
3512-
length = len(cx.stack[last].Bytes) - int(startIdx)
3517+
// If length has wrapped, it's because start > len(), so extractCarefully will report
3518+
length = uint64(len(cx.stack[last].Bytes) - int(start))
35133519
}
3514-
bytes, err := opExtractImpl(cx.stack[last].Bytes, int(startIdx), length)
3520+
bytes, err := extractCarefully(cx.stack[last].Bytes, start, length)
35153521
cx.stack[last].Bytes = bytes
35163522
return err
35173523
}
35183524

35193525
func opExtract3(cx *EvalContext) error {
35203526
last := len(cx.stack) - 1 // length
35213527
prev := last - 1 // start
3522-
byteArrayIdx := prev - 1 // bytes
3523-
startIdx := cx.stack[prev].Uint
3524-
lengthIdx := cx.stack[last].Uint
3525-
if startIdx > math.MaxInt32 || lengthIdx > math.MaxInt32 {
3526-
return errors.New("extract range beyond length of string")
3527-
}
3528-
bytes, err := opExtractImpl(cx.stack[byteArrayIdx].Bytes, int(startIdx), int(lengthIdx))
3529-
cx.stack[byteArrayIdx].Bytes = bytes
3528+
pprev := prev - 1 // bytes
3529+
3530+
start := cx.stack[prev].Uint
3531+
length := cx.stack[last].Uint
3532+
bytes, err := extractCarefully(cx.stack[pprev].Bytes, start, length)
3533+
cx.stack[pprev].Bytes = bytes
35303534
cx.stack = cx.stack[:prev]
35313535
return err
35323536
}
@@ -3542,11 +3546,11 @@ func convertBytesToInt(x []byte) uint64 {
35423546
return out
35433547
}
35443548

3545-
func opExtractNBytes(cx *EvalContext, n int) error {
3549+
func opExtractNBytes(cx *EvalContext, n uint64) error {
35463550
last := len(cx.stack) - 1 // start
35473551
prev := last - 1 // bytes
3548-
startIdx := cx.stack[last].Uint
3549-
bytes, err := opExtractImpl(cx.stack[prev].Bytes, int(startIdx), n) // extract n bytes
3552+
start := cx.stack[last].Uint
3553+
bytes, err := extractCarefully(cx.stack[prev].Bytes, start, n) // extract n bytes
35503554
if err != nil {
35513555
return err
35523556
}

data/transactions/logic/eval_test.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ func defaultEvalParamsWithVersion(version uint64, txns ...transactions.SignedTxn
129129
txns = []transactions.SignedTxn{{Txn: transactions.Transaction{Type: protocol.ApplicationCallTx}}}
130130
}
131131
ep := NewEvalParams(transactions.WrapSignedTxnsWithAD(txns), makeTestProtoV(version), &transactions.SpecialAddresses{})
132+
ep.Trace = &strings.Builder{}
132133
if empty {
133134
// We made an app type in order to get a full ep, but that sets MinTealVersion=2
134135
ep.TxnGroup[0].Txn.Type = "" // set it back
@@ -2299,41 +2300,41 @@ func TestExtractFlop(t *testing.T) {
22992300
err := testPanics(t, `byte 0xf000000000000000
23002301
extract 1 8
23012302
len`, 5)
2302-
require.Contains(t, err.Error(), "extract range beyond length of string")
2303+
require.Contains(t, err.Error(), "extraction end 9")
23032304

23042305
err = testPanics(t, `byte 0xf000000000000000
23052306
extract 9 0
23062307
len`, 5)
2307-
require.Contains(t, err.Error(), "extract range beyond length of string")
2308+
require.Contains(t, err.Error(), "extraction start 9")
23082309

23092310
err = testPanics(t, `byte 0xf000000000000000
23102311
int 4
23112312
int 0xFFFFFFFFFFFFFFFE
23122313
extract3
23132314
len`, 5)
2314-
require.Contains(t, err.Error(), "extract range beyond length of string")
2315+
require.Contains(t, err.Error(), "extraction end exceeds uint64")
23152316

23162317
err = testPanics(t, `byte 0xf000000000000000
23172318
int 100
23182319
int 2
23192320
extract3
23202321
len`, 5)
2321-
require.Contains(t, err.Error(), "extract range beyond length of string")
2322+
require.Contains(t, err.Error(), "extraction start 100")
23222323

23232324
err = testPanics(t, `byte 0xf000000000000000
23242325
int 55
23252326
extract_uint16`, 5)
2326-
require.Contains(t, err.Error(), "extract range beyond length of string")
2327+
require.Contains(t, err.Error(), "extraction start 55")
23272328

23282329
err = testPanics(t, `byte 0xf000000000000000
23292330
int 9
23302331
extract_uint32`, 5)
2331-
require.Contains(t, err.Error(), "extract range beyond length of string")
2332+
require.Contains(t, err.Error(), "extraction start 9")
23322333

23332334
err = testPanics(t, `byte 0xf000000000000000
23342335
int 1
23352336
extract_uint64`, 5)
2336-
require.Contains(t, err.Error(), "extract range beyond length of string")
2337+
require.Contains(t, err.Error(), "extraction end 9")
23372338
}
23382339

23392340
func TestLoadStore(t *testing.T) {

0 commit comments

Comments
 (0)