lnd.xprv/lnwire/features.go
nsa d65f17f1b1 fuzzing: fixed calculation of serializedSize() in features.go
This commit fixes an incorrectly calculated size of a
*FeatureVector in the serializedSize() function. go-fuzz
found that when calling NewFeatureVectorFromReader, if
a flag is invalid, it is not added to f.flags. However,
it will skip the index that wasn't included. This
becomes a problem when serializedSize() calculates the
length of f.flags via len() which can lead to an index
out of range since certain flags may be missing.
2017-09-19 16:21:31 -07:00

304 lines
9.1 KiB
Go

package lnwire
import (
"encoding/binary"
"io"
"math"
"github.com/go-errors/errors"
)
// featureFlag represent the status of the feature optional/required and needed
// to allow future incompatible changes, or backward compatible changes.
type featureFlag uint8
// String returns the string representation for the featureFlag.
func (f featureFlag) String() string {
switch f {
case OptionalFlag:
return "optional"
case RequiredFlag:
return "required"
default:
return "<unknown>"
}
}
// featureName represent the name of the feature and needed in order to have
// the compile errors if we specify wrong feature name.
type featureName string
const (
// OptionalFlag represent the feature which we already have but it
// isn't required yet, and if remote peer doesn't have this feature we
// may turn it off without disconnecting with peer.
OptionalFlag featureFlag = 2 // 0b10
// RequiredFlag represent the features which is required for proper
// peer interaction, we disconnect with peer if it doesn't have this
// particular feature.
RequiredFlag featureFlag = 1 // 0b01
// flagMask is a mask which is needed to extract feature flag value.
flagMask = 3 // 0b11
// flagBitsSize represent the size of the feature flag in bits. For
// more information read the init message specification.
flagBitsSize = 2
// maxAllowedSize is a maximum allowed size of feature vector.
//
// NOTE: Within the protocol, the maximum allowed message size is 65535
// bytes. Adding the overhead from the crypto protocol (the 2-byte
// packet length and 16-byte MAC), we arrive at 65569 bytes. Accounting
// for the overhead within the feature message to signal the type of
// the message, that leaves 65567 bytes for the init message itself.
// Next, we reserve 4-bytes to encode the lengths of both the local and
// global feature vectors, so 65563 for the global and local features.
// Knocking off one byte for the sake of the calculation, that leads to
// a max allowed size of 32781 bytes for each feature vector, or 131124
// different features.
maxAllowedSize = 32781
)
// Feature represent the feature which is used on stage of initialization of
// feature vector. Initial feature flags might be changed dynamically later.
type Feature struct {
Name featureName
Flag featureFlag
}
// FeatureVector represents the global/local feature vector. With this
// structure you may set/get the feature by name and compare feature vector
// with remote one.
type FeatureVector struct {
// featuresMap is the map which stores the correspondence between
// feature name and its index within feature vector. Index within
// feature vector and actual binary position of feature are different
// things)
featuresMap map[featureName]int // name -> index
// flags is the map which stores the correspondence between feature
// index and its flag.
flags map[int]featureFlag // index -> flag
}
// NewFeatureVector creates new instance of feature vector.
func NewFeatureVector(features []Feature) *FeatureVector {
featuresMap := make(map[featureName]int)
flags := make(map[int]featureFlag)
for index, feature := range features {
featuresMap[feature.Name] = index
flags[index] = feature.Flag
}
return &FeatureVector{
featuresMap: featuresMap,
flags: flags,
}
}
// SetFeatureFlag assign flag to the feature.
func (f *FeatureVector) SetFeatureFlag(name featureName, flag featureFlag) error {
position, ok := f.featuresMap[name]
if !ok {
return errors.Errorf("can't find feature with name: %v", name)
}
f.flags[position] = flag
return nil
}
// serializedSize returns the number of bytes which is needed to represent
// feature vector in byte format.
func (f *FeatureVector) serializedSize() uint16 {
// Find the largest index in f.flags
max := -1
for index := range f.flags {
if index > max {
max = index
}
}
if max == -1 {
return 0
}
// We calculate length via the largest index in f.flags so as to not
// get an index out of bounds in Encode's setFlag function.
return uint16(math.Ceil(float64(flagBitsSize*(max+1)) / 8))
}
// NewFeatureVectorFromReader decodes the feature vector from binary
// representation and creates the instance of it. Every feature decoded as 2
// bits where odd bit determine whether the feature is "optional" and even bit
// told us whether the feature is "required". The even/odd semantic allows
// future incompatible changes, or backward compatible changes. Bits generally
// assigned in pairs, so that optional features can later become compulsory.
func NewFeatureVectorFromReader(r io.Reader) (*FeatureVector, error) {
f := &FeatureVector{
flags: make(map[int]featureFlag),
}
getFlag := func(data []byte, position int) featureFlag {
byteNumber := uint(position / 8)
bitNumber := uint(position % 8)
return featureFlag((data[byteNumber] >> bitNumber) & flagMask)
}
// Read the length of the feature vector.
var l [2]byte
if _, err := io.ReadFull(r, l[:]); err != nil {
return nil, err
}
length := binary.BigEndian.Uint16(l[:])
// Read the feature vector data.
data := make([]byte, length)
if _, err := io.ReadFull(r, data[:]); err != nil {
return nil, err
}
// Initialize feature vector.
bitsNumber := len(data) * 8
for position := 0; position <= bitsNumber-flagBitsSize; position += flagBitsSize {
flag := getFlag(data, position)
switch flag {
case OptionalFlag, RequiredFlag:
// Every feature/flag takes 2 bits, so in order to get
// the feature/flag index we should divide position
// on 2.
index := position / flagBitsSize
f.flags[index] = flag
default:
continue
}
}
return f, nil
}
// Encode encodes the features vector into bytes representation, every feature
// encoded as 2 bits where odd bit determine whether the feature is "optional"
// and even bit told us whether the feature is "required". The even/odd
// semantic allows future incompatible changes, or backward compatible changes.
// Bits generally assigned in pairs, so that optional features can later become
// compulsory.
func (f *FeatureVector) Encode(w io.Writer) error {
setFlag := func(data []byte, position int, flag featureFlag) {
byteNumber := uint(position / 8)
bitNumber := uint(position % 8)
data[byteNumber] |= (byte(flag) << bitNumber)
}
// Write length of feature vector.
var l [2]byte
length := f.serializedSize()
binary.BigEndian.PutUint16(l[:], length)
if _, err := w.Write(l[:]); err != nil {
return err
}
// Generate the data and write it.
data := make([]byte, length)
for index, flag := range f.flags {
// Every feature takes 2 bits, so in order to get the feature
// bits position we should multiply index by 2.
position := index * flagBitsSize
setFlag(data, position, flag)
}
_, err := w.Write(data)
return err
}
// Compare checks that features are compatible and returns the features which
// were present in both remote and local feature vectors. If remote/local node
// doesn't have the feature and local/remote node require it than such vectors
// are incompatible.
func (f *FeatureVector) Compare(f2 *FeatureVector) (*SharedFeatures, error) {
shared := newSharedFeatures(f.Copy())
for index, flag := range f.flags {
if _, exist := f2.flags[index]; !exist {
switch flag {
case RequiredFlag:
return nil, errors.New("Remote node hasn't " +
"locally required feature")
case OptionalFlag:
// If feature is optional and remote side
// haven't it than it might be safely disabled.
delete(shared.flags, index)
continue
}
}
// If feature exists on both sides than such feature might be
// considered as active.
shared.flags[index] = flag
}
for index, flag := range f2.flags {
if _, exist := f.flags[index]; !exist {
switch flag {
case RequiredFlag:
return nil, errors.New("Local node hasn't " +
"locally required feature")
case OptionalFlag:
// If feature is optional and local side
// haven't it than it might be safely disabled.
delete(shared.flags, index)
continue
}
}
// If feature exists on both sides than such feature might be
// considered as active.
shared.flags[index] = flag
}
return shared, nil
}
// Copy generate new distinct instance of the feature vector.
func (f *FeatureVector) Copy() *FeatureVector {
features := make([]Feature, len(f.featuresMap))
for name, index := range f.featuresMap {
features[index] = Feature{
Name: name,
Flag: f.flags[index],
}
}
return NewFeatureVector(features)
}
// SharedFeatures is a product of comparison of two features vector which
// consist of features which are present in both local and remote features
// vectors.
type SharedFeatures struct {
*FeatureVector
}
// newSharedFeatures creates new shared features instance.
func newSharedFeatures(f *FeatureVector) *SharedFeatures {
return &SharedFeatures{f}
}
// IsActive checks is feature active or not, it might be disabled during
// comparision with remote feature vector if it was optional and remote peer
// doesn't support it.
func (f *SharedFeatures) IsActive(name featureName) bool {
index, ok := f.featuresMap[name]
if !ok {
// If we even have no such feature in feature map, than it
// can't be active in any circumstances.
return false
}
_, exist := f.flags[index]
return exist
}