diff options
author | Ridwan Sharif <ridwanmsharif@google.com> | 2020-08-19 20:11:59 -0400 |
---|---|---|
committer | Andrei Vagin <avagin@gmail.com> | 2020-09-16 12:19:30 -0700 |
commit | d51ddcefdc46a1007f7345bfa2f7006bb820b157 (patch) | |
tree | 6fab64e6cc39a057d4498e5a5ed465097f58ab5c /pkg | |
parent | 4a5857d644ae0e62090bbbed86852dceca79395c (diff) |
fuse: use safe go_marshal API for FUSE
Until #3698 is resolved, this change is needed to ensure we're not
corrupting memory anywhere.
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/abi/linux/fuse.go | 97 | ||||
-rw-r--r-- | pkg/marshal/marshal_impl_util.go | 2 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/fuse/connection.go | 9 |
3 files changed, 103 insertions, 5 deletions
diff --git a/pkg/abi/linux/fuse.go b/pkg/abi/linux/fuse.go index e7b5f45de..d105c5176 100644 --- a/pkg/abi/linux/fuse.go +++ b/pkg/abi/linux/fuse.go @@ -359,7 +359,12 @@ type FUSELookupIn struct { // MarshalUnsafe serializes r.name to the dst buffer. func (r *FUSELookupIn) MarshalUnsafe(buf []byte) { - copy(buf, []byte(r.Name)) + copy(buf, r.Name) +} + +// MarshalBytes serializes r.name to the dst buffer. +func (r *FUSELookupIn) MarshalBytes(buf []byte) { + copy(buf, r.Name) } // SizeBytes is the size of the memory representation of FUSELookupIn. @@ -491,6 +496,12 @@ func (r *FUSEMknodIn) MarshalUnsafe(buf []byte) { copy(buf[r.MknodMeta.SizeBytes():], r.Name) } +// MarshalBytes serializes r.MknodMeta and r.Name to the dst buffer. +func (r *FUSEMknodIn) MarshalBytes(buf []byte) { + r.MknodMeta.MarshalBytes(buf[:r.MknodMeta.SizeBytes()]) + copy(buf[r.MknodMeta.SizeBytes():], r.Name) +} + // SizeBytes is the size of the memory representation of FUSEMknodIn. // 1 extra byte for null-terminated string. func (r *FUSEMknodIn) SizeBytes() int { @@ -518,6 +529,13 @@ func (r *FUSESymLinkIn) MarshalUnsafe(buf []byte) { copy(buf[len(r.Name)+1:], r.Target) } +// MarshalBytes serializes r.Name and r.Target to the dst buffer. +// Left null-termination at end of r.Name and r.Target. +func (r *FUSESymLinkIn) MarshalBytes(buf []byte) { + copy(buf, r.Name) + copy(buf[len(r.Name)+1:], r.Target) +} + // SizeBytes is the size of the memory representation of FUSESymLinkIn. // 2 extra bytes for null-terminated string. func (r *FUSESymLinkIn) SizeBytes() int { @@ -530,6 +548,9 @@ type FUSEEmptyIn struct{ marshal.StubMarshallable } // MarshalUnsafe do nothing for marshal. func (r *FUSEEmptyIn) MarshalUnsafe(buf []byte) {} +// MarshalBytes do nothing for marshal. +func (r *FUSEEmptyIn) MarshalBytes(buf []byte) {} + // SizeBytes is 0 for empty request. func (r *FUSEEmptyIn) SizeBytes() int { return 0 @@ -567,6 +588,12 @@ func (r *FUSEMkdirIn) MarshalUnsafe(buf []byte) { copy(buf[r.MkdirMeta.SizeBytes():], r.Name) } +// MarshalBytes serializes r.MkdirMeta and r.Name to the dst buffer. +func (r *FUSEMkdirIn) MarshalBytes(buf []byte) { + r.MkdirMeta.MarshalBytes(buf[:r.MkdirMeta.SizeBytes()]) + copy(buf[r.MkdirMeta.SizeBytes():], r.Name) +} + // SizeBytes is the size of the memory representation of FUSEMkdirIn. // 1 extra byte for null-terminated Name string. func (r *FUSEMkdirIn) SizeBytes() int { @@ -589,6 +616,11 @@ func (r *FUSERmDirIn) MarshalUnsafe(buf []byte) { copy(buf, r.Name) } +// MarshalBytes serializes r.name to the dst buffer. +func (r *FUSERmDirIn) MarshalBytes(buf []byte) { + copy(buf, r.Name) +} + // SizeBytes is the size of the memory representation of FUSERmDirIn. func (r *FUSERmDirIn) SizeBytes() int { return len(r.Name) + 1 @@ -599,6 +631,11 @@ func (r *FUSERmDirIn) UnmarshalUnsafe(src []byte) { r.Name = string(src) } +// UnmarshalBytes deserializes r.name from the src buffer. +func (r *FUSERmDirIn) UnmarshalBytes(src []byte) { + r.Name = string(src) +} + // FUSEDirents is a list of Dirents received from the FUSE daemon server. // It is used for FUSE_READDIR. // @@ -649,6 +686,14 @@ func (r *FUSEDirents) MarshalUnsafe(dst []byte) { } } +// MarshalBytes serializes FUSEDirents to the dst buffer. +func (r *FUSEDirents) MarshalBytes(dst []byte) { + for _, dirent := range r.Dirents { + dirent.MarshalBytes(dst) + dst = dst[dirent.SizeBytes():] + } +} + // SizeBytes is the size of the memory representation of FUSEDirents. func (r *FUSEDirents) SizeBytes() int { var sizeBytes int @@ -683,6 +728,30 @@ func (r *FUSEDirents) UnmarshalUnsafe(src []byte) { } } +// UnmarshalBytes deserializes FUSEDirents from the src buffer. +func (r *FUSEDirents) UnmarshalBytes(src []byte) { + for { + if len(src) <= (*FUSEDirentMeta)(nil).SizeBytes() { + break + } + + // Its unclear how many dirents there are in src. Each dirent is dynamically + // sized and so we can't make assumptions about how many dirents we can allocate. + if r.Dirents == nil { + r.Dirents = make([]*FUSEDirent, 0) + } + + // We have to allocate a struct for each dirent - there must be a better way + // to do this. Linux allocates 1 page to store all the dirents and then + // simply reads them from the page. + var dirent FUSEDirent + dirent.UnmarshalBytes(src) + r.Dirents = append(r.Dirents, &dirent) + + src = src[dirent.SizeBytes():] + } +} + // MarshalUnsafe serializes FUSEDirent to the dst buffer. func (r *FUSEDirent) MarshalUnsafe(dst []byte) { r.Meta.MarshalUnsafe(dst) @@ -692,6 +761,15 @@ func (r *FUSEDirent) MarshalUnsafe(dst []byte) { name.MarshalUnsafe(dst) } +// MarshalBytes serializes FUSEDirent to the dst buffer. +func (r *FUSEDirent) MarshalBytes(dst []byte) { + r.Meta.MarshalBytes(dst) + dst = dst[r.Meta.SizeBytes():] + + name := primitive.ByteSlice(r.Name) + name.MarshalBytes(dst) +} + // SizeBytes is the size of the memory representation of FUSEDirent. func (r *FUSEDirent) SizeBytes() int { dataSize := r.Meta.SizeBytes() + len(r.Name) @@ -718,3 +796,20 @@ func (r *FUSEDirent) UnmarshalUnsafe(src []byte) { name.UnmarshalUnsafe(src[:r.Meta.NameLen]) r.Name = string(name) } + +// UnmarshalBytes deserializes FUSEDirent from the src buffer. +func (r *FUSEDirent) UnmarshalBytes(src []byte) { + r.Meta.UnmarshalBytes(src) + src = src[r.Meta.SizeBytes():] + + if r.Meta.NameLen > FUSE_NAME_MAX { + // The name is too long and therefore invalid. We don't + // need to unmarshal the name since it'll be thrown away. + return + } + + buf := make([]byte, r.Meta.NameLen) + name := primitive.ByteSlice(buf) + name.UnmarshalBytes(src[:r.Meta.NameLen]) + r.Name = string(name) +} diff --git a/pkg/marshal/marshal_impl_util.go b/pkg/marshal/marshal_impl_util.go index 69f33321e..ea75e09f2 100644 --- a/pkg/marshal/marshal_impl_util.go +++ b/pkg/marshal/marshal_impl_util.go @@ -44,7 +44,7 @@ func (StubMarshallable) MarshalBytes(dst []byte) { // UnmarshalBytes implements Marshallable.UnmarshalBytes. func (StubMarshallable) UnmarshalBytes(src []byte) { - panic("Please implement your own UnMarshalBytes function") + panic("Please implement your own UnmarshalBytes function") } // Packed implements Marshallable.Packed. diff --git a/pkg/sentry/fsimpl/fuse/connection.go b/pkg/sentry/fsimpl/fuse/connection.go index 133306158..0e70d37ec 100644 --- a/pkg/sentry/fsimpl/fuse/connection.go +++ b/pkg/sentry/fsimpl/fuse/connection.go @@ -271,8 +271,10 @@ func (conn *connection) NewRequest(creds *auth.Credentials, pid uint32, ino uint } buf := make([]byte, hdr.Len) - hdr.MarshalUnsafe(buf[:hdrLen]) - payload.MarshalUnsafe(buf[hdrLen:]) + + // TODO(gVisor.dev/3698): Use the unsafe version once go_marshal is safe to use again. + hdr.MarshalBytes(buf[:hdrLen]) + payload.MarshalBytes(buf[hdrLen:]) return &Request{ id: hdr.Unique, @@ -360,7 +362,8 @@ func (r *Response) UnmarshalPayload(m marshal.Marshallable) error { return nil } - m.UnmarshalUnsafe(r.data[hdrLen:]) + // TODO(gVisor.dev/3698): Use the unsafe version once go_marshal is safe to use again. + m.UnmarshalBytes(r.data[hdrLen:]) return nil } |