Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
ld
go-ld-prime
Commits
6428f6bb
Unverified
Commit
6428f6bb
authored
Oct 20, 2020
by
Eric Myhre
Committed by
GitHub
Oct 20, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #85 from ipld/resource-budget-for-dagcbor-parser
Implement resource budgets in dagcbor parsing.
parents
6a417032
344457a5
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
60 additions
and
5 deletions
+60
-5
codec/dagcbor/unmarshal.go
codec/dagcbor/unmarshal.go
+60
-5
No files found.
codec/dagcbor/unmarshal.go
View file @
6428f6bb
...
...
@@ -15,6 +15,12 @@ import (
var
(
ErrInvalidMultibase
=
errors
.
New
(
"invalid multibase on IPLD link"
)
ErrAllocationBudgetExceeded
=
errors
.
New
(
"message structure demanded too many resources to process"
)
)
const
(
mapEntryGasScore
=
8
listEntryGasScore
=
4
)
// This should be identical to the general feature in the parent package,
...
...
@@ -22,6 +28,15 @@ var (
// which has dag-cbor's special sauce for detecting schemafree links.
func
Unmarshal
(
na
ipld
.
NodeAssembler
,
tokSrc
shared
.
TokenSource
)
error
{
// Have a gas budget, which will be decremented as we allocate memory, and an error returned when execeeded (or about to be exceeded).
// 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
return
unmarshal1
(
na
,
tokSrc
,
&
gas
)
}
func
unmarshal1
(
na
ipld
.
NodeAssembler
,
tokSrc
shared
.
TokenSource
,
gas
*
int
)
error
{
var
tk
tok
.
Token
done
,
err
:=
tokSrc
.
Step
(
&
tk
)
if
err
!=
nil
{
...
...
@@ -30,12 +45,12 @@ func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource) error {
if
done
&&
!
tk
.
Type
.
IsValue
()
{
return
fmt
.
Errorf
(
"unexpected eof"
)
}
return
unmarshal
(
na
,
tokSrc
,
&
tk
)
return
unmarshal
2
(
na
,
tokSrc
,
&
tk
,
gas
)
}
// starts with the first token already primed. Necessary to get recursion
// to flow right without a peek+unpeek system.
func
unmarshal
(
na
ipld
.
NodeAssembler
,
tokSrc
shared
.
TokenSource
,
tk
*
tok
.
Token
)
error
{
func
unmarshal
2
(
na
ipld
.
NodeAssembler
,
tokSrc
shared
.
TokenSource
,
tk
*
tok
.
Token
,
gas
*
int
)
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
:
...
...
@@ -44,6 +59,10 @@ func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token)
if
tk
.
Length
==
-
1
{
expectLen
=
math
.
MaxInt32
allocLen
=
0
}
else
{
if
*
gas
-
allocLen
<
0
{
// halt early if this will clearly demand too many resources
return
ErrAllocationBudgetExceeded
}
}
ma
,
err
:=
na
.
BeginMap
(
allocLen
)
if
err
!=
nil
{
...
...
@@ -62,6 +81,10 @@ func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token)
}
return
ma
.
Finish
()
case
tok
.
TString
:
*
gas
-=
len
(
tk
.
Str
)
+
mapEntryGasScore
if
*
gas
<
0
{
return
ErrAllocationBudgetExceeded
}
// continue
default
:
return
fmt
.
Errorf
(
"unexpected %s token while expecting map key"
,
tk
.
Type
)
...
...
@@ -74,7 +97,7 @@ func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token)
if
err
!=
nil
{
// return in error if the key was rejected
return
err
}
err
=
U
nmarshal
(
mva
,
tokSrc
)
err
=
u
nmarshal
1
(
mva
,
tokSrc
,
gas
)
if
err
!=
nil
{
// return in error if some part of the recursion errored
return
err
}
...
...
@@ -87,6 +110,10 @@ func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token)
if
tk
.
Length
==
-
1
{
expectLen
=
math
.
MaxInt32
allocLen
=
0
}
else
{
if
*
gas
-
allocLen
<
0
{
// halt early if this will clearly demand too many resources
return
ErrAllocationBudgetExceeded
}
}
la
,
err
:=
na
.
BeginList
(
allocLen
)
if
err
!=
nil
{
...
...
@@ -105,11 +132,15 @@ func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token)
}
return
la
.
Finish
()
default
:
*
gas
-=
listEntryGasScore
if
*
gas
<
0
{
return
ErrAllocationBudgetExceeded
}
observedLen
++
if
observedLen
>
expectLen
{
return
fmt
.
Errorf
(
"unexpected continuation of array elements beyond declared length"
)
}
err
:=
unmarshal
(
la
.
AssembleValue
(),
tokSrc
,
tk
)
err
:=
unmarshal
2
(
la
.
AssembleValue
(),
tokSrc
,
tk
,
gas
)
if
err
!=
nil
{
// return in error if some part of the recursion errored
return
err
}
...
...
@@ -120,8 +151,16 @@ func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token)
case
tok
.
TNull
:
return
na
.
AssignNull
()
case
tok
.
TString
:
*
gas
-=
len
(
tk
.
Str
)
if
*
gas
<
0
{
return
ErrAllocationBudgetExceeded
}
return
na
.
AssignString
(
tk
.
Str
)
case
tok
.
TBytes
:
*
gas
-=
len
(
tk
.
Bytes
)
if
*
gas
<
0
{
return
ErrAllocationBudgetExceeded
}
if
!
tk
.
Tagged
{
return
na
.
AssignBytes
(
tk
.
Bytes
)
}
...
...
@@ -139,12 +178,28 @@ func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token)
return
fmt
.
Errorf
(
"unhandled cbor tag %d"
,
tk
.
Tag
)
}
case
tok
.
TBool
:
*
gas
-=
1
if
*
gas
<
0
{
return
ErrAllocationBudgetExceeded
}
return
na
.
AssignBool
(
tk
.
Bool
)
case
tok
.
TInt
:
*
gas
-=
1
if
*
gas
<
0
{
return
ErrAllocationBudgetExceeded
}
return
na
.
AssignInt
(
int
(
tk
.
Int
))
// FIXME overflow check
case
tok
.
TUint
:
*
gas
-=
1
if
*
gas
<
0
{
return
ErrAllocationBudgetExceeded
}
return
na
.
AssignInt
(
int
(
tk
.
Uint
))
// FIXME overflow check
case
tok
.
TFloat64
:
*
gas
-=
1
if
*
gas
<
0
{
return
ErrAllocationBudgetExceeded
}
return
na
.
AssignFloat
(
tk
.
Float64
)
default
:
panic
(
"unreachable"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment