-
Notifications
You must be signed in to change notification settings - Fork 378
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 panic on logVerifier.VerifyRoot when newRoot is nil #779
Fix panic on logVerifier.VerifyRoot when newRoot is nil #779
Conversation
client/verifier.go
Outdated
if err := tcrypto.Verify(c.pubKey, hash, newRoot.Signature); err != nil { | ||
return err | ||
if (newRoot == nil){ | ||
return fmt.Errorf("VerifyRoot() error: newRoot == nil.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error strings should not be capitalized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd remove the "." at the end too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, sorry for the back and forth here, but since the capitalized string is actually the method name, what you had originally is correct.
We don't capitalize sentences in errors because error chaining is a common practice, so it would look weird, but in this case it works fine.
I'd go with a small change, which is dropping the " error" part: fmt.Errorf("VerifyRoot(): newRoot == nil")
client/verifier.go
Outdated
return err | ||
if (newRoot == nil){ | ||
return fmt.Errorf("VerifyRoot() error: newRoot == nil.") | ||
}else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block can be outdented
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you mean. Do you want me to take the code out of the else branch, as the else is redundant since when the if branch is taken the function returns?
I wrote it like this as I think it is easier to see that in the else branch newRoot cannot be null, but happy to switch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gdbelvin I made the switch. Please let me know if this is what you meant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not Gary, but it looks good. :)
Just clarifying, it's somewhat of a general Go-style rule that, if you return inside the if, you omit the else clause.
Eg, we prefer:
if something {
return fmt.Errorf("...")
}
// code continues here
instead of
if something {
return fmt.Errorf("...")
} else {
// code continues here
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Just a few code style comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Antonio, thanks for the contributions.
Could you please add a test for VerifyRoot where newRoot is nil?
@@ -8,5 +8,6 @@ | |||
# | |||
# Please keep the list sorted. | |||
|
|||
Antonio Marcedone <a.marcedone@gmail.com> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add you to contributors, but not authors. It has a different meaning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the contributors file:
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file, depending on whether the
# individual or corporate CLA was used.
I signed the individual CLA.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. What got me is that, apparently, we're missing a bunch of people at AUTHORS, so I understood their absence as a sign that we shouldn't do that. A glimpse at another repo quickly showed my error.
Edit: expanded explanation.
client/verifier.go
Outdated
@@ -22,6 +22,7 @@ import ( | |||
tcrypto "github.com/google/trillian/crypto" | |||
"github.com/google/trillian/merkle" | |||
"github.com/google/trillian/merkle/hashers" | |||
"fmt" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Please move "fmt" to the block of Golang imports (below crypto/sha256)
client/verifier.go
Outdated
hash := tcrypto.HashLogRoot(*newRoot) | ||
if err := tcrypto.Verify(c.pubKey, hash, newRoot.Signature); err != nil { | ||
return err | ||
if (newRoot == nil){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Parenthesis aren't usually in Go ifs (unless really needed)
client/verifier.go
Outdated
if err := tcrypto.Verify(c.pubKey, hash, newRoot.Signature); err != nil { | ||
return err | ||
if (newRoot == nil){ | ||
return fmt.Errorf("VerifyRoot() error: newRoot == nil.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd remove the "." at the end too.
client/verifier.go
Outdated
} | ||
|
||
// Implicitly trust the first root we get. | ||
if trusted.TreeSize != 0 { | ||
if trusted.GetTreeSize() != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason for the change on the getters here and below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. If trusted == nil, trusted.TreeSize is a nil pointer dereference panic which causes SEGFAULT when tested. The getter prevents this (if you look at the code, it just adds a nil test).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling VerifyRoot with trusted = nil sounds like a programming error to me. I think we should error out earlier, just as we do if newRoot == nil.
@gdbelvin, what say you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would agree. Clients should explicitly create an initial trusted root to work with.
This can easily be a static object with TreeSize == 0 and RootHash = empty root
crypto/data_formats.go
Outdated
mapKeyRootHash: base64.StdEncoding.EncodeToString(root.RootHash), | ||
mapKeyTimestampNanos: strconv.FormatInt(root.TimestampNanos, 10), | ||
mapKeyTreeSize: strconv.FormatInt(root.TreeSize, 10)} | ||
mapKeyRootHash: base64.StdEncoding.EncodeToString(root.GetRootHash()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason for the changes here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, getters prevent nil pointer dereference errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
root is not a pointer, you can't really pass nil to this function. (Maybe it should be a pointer, but that's another discussion.)
Could you edit the PR's title as well, it's too generic and will be ambiguous once in the Git log. I suggest something like "Fix panic on logVerifier.VerifyRoot when newRoot is nil" |
…reference errors/segfaults. Other minor nits in response to PR reviews.
@gdbelvin and @codingllama thanks a lot for your reviews! |
@@ -8,5 +8,6 @@ | |||
# | |||
# Please keep the list sorted. | |||
|
|||
Antonio Marcedone <a.marcedone@gmail.com> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. What got me is that, apparently, we're missing a bunch of people at AUTHORS, so I understood their absence as a sign that we shouldn't do that. A glimpse at another repo quickly showed my error.
Edit: expanded explanation.
client/client_test.go
Outdated
@@ -254,3 +257,42 @@ func TestUpdateRoot(t *testing.T) { | |||
t.Errorf("Tree size after add Leaf: %v, want > %v", got, want) | |||
} | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we move the tests to verifier_test.go
?
client/client_test.go
Outdated
@@ -254,3 +257,42 @@ func TestUpdateRoot(t *testing.T) { | |||
t.Errorf("Tree size after add Leaf: %v, want > %v", got, want) | |||
} | |||
} | |||
|
|||
func TestVerifyRootReturnsErrWhenNewRootIsNil(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
go style nit: Could we structure both tests as a single, table-driven test?
func TestVerifyRootErrors(t *testing.T) {
tests := []struct {
desc string
trusted, newRoot *trillian.SignedLogRoot
}{
{desc: "newRootNil", trusted: someRoot, newRoot: nil},
{desc: "trustedNil",trusted: nil, newRoot: someRoot},
// so on...
}
for _, test := range tests {
if err := verifier.VerifyRoot(test.trusted, test.newRoot); err == nil {
t.Errorf("%v: VerifyRoot() returned err = nil", test.desc)
}
}
}
client/verifier.go
Outdated
@@ -18,6 +18,7 @@ import ( | |||
"crypto" | |||
"crypto/sha256" | |||
|
|||
"fmt" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Move to the block above
client/verifier.go
Outdated
} | ||
|
||
// Implicitly trust the first root we get. | ||
if trusted.TreeSize != 0 { | ||
if trusted.GetTreeSize() != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling VerifyRoot with trusted = nil sounds like a programming error to me. I think we should error out earlier, just as we do if newRoot == nil.
@gdbelvin, what say you?
client/verifier.go
Outdated
@@ -68,14 +72,14 @@ func (c *logVerifier) VerifyRoot(trusted, newRoot *trillian.SignedLogRoot, | |||
// the currently trusted root. The inclusion proof must be requested for Root().TreeSize. | |||
func (c *logVerifier) VerifyInclusionAtIndex(trusted *trillian.SignedLogRoot, data []byte, leafIndex int64, proof [][]byte) error { | |||
leaf := c.buildLeaf(data) | |||
return c.v.VerifyInclusionProof(leafIndex, trusted.TreeSize, | |||
proof, trusted.RootHash, leaf.MerkleLeafHash) | |||
return c.v.VerifyInclusionProof(leafIndex, trusted.GetTreeSize(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto here and at VerifyInclusionByHash. I'd rather error out or panic, silently proceeding makes the trusted = nil
misstep harder to detect.
Please add a test for VerifyInclusion* methods too, if you decide to touch them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A panic (SEGFAULT) is bad for gobind integration (the android app hangs) and might cause errors too, so I would go for an error instead.
crypto/data_formats.go
Outdated
mapKeyRootHash: base64.StdEncoding.EncodeToString(root.RootHash), | ||
mapKeyTimestampNanos: strconv.FormatInt(root.TimestampNanos, 10), | ||
mapKeyTreeSize: strconv.FormatInt(root.TreeSize, 10)} | ||
mapKeyRootHash: base64.StdEncoding.EncodeToString(root.GetRootHash()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
root is not a pointer, you can't really pass nil to this function. (Maybe it should be a pointer, but that's another discussion.)
That sounds like the right path. Thanks for taking this on Antonio
…On Tue, Aug 8, 2017 at 10:41 PM Antonio Marcedone ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In client/verifier.go
<#779 (comment)>:
> @@ -68,14 +72,14 @@ func (c *logVerifier) VerifyRoot(trusted, newRoot *trillian.SignedLogRoot,
// the currently trusted root. The inclusion proof must be requested for Root().TreeSize.
func (c *logVerifier) VerifyInclusionAtIndex(trusted *trillian.SignedLogRoot, data []byte, leafIndex int64, proof [][]byte) error {
leaf := c.buildLeaf(data)
- return c.v.VerifyInclusionProof(leafIndex, trusted.TreeSize,
- proof, trusted.RootHash, leaf.MerkleLeafHash)
+ return c.v.VerifyInclusionProof(leafIndex, trusted.GetTreeSize(),
A panic (SEGFAULT) is bad for gobind integration (the android app hangs)
and might cause errors too, so I would go for an error instead.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#779 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAMHTni3UXEUZU6Cf9z6M16h1s7sn9qxks5sWNYYgaJpZM4OuWsN>
.
|
29fdcb3
to
03c7b51
Compare
Updated after the most recent comments. And thanks for your patience, I am just starting to write in go for this project :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for sticking with us, Antonio. I've a few more nits left and a comment asking for auto-formatting, but I think we're close to a merge here.
client/verifier_test.go
Outdated
"github.com/google/trillian/crypto/keys" | ||
"github.com/google/trillian/merkle/rfc6962" | ||
"github.com/google/trillian/testonly" | ||
"strings" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Move "strings" above "testing", on the Golang libraries block.
client/verifier_test.go
Outdated
logVerifier := NewLogVerifier(nil, nil) | ||
err := logVerifier.VerifyInclusionAtIndex(nil, nil, 1, nil) | ||
if err == nil { | ||
t.Errorf("VerifyInclusionAtIndex() error expected, but none was thrown") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Technically, Go errors aren't "thrown", that's Java/C++/etc lingo. ;)
Ditto below.
client/verifier_test.go
Outdated
if err == nil { | ||
t.Errorf("VerifyInclusionAtIndex() error expected, but none was thrown") | ||
} | ||
if !strings.Contains(err.Error(), "trusted == nil"){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather drop the error string check and call VerifyInclusionAtIndex with only trusted = nil, so it's clear what the source of the error is. Error message checking lands too close to change detector tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your point.
I removed the check and added a comment.
client/verifier_test.go
Outdated
func TestVerifyInclusionByHashErrors(t *testing.T) { | ||
tests := []struct { | ||
desc string | ||
trusted *trillian.SignedLogRoot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're missing a gofmt run here. Try doing scripts/presubmit.sh --fix --no-generate
, that should do all the formatting needed.
client/verifier_test.go
Outdated
if err == nil { | ||
t.Errorf("%v: VerifyInclusionByHash() error expected, but none was thrown", test.desc) | ||
} | ||
if !strings.Contains(err.Error(), test.errorDesc){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: As above, I think it's fine to drop this.
client/verifier_test.go
Outdated
t.Fatalf("Failed to load public key, err=%v", err) | ||
} | ||
|
||
signedRoot := trillian.SignedLogRoot{0, nil, 0, nil, 0, 0} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be rewritten as trillian.SignedLogRoot{}
since all the fields have their default values
b130d35
to
a312f4e
Compare
Updated. PTAL and thanks again :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, Antonio!
@gdbelvin, feel free to squash/merge after you approve.
Oh, nevermind, I see that it was deferred to me. Merging. |
Solves #778