Skip to content

libs/bits: BitArray.FromProto code invalidly tries to clear pointer receiver, plus uses a reference of Elements instead of a copy #5705

@odeke-em

Description

@odeke-em

Tendermint version (use tendermint version or git rev-parse --verify HEAD if installed from source):
Version 170cb70

What happened:
Auditing dependencies for the cosmos-sdk and I noticed this code

func (bA *BitArray) FromProto(protoBitArray *tmprotobits.BitArray) {
if protoBitArray == nil {
bA = nil
return
}
bA.Bits = int(protoBitArray.Bits)
if len(protoBitArray.Elems) > 0 {
bA.Elems = protoBitArray.Elems
}
}

Problems in it:

a) If protoBitArray is nil, it tries to set the pointer receiver to nil; but that operation doesn't reflect on the outside, because of the way pointer receivers are created demoed at https://play.golang.org/p/6IXb-hsVJ7s or inlined below

package main

import "fmt"

func main() {
	s := &sk{dt: []int{12, 19}}
	s.fromOther(nil)
	fmt.Printf("%#v\n", s)
}

type sk struct {
	dt []int
}

func (s *sk) fromOther(dk *sk) {
	if dk == nil {
		s = nil
		return
	}
	s.dt = dk.dt
}

it'll still print the prior result

&main.sk{dt:[]int{12, 19}}

Did we mean to instead clear out all the fields of the receiver?
It doesn't work because to change a pointer value, you need an lvalue aka a variable receiving it. If it were a **, then one could do *s = nil.

b) FromBitArray blindly reuses the slice passed in from its arguments; which means that someone can later change the underlying slice and mutate the end result
c) ToProto also blindly passes in the underlying slice

return &tmprotobits.BitArray{
Bits: int64(bA.Bits),
Elems: bA.Elems,
}

For both b) and c) we should make a copy of the slice Elems.

Metadata

Metadata

Assignees

Labels

C:libsComponent: LibraryT:bugType Bug (Confirmed)

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions