Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make 32-bit safe and stable & add to CI #407

Merged
merged 1 commit into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,15 @@ jobs:
with:
submodules: 'true'
- name: Test
run: go test ./...
uses: protocol/[email protected]
with:
run: go test ./...
- name: Tests (32 bit)
if: ${{ matrix.os != 'macos-latest' }} # can't run 32 bit tests on OSX
uses: protocol/[email protected]
env:
GOARCH: 386
with:
run: |
export "PATH=${{ env.PATH_386 }}:$PATH"
go test ./...
36 changes: 18 additions & 18 deletions codec/dagcbor/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ func Unmarshal(na datamodel.NodeAssembler, tokSrc shared.TokenSource, options De
// This is a DoS defense mechanism.
// It's *roughly* in units of bytes (but only very, VERY roughly) -- it also treats words as 1 in many cases.
// FUTURE: this ought be configurable somehow. (How, and at what granularity though?)
var gas int = 1048576 * 10
var gas int64 = 1048576 * 10
return unmarshal1(na, tokSrc, &gas, options)
}

func unmarshal1(na datamodel.NodeAssembler, tokSrc shared.TokenSource, gas *int, options DecodeOptions) error {
func unmarshal1(na datamodel.NodeAssembler, tokSrc shared.TokenSource, gas *int64, options DecodeOptions) error {
var tk tok.Token
done, err := tokSrc.Step(&tk)
if err == io.EOF {
Expand All @@ -121,25 +121,25 @@ func unmarshal1(na datamodel.NodeAssembler, tokSrc shared.TokenSource, gas *int,

// starts with the first token already primed. Necessary to get recursion
// to flow right without a peek+unpeek system.
func unmarshal2(na datamodel.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, gas *int, options DecodeOptions) error {
func unmarshal2(na datamodel.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, gas *int64, options DecodeOptions) error {
// FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want).
switch tk.Type {
case tok.TMapOpen:
expectLen := tk.Length
allocLen := tk.Length
expectLen := int64(tk.Length)
allocLen := int64(tk.Length)
if tk.Length == -1 {
expectLen = math.MaxInt32
expectLen = math.MaxInt64
allocLen = 0
} else {
if *gas-allocLen < 0 { // halt early if this will clearly demand too many resources
return ErrAllocationBudgetExceeded
}
}
ma, err := na.BeginMap(int64(allocLen))
ma, err := na.BeginMap(allocLen)
if err != nil {
return err
}
observedLen := 0
var observedLen int64
lastKey := ""
for {
_, err := tokSrc.Step(tk)
Expand All @@ -148,12 +148,12 @@ func unmarshal2(na datamodel.NodeAssembler, tokSrc shared.TokenSource, tk *tok.T
}
switch tk.Type {
case tok.TMapClose:
if expectLen != math.MaxInt32 && observedLen != expectLen {
if expectLen != math.MaxInt64 && observedLen != expectLen {
return fmt.Errorf("unexpected mapClose before declared length")
}
return ma.Finish()
case tok.TString:
*gas -= len(tk.Str) + mapEntryGasScore
*gas -= int64(len(tk.Str) + mapEntryGasScore)
if *gas < 0 {
return ErrAllocationBudgetExceeded
}
Expand Down Expand Up @@ -183,29 +183,29 @@ func unmarshal2(na datamodel.NodeAssembler, tokSrc shared.TokenSource, tk *tok.T
case tok.TMapClose:
return fmt.Errorf("unexpected mapClose token")
case tok.TArrOpen:
expectLen := tk.Length
allocLen := tk.Length
expectLen := int64(tk.Length)
allocLen := int64(tk.Length)
if tk.Length == -1 {
expectLen = math.MaxInt32
expectLen = math.MaxInt64
allocLen = 0
} else {
if *gas-allocLen < 0 { // halt early if this will clearly demand too many resources
return ErrAllocationBudgetExceeded
}
}
la, err := na.BeginList(int64(allocLen))
la, err := na.BeginList(allocLen)
if err != nil {
return err
}
observedLen := 0
var observedLen int64
for {
_, err := tokSrc.Step(tk)
if err != nil {
return err
}
switch tk.Type {
case tok.TArrClose:
if expectLen != math.MaxInt32 && observedLen != expectLen {
if expectLen != math.MaxInt64 && observedLen != expectLen {
return fmt.Errorf("unexpected arrClose before declared length")
}
return la.Finish()
Expand All @@ -229,13 +229,13 @@ func unmarshal2(na datamodel.NodeAssembler, tokSrc shared.TokenSource, tk *tok.T
case tok.TNull:
return na.AssignNull()
case tok.TString:
*gas -= len(tk.Str)
*gas -= int64(len(tk.Str))
if *gas < 0 {
return ErrAllocationBudgetExceeded
}
return na.AssignString(tk.Str)
case tok.TBytes:
*gas -= len(tk.Bytes)
*gas -= int64(len(tk.Bytes))
if *gas < 0 {
return ErrAllocationBudgetExceeded
}
Expand Down
22 changes: 19 additions & 3 deletions codec/dagcbor/unmarshal_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dagcbor

import (
"runtime"
"strings"
"testing"

Expand All @@ -24,21 +25,36 @@ func TestFunBlocks(t *testing.T) {
buf := strings.NewReader("\x9a\xff000")
nb := basicnode.Prototype.Any.NewBuilder()
err := Decode(nb, buf)
qt.Assert(t, err, qt.Equals, ErrAllocationBudgetExceeded)
if runtime.GOARCH == "386" {
// TODO: fix refmt to properly handle 64-bit ints on 32-bit runtime
qt.Assert(t, err.Error(), qt.Equals, "cbor: positive integer is out of length")
} else {
qt.Assert(t, err, qt.Equals, ErrAllocationBudgetExceeded)
}
})
t.Run("fuzz002", func(t *testing.T) {
// This fixture might cause an overly large allocation if you aren't careful to have resource budgets.
buf := strings.NewReader("\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9a\xff000")
nb := basicnode.Prototype.Any.NewBuilder()
err := Decode(nb, buf)
qt.Assert(t, err, qt.Equals, ErrAllocationBudgetExceeded)
if runtime.GOARCH == "386" {
// TODO: fix refmt to properly handle 64-bit ints on 32-bit
qt.Assert(t, err.Error(), qt.Equals, "cbor: positive integer is out of length")
} else {
qt.Assert(t, err, qt.Equals, ErrAllocationBudgetExceeded)
}
})
t.Run("fuzz003", func(t *testing.T) {
// This fixture might cause an overly large allocation if you aren't careful to have resource budgets.
buf := strings.NewReader("\x9f\x9f\x9f\x9f\x9f\x9f\x9f\xbb00000000")
nb := basicnode.Prototype.Any.NewBuilder()
err := Decode(nb, buf)
qt.Assert(t, err, qt.Equals, ErrAllocationBudgetExceeded)
if runtime.GOARCH == "386" {
// TODO: fix refmt to properly handle 64-bit ints on 32-bit
qt.Assert(t, err.Error(), qt.Equals, "cbor: positive integer is out of length")
} else {
qt.Assert(t, err, qt.Equals, ErrAllocationBudgetExceeded)
}
})
t.Run("undef", func(t *testing.T) {
// This fixture tests that we tolerate cbor's "undefined" token (even though it's noncanonical and you shouldn't use it),
Expand Down
4 changes: 2 additions & 2 deletions schema/gen/go/testEngine_nocgo_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:build !cgo && !skipgenbehavtests
// +build !cgo,!skipgenbehavtests
//go:build !cgo && !skipgenbehavtests && !windows
// +build !cgo,!skipgenbehavtests,!windows

// Confession:
// This build tag specification is NOT sufficient nor necessarily correct --
Expand Down