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 panic on logVerifier.VerifyRoot when newRoot is nil #779

Merged
merged 10 commits into from
Aug 10, 2017

Conversation

AMarcedone
Copy link
Contributor

Solves #778

if err := tcrypto.Verify(c.pubKey, hash, newRoot.Signature); err != nil {
return err
if (newRoot == nil){
return fmt.Errorf("VerifyRoot() error: newRoot == nil.")
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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")

return err
if (newRoot == nil){
return fmt.Errorf("VerifyRoot() error: newRoot == nil.")
}else {
Copy link
Contributor

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

Copy link
Contributor Author

@AMarcedone AMarcedone Aug 7, 2017

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

Copy link
Contributor Author

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.

Copy link
Contributor

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
}

Copy link
Contributor

@gdbelvin gdbelvin left a 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.

Copy link
Contributor

@codingllama codingllama left a 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>
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

@codingllama codingllama Aug 8, 2017

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.

@@ -22,6 +22,7 @@ import (
tcrypto "github.com/google/trillian/crypto"
"github.com/google/trillian/merkle"
"github.com/google/trillian/merkle/hashers"
"fmt"
Copy link
Contributor

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)

hash := tcrypto.HashLogRoot(*newRoot)
if err := tcrypto.Verify(c.pubKey, hash, newRoot.Signature); err != nil {
return err
if (newRoot == nil){
Copy link
Contributor

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)

if err := tcrypto.Verify(c.pubKey, hash, newRoot.Signature); err != nil {
return err
if (newRoot == nil){
return fmt.Errorf("VerifyRoot() error: newRoot == nil.")
Copy link
Contributor

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.

}

// Implicitly trust the first root we get.
if trusted.TreeSize != 0 {
if trusted.GetTreeSize() != 0 {
Copy link
Contributor

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?

Copy link
Contributor Author

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).

Copy link
Contributor

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?

Copy link
Contributor

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

mapKeyRootHash: base64.StdEncoding.EncodeToString(root.RootHash),
mapKeyTimestampNanos: strconv.FormatInt(root.TimestampNanos, 10),
mapKeyTreeSize: strconv.FormatInt(root.TreeSize, 10)}
mapKeyRootHash: base64.StdEncoding.EncodeToString(root.GetRootHash()),
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.)

@codingllama
Copy link
Contributor

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"

@AMarcedone AMarcedone changed the title Fix nil pointer dereference bug Fix panic on logVerifier.VerifyRoot when newRoot is nil Aug 7, 2017
…reference errors/segfaults. Other minor nits in response to PR reviews.
@AMarcedone
Copy link
Contributor Author

@gdbelvin and @codingllama thanks a lot for your reviews!
I made the requested changes. PTAL

@@ -8,5 +8,6 @@
#
# Please keep the list sorted.

Antonio Marcedone <a.marcedone@gmail.com>
Copy link
Contributor

@codingllama codingllama Aug 8, 2017

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.

@@ -254,3 +257,42 @@ func TestUpdateRoot(t *testing.T) {
t.Errorf("Tree size after add Leaf: %v, want > %v", got, want)
}
}

Copy link
Contributor

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?

@@ -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) {
Copy link
Contributor

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)
    }
  }
}

https://github.com/golang/go/wiki/TableDrivenTests

@@ -18,6 +18,7 @@ import (
"crypto"
"crypto/sha256"

"fmt"
Copy link
Contributor

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

}

// Implicitly trust the first root we get.
if trusted.TreeSize != 0 {
if trusted.GetTreeSize() != 0 {
Copy link
Contributor

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?

@@ -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(),
Copy link
Contributor

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.

Copy link
Contributor Author

@AMarcedone AMarcedone Aug 8, 2017

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.

mapKeyRootHash: base64.StdEncoding.EncodeToString(root.RootHash),
mapKeyTimestampNanos: strconv.FormatInt(root.TimestampNanos, 10),
mapKeyTreeSize: strconv.FormatInt(root.TreeSize, 10)}
mapKeyRootHash: base64.StdEncoding.EncodeToString(root.GetRootHash()),
Copy link
Contributor

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.)

@gdbelvin
Copy link
Contributor

gdbelvin commented Aug 8, 2017 via email

@AMarcedone AMarcedone force-pushed the fix_nil_pointer_dereference_bug branch from 29fdcb3 to 03c7b51 Compare August 8, 2017 22:48
@AMarcedone
Copy link
Contributor Author

Updated after the most recent comments. And thanks for your patience, I am just starting to write in go for this project :)
PTAL

Copy link
Contributor

@codingllama codingllama left a 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.

"github.com/google/trillian/crypto/keys"
"github.com/google/trillian/merkle/rfc6962"
"github.com/google/trillian/testonly"
"strings"
Copy link
Contributor

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.

logVerifier := NewLogVerifier(nil, nil)
err := logVerifier.VerifyInclusionAtIndex(nil, nil, 1, nil)
if err == nil {
t.Errorf("VerifyInclusionAtIndex() error expected, but none was thrown")
Copy link
Contributor

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.

if err == nil {
t.Errorf("VerifyInclusionAtIndex() error expected, but none was thrown")
}
if !strings.Contains(err.Error(), "trusted == nil"){
Copy link
Contributor

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.

Copy link
Contributor Author

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.

func TestVerifyInclusionByHashErrors(t *testing.T) {
tests := []struct {
desc string
trusted *trillian.SignedLogRoot
Copy link
Contributor

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.

if err == nil {
t.Errorf("%v: VerifyInclusionByHash() error expected, but none was thrown", test.desc)
}
if !strings.Contains(err.Error(), test.errorDesc){
Copy link
Contributor

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.

t.Fatalf("Failed to load public key, err=%v", err)
}

signedRoot := trillian.SignedLogRoot{0, nil, 0, nil, 0, 0}
Copy link
Contributor

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

@gdbelvin gdbelvin dismissed their stale review August 9, 2017 10:12

deferring to codingllama

@AMarcedone AMarcedone force-pushed the fix_nil_pointer_dereference_bug branch from b130d35 to a312f4e Compare August 9, 2017 18:37
@AMarcedone
Copy link
Contributor Author

Updated. PTAL and thanks again :)

Copy link
Contributor

@codingllama codingllama left a 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.

@codingllama
Copy link
Contributor

Oh, nevermind, I see that it was deferred to me. Merging.

@codingllama codingllama merged commit 29bc71f into google:master Aug 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants