Skip to content

Commit

Permalink
CBG-3238 perform a length check on raw bytes (#6438)
Browse files Browse the repository at this point in the history
* CBG-3238 perform a length check on raw bytes

- refactor attachment compaction tests so we can make sure that it doesn't terminate

* Add comment

* remove redundant returns

* Move error handling into getAttachmentSyncData

* PR fixup

- remove attachment compaction changes and leave only a change to avoid
  failure to mark changes if xattr length is invalid. Treat this as a
  document without xattrs in order to not fail attachment compaction
  test.
- add code to prevent overflow if xattr len is a number between 1-4
  which would cause a panic.

* Update tests

* remove test tested in separate function
  • Loading branch information
torcolvin committed Sep 21, 2023
1 parent 553ac08 commit 1488a8b
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 1 deletion.
3 changes: 3 additions & 0 deletions base/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ var (
ErrXattrNotFound = &sgError{"Xattr Not Found"}
ErrTimeout = &sgError{"Operation timed out"}

// ErrXattrInvalidLen is returned if the xattr is corrupt.
ErrXattrInvalidLen = &sgError{"Xattr stream length"}

// ErrPartialViewErrors is returned if the view call contains any partial errors.
// This is more of a warning, and inspecting ViewResult.Errors is required for detail.
ErrPartialViewErrors = &sgError{"Partial errors in view"}
Expand Down
2 changes: 1 addition & 1 deletion db/attachment_compaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func getAttachmentSyncData(dataType uint8, data []byte) (*AttachmentCompactionDa
if dataType&base.MemcachedDataTypeXattr != 0 {
body, xattr, _, err := parseXattrStreamData(base.SyncXattrName, "", data)
if err != nil {
if errors.Is(err, base.ErrXattrNotFound) {
if errors.Is(err, base.ErrXattrNotFound) || errors.Is(err, base.ErrXattrInvalidLen) {
return nil, nil
}
return nil, err
Expand Down
9 changes: 9 additions & 0 deletions db/change_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2243,3 +2243,12 @@ func BenchmarkDocChanged(b *testing.B) {
})
}
}

func TestInvalidXattrStream(t *testing.T) {

body, xattr, userXattr, err := parseXattrStreamData(base.SyncXattrName, "", []byte("abcde"))
require.Error(t, err)
require.Nil(t, body)
require.Nil(t, xattr)
require.Nil(t, userXattr)
}
3 changes: 3 additions & 0 deletions db/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,9 @@ func parseXattrStreamData(xattrName string, userXattrName string, data []byte) (
}

xattrsLen := binary.BigEndian.Uint32(data[0:4])
if int(xattrsLen+4) > len(data) {
return nil, nil, nil, fmt.Errorf("%w (%d) from bytes %+v", base.ErrXattrInvalidLen, xattrsLen, data[0:4])
}
body = data[xattrsLen+4:]
if xattrsLen == 0 {
return body, nil, nil, nil
Expand Down
59 changes: 59 additions & 0 deletions db/document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/couchbase/sync_gateway/base"
goassert "github.com/couchbaselabs/go.assert"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TODO: Could consider checking this in as a file and include it into the compiled test binary using something like https://github.com/jteeuwen/go-bindata
Expand Down Expand Up @@ -290,3 +291,61 @@ func TestGetDeepMutableBody(t *testing.T) {
})
}
}

func TestInvalidXattrStreamDataLen(t *testing.T) {
testCases := []struct {
name string
body []byte
expectedErr error
}{
{
name: "bad value",
body: []byte("abcde"),
expectedErr: base.ErrXattrInvalidLen,
},
{
name: "xattr length 4, overflow",
body: []byte{0x00, 0x00, 0x00, 0x04, 0x01},
expectedErr: base.ErrXattrInvalidLen,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
// parseXattrStreamData is the underlying function
body, xattr, userXattr, err := parseXattrStreamData(base.SyncXattrName, "", test.body)
require.Error(t, err)
require.ErrorIs(t, err, test.expectedErr)
require.Nil(t, body)
require.Nil(t, xattr)
require.Nil(t, userXattr)
// UnmarshalDocumentSyncData wraps parseXattrStreamData
result, rawBody, rawXattr, rawUserXattr, err := UnmarshalDocumentSyncDataFromFeed(test.body, base.MemcachedDataTypeXattr, "", false)
require.ErrorIs(t, err, base.ErrXattrInvalidLen)
require.Nil(t, result)
require.Nil(t, rawBody)
require.Nil(t, rawXattr)
require.Nil(t, rawUserXattr)

})
}
}

func TestInvalidXattrStreamEmptyBody(t *testing.T) {
inputStream := []byte{0x00, 0x00, 0x00, 0x01, 0x01}
emptyBody := []byte{}
// parseXattrStreamData is the underlying function
body, xattr, userXattr, err := parseXattrStreamData(base.SyncXattrName, "", inputStream)
require.NoError(t, err)
require.Equal(t, emptyBody, body)
require.Nil(t, xattr)
require.Nil(t, userXattr)

// UnmarshalDocumentSyncData wraps parseXattrStreamData
result, rawBody, rawXattr, rawUserXattr, err := UnmarshalDocumentSyncDataFromFeed(inputStream, base.MemcachedDataTypeXattr, "", false)
require.Error(t, err) // unexpected end of JSON input
require.Nil(t, result)
require.Equal(t, emptyBody, rawBody)
require.Nil(t, rawXattr)
require.Nil(t, rawUserXattr)

}

0 comments on commit 1488a8b

Please sign in to comment.