Avoid uint32 to int cast in lisafs unmarshalling code.

On 32 bit systems, 0xffffffff will be cast to -1 which can trip the bound
checks in the unmarshalling code. A compromised sentry (client) will be able
to panic the gofer server.

PiperOrigin-RevId: 423221118
This commit is contained in:
Ayush Ranjan
2022-01-20 20:17:05 -08:00
committed by gVisor bot
parent 65a26689cb
commit 2a62f43796
4 changed files with 48 additions and 45 deletions
+2 -2
View File
@@ -344,11 +344,11 @@ type ControlFDImpl interface {
Symlink(c *Connection, name string, target string, uid UID, gid GID) (Inode, error)
Link(c *Connection, dir ControlFDImpl, name string) (Inode, error)
StatFS(c *Connection) (StatFS, error)
Readlink(c *Connection, getLinkBuf func(uint32) []byte) (uint32, error)
Readlink(c *Connection, getLinkBuf func(uint32) []byte) (uint16, error)
Connect(c *Connection, sockType uint32) (int, error)
Unlink(c *Connection, name string, flags uint32) error
RenameLocked(c *Connection, newDir ControlFDImpl, newName string) (func(ControlFDImpl), func(), error)
GetXattr(c *Connection, name string, dataBuf []byte) (uint32, error)
GetXattr(c *Connection, name string, dataBuf []byte) (uint16, error)
SetXattr(c *Connection, name string, value string, flags uint32) error
ListXattr(c *Connection, size uint64) (StringArray, error)
RemoveXattr(c *Connection, name string) error
+9 -9
View File
@@ -251,7 +251,7 @@ func WalkHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, e
// the slice allocation. The memory format should be WalkResp's.
var (
status WalkStatus
numInodes primitive.Uint32
numInodes primitive.Uint16
)
maxPayloadSize := status.SizeBytes() + numInodes.SizeBytes() + (len(req.Path) * (*Inode)(nil).SizeBytes())
if maxPayloadSize > math.MaxUint32 {
@@ -308,7 +308,7 @@ func WalkStatHandler(c *Connection, comm Communicator, payloadLen uint32) (uint3
// We will manually marshal the statx results into the payload buffer as they
// are generated to avoid the slice allocation. The memory format should be
// the same as WalkStatResp's.
var numStats primitive.Uint32
var numStats primitive.Uint16
maxPayloadSize := numStats.SizeBytes() + (len(req.Path) * linux.SizeOfStatx)
if maxPayloadSize > math.MaxUint32 {
// Too much to walk, can't do.
@@ -730,7 +730,7 @@ func ReadLinkAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uin
// We will manually marshal ReadLinkAtResp, which just contains a
// SizedString. Let Readlinkat directly write into the payload buffer and
// manually write the string size before it.
var linkLen primitive.Uint32
var linkLen primitive.Uint16
respMetaSize := uint32(linkLen.SizeBytes())
n, err := fd.impl.Readlink(c, func(dataLen uint32) []byte {
return comm.PayloadBuf(dataLen + respMetaSize)[respMetaSize:]
@@ -738,9 +738,9 @@ func ReadLinkAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uin
if err != nil {
return 0, err
}
linkLen = primitive.Uint32(n)
linkLen = primitive.Uint16(n)
linkLen.MarshalUnsafe(comm.PayloadBuf(respMetaSize))
return respMetaSize + n, nil
return respMetaSize + uint32(n), nil
}
// FlushHandler handles the Flush RPC.
@@ -923,7 +923,7 @@ func Getdents64Handler(c *Connection, comm Communicator, payloadLen uint32) (uin
// We will manually marshal the response Getdents64Resp.
// numDirents is the number of dirents marshalled into the payload.
var numDirents primitive.Uint32
var numDirents primitive.Uint16
// The payload starts with numDirents, dirents go right after that.
// payloadBufPos represents the position at which to write the next dirent.
payloadBufPos := uint32(numDirents.SizeBytes())
@@ -964,16 +964,16 @@ func FGetXattrHandler(c *Connection, comm Communicator, payloadLen uint32) (uint
// Manually marshal FGetXattrResp to avoid allocations and copying.
// FGetXattrResp simply is a wrapper around SizedString.
var valueLen primitive.Uint32
var valueLen primitive.Uint16
respMetaSize := uint32(valueLen.SizeBytes())
payloadBuf := comm.PayloadBuf(respMetaSize + uint32(req.BufSize))
n, err := fd.impl.GetXattr(c, string(req.Name), payloadBuf[respMetaSize:])
if err != nil {
return 0, err
}
valueLen = primitive.Uint32(n)
valueLen = primitive.Uint16(n)
valueLen.MarshalBytes(payloadBuf)
return respMetaSize + n, nil
return respMetaSize + uint32(n), nil
}
// FSetXattrHandler handles the FSetXattr RPC.
+30 -27
View File
@@ -181,17 +181,17 @@ func NoopMarshal(b []byte) []byte { return b }
func NoopUnmarshal(b []byte) ([]byte, bool) { return b, true }
// SizedString represents a string in memory. The marshalled string bytes are
// preceded by a uint32 signifying the string length.
// preceded by a uint16 signifying the string length.
type SizedString string
// SizeBytes implements marshal.Marshallable.SizeBytes.
func (s *SizedString) SizeBytes() int {
return (*primitive.Uint32)(nil).SizeBytes() + len(*s)
return (*primitive.Uint16)(nil).SizeBytes() + len(*s)
}
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
func (s *SizedString) MarshalBytes(dst []byte) []byte {
strLen := primitive.Uint32(len(*s))
strLen := primitive.Uint16(len(*s))
dst = strLen.MarshalUnsafe(dst)
// Copy without any allocation.
return dst[copy(dst[:strLen], *s):]
@@ -199,7 +199,7 @@ func (s *SizedString) MarshalBytes(dst []byte) []byte {
// CheckedUnmarshal implements marshal.CheckedMarshallable.CheckedUnmarshal.
func (s *SizedString) CheckedUnmarshal(src []byte) ([]byte, bool) {
var strLen primitive.Uint32
var strLen primitive.Uint16
srcRemain, ok := strLen.CheckedUnmarshal(src)
if !ok || len(srcRemain) < int(strLen) {
return src, false
@@ -210,12 +210,12 @@ func (s *SizedString) CheckedUnmarshal(src []byte) ([]byte, bool) {
}
// StringArray represents an array of SizedStrings in memory. The marshalled
// array data is preceded by a uint32 signifying the array length.
// array data is preceded by a uint16 signifying the array length.
type StringArray []string
// SizeBytes implements marshal.Marshallable.SizeBytes.
func (s *StringArray) SizeBytes() int {
size := (*primitive.Uint32)(nil).SizeBytes()
size := (*primitive.Uint16)(nil).SizeBytes()
for _, str := range *s {
sstr := SizedString(str)
size += sstr.SizeBytes()
@@ -225,7 +225,7 @@ func (s *StringArray) SizeBytes() int {
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
func (s *StringArray) MarshalBytes(dst []byte) []byte {
arrLen := primitive.Uint32(len(*s))
arrLen := primitive.Uint16(len(*s))
dst = arrLen.MarshalUnsafe(dst)
for _, str := range *s {
sstr := SizedString(str)
@@ -236,7 +236,7 @@ func (s *StringArray) MarshalBytes(dst []byte) []byte {
// CheckedUnmarshal implements marshal.CheckedMarshallable.CheckedUnmarshal.
func (s *StringArray) CheckedUnmarshal(src []byte) ([]byte, bool) {
var arrLen primitive.Uint32
var arrLen primitive.Uint16
srcRemain, ok := arrLen.CheckedUnmarshal(src)
if !ok {
return src, false
@@ -248,7 +248,7 @@ func (s *StringArray) CheckedUnmarshal(src []byte) ([]byte, bool) {
*s = (*s)[:arrLen]
}
for i := primitive.Uint32(0); i < arrLen; i++ {
for i := primitive.Uint16(0); i < arrLen; i++ {
var sstr SizedString
srcRemain, ok = sstr.CheckedUnmarshal(srcRemain)
if !ok {
@@ -436,7 +436,8 @@ const (
WalkComponentSymlink
)
// WalkResp is used to communicate the inodes walked by the server.
// WalkResp is used to communicate the inodes walked by the server. In memory,
// the inode array is preceded by a uint16 integer denoting array length.
type WalkResp struct {
Status WalkStatus
Inodes []Inode
@@ -445,14 +446,14 @@ type WalkResp struct {
// SizeBytes implements marshal.Marshallable.SizeBytes.
func (w *WalkResp) SizeBytes() int {
return w.Status.SizeBytes() +
(*primitive.Uint32)(nil).SizeBytes() + (len(w.Inodes) * (*Inode)(nil).SizeBytes())
(*primitive.Uint16)(nil).SizeBytes() + (len(w.Inodes) * (*Inode)(nil).SizeBytes())
}
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
func (w *WalkResp) MarshalBytes(dst []byte) []byte {
dst = w.Status.MarshalUnsafe(dst)
numInodes := primitive.Uint32(len(w.Inodes))
numInodes := primitive.Uint16(len(w.Inodes))
dst = numInodes.MarshalUnsafe(dst)
return MarshalUnsafeInodeSlice(w.Inodes, dst)
@@ -466,7 +467,7 @@ func (w *WalkResp) CheckedUnmarshal(src []byte) ([]byte, bool) {
}
srcRemain := w.Status.UnmarshalUnsafe(src)
var numInodes primitive.Uint32
var numInodes primitive.Uint16
srcRemain = numInodes.UnmarshalUnsafe(srcRemain)
if int(numInodes)*(*Inode)(nil).SizeBytes() > len(srcRemain) {
return src, false
@@ -479,19 +480,20 @@ func (w *WalkResp) CheckedUnmarshal(src []byte) ([]byte, bool) {
return UnmarshalUnsafeInodeSlice(w.Inodes, srcRemain), true
}
// WalkStatResp is used to communicate stat results for WalkStat.
// WalkStatResp is used to communicate stat results for WalkStat. In memory,
// the array data is preceded by a uint16 denoting the array length.
type WalkStatResp struct {
Stats []linux.Statx
}
// SizeBytes implements marshal.Marshallable.SizeBytes.
func (w *WalkStatResp) SizeBytes() int {
return (*primitive.Uint32)(nil).SizeBytes() + (len(w.Stats) * linux.SizeOfStatx)
return (*primitive.Uint16)(nil).SizeBytes() + (len(w.Stats) * linux.SizeOfStatx)
}
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
func (w *WalkStatResp) MarshalBytes(dst []byte) []byte {
numStats := primitive.Uint32(len(w.Stats))
numStats := primitive.Uint16(len(w.Stats))
dst = numStats.MarshalUnsafe(dst)
return linux.MarshalUnsafeStatxSlice(w.Stats, dst)
@@ -503,7 +505,7 @@ func (w *WalkStatResp) CheckedUnmarshal(src []byte) ([]byte, bool) {
if w.SizeBytes() > len(src) {
return src, false
}
var numStats primitive.Uint32
var numStats primitive.Uint16
srcRemain := numStats.UnmarshalUnsafe(src)
if int(numStats)*linux.SizeOfStatx > len(srcRemain) {
@@ -585,17 +587,17 @@ type OpenCreateAtResp struct {
// FdArray is a utility struct which implements a marshallable type for
// communicating an array of FDIDs. In memory, the array data is preceded by a
// uint32 denoting the array length.
// uint16 denoting the array length.
type FdArray []FDID
// SizeBytes implements marshal.Marshallable.SizeBytes.
func (f *FdArray) SizeBytes() int {
return (*primitive.Uint32)(nil).SizeBytes() + (len(*f) * (*FDID)(nil).SizeBytes())
return (*primitive.Uint16)(nil).SizeBytes() + (len(*f) * (*FDID)(nil).SizeBytes())
}
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
func (f *FdArray) MarshalBytes(dst []byte) []byte {
arrLen := primitive.Uint32(len(*f))
arrLen := primitive.Uint16(len(*f))
dst = arrLen.MarshalUnsafe(dst)
return MarshalUnsafeFDIDSlice(*f, dst)
}
@@ -606,7 +608,7 @@ func (f *FdArray) CheckedUnmarshal(src []byte) ([]byte, bool) {
if f.SizeBytes() > len(src) {
return src, false
}
var arrLen primitive.Uint32
var arrLen primitive.Uint16
srcRemain := arrLen.UnmarshalUnsafe(src)
if int(arrLen)*(*FDID)(nil).SizeBytes() > len(srcRemain) {
return src, false
@@ -688,7 +690,7 @@ func (r *PReadResp) MarshalBytes(dst []byte) []byte {
// CheckedUnmarshal implements marshal.CheckedMarshallable.CheckedUnmarshal.
func (r *PReadResp) CheckedUnmarshal(src []byte) ([]byte, bool) {
srcRemain, ok := r.NumBytes.CheckedUnmarshal(src)
if !ok || int(r.NumBytes) > len(srcRemain) || int(r.NumBytes) > len(r.Buf) {
if !ok || uint32(r.NumBytes) > uint32(len(srcRemain)) || uint32(r.NumBytes) > uint32(len(r.Buf)) {
return src, false
}
@@ -730,7 +732,7 @@ func (w *PWriteReq) CheckedUnmarshal(src []byte) ([]byte, bool) {
// This is an optimization. Assuming that the server is making this call, it
// is safe to just point to src rather than allocating and copying.
if int(w.NumBytes) > len(srcRemain) {
if uint32(w.NumBytes) > uint32(len(srcRemain)) {
return src, false
}
w.Buf = srcRemain[:w.NumBytes]
@@ -1112,14 +1114,15 @@ func (d *Dirent64) CheckedUnmarshal(src []byte) ([]byte, bool) {
return src, false
}
// Getdents64Resp is used to communicate getdents64 results.
// Getdents64Resp is used to communicate getdents64 results. In memory, the
// dirents array is preceded by a uint16 integer denoting array length.
type Getdents64Resp struct {
Dirents []Dirent64
}
// SizeBytes implements marshal.Marshallable.SizeBytes.
func (g *Getdents64Resp) SizeBytes() int {
ret := (*primitive.Uint32)(nil).SizeBytes()
ret := (*primitive.Uint16)(nil).SizeBytes()
for i := range g.Dirents {
ret += g.Dirents[i].SizeBytes()
}
@@ -1128,7 +1131,7 @@ func (g *Getdents64Resp) SizeBytes() int {
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
func (g *Getdents64Resp) MarshalBytes(dst []byte) []byte {
numDirents := primitive.Uint32(len(g.Dirents))
numDirents := primitive.Uint16(len(g.Dirents))
dst = numDirents.MarshalUnsafe(dst)
for i := range g.Dirents {
dst = g.Dirents[i].MarshalBytes(dst)
@@ -1142,7 +1145,7 @@ func (g *Getdents64Resp) CheckedUnmarshal(src []byte) ([]byte, bool) {
if g.SizeBytes() > len(src) {
return src, false
}
var numDirents primitive.Uint32
var numDirents primitive.Uint16
srcRemain := numDirents.UnmarshalUnsafe(src)
if cap(g.Dirents) < int(numDirents) {
g.Dirents = make([]Dirent64, numDirents)
+7 -7
View File
@@ -16,6 +16,7 @@ package fsgofer
import (
"io"
"math"
"path"
"strconv"
"sync/atomic"
@@ -622,17 +623,16 @@ func (fd *controlFDLisa) StatFS(c *lisafs.Connection) (lisafs.StatFS, error) {
}
// Readlink implements lisafs.ControlFDImpl.Readlink.
func (fd *controlFDLisa) Readlink(c *lisafs.Connection, getLinkBuf func(uint32) []byte) (uint32, error) {
func (fd *controlFDLisa) Readlink(c *lisafs.Connection, getLinkBuf func(uint32) []byte) (uint16, error) {
// This is similar to what os.Readlink does.
const limit = uint32(1024 * 1024)
for linkLen := uint32(128); linkLen < limit; linkLen *= 2 {
b := getLinkBuf(linkLen)
for linkLen := 128; linkLen < math.MaxUint16; linkLen *= 2 {
b := getLinkBuf(uint32(linkLen))
n, err := unix.Readlinkat(fd.hostFD, "", b)
if err != nil {
return 0, err
}
if n < int(linkLen) {
return uint32(n), nil
return uint16(n), nil
}
}
return 0, unix.ENOMEM
@@ -693,7 +693,7 @@ func (fd *controlFDLisa) RenameLocked(c *lisafs.Connection, newDir lisafs.Contro
}
// GetXattr implements lisafs.ControlFDImpl.GetXattr.
func (fd *controlFDLisa) GetXattr(c *lisafs.Connection, name string, dataBuf []byte) (uint32, error) {
func (fd *controlFDLisa) GetXattr(c *lisafs.Connection, name string, dataBuf []byte) (uint16, error) {
if !c.ServerImpl().(*LisafsServer).config.EnableVerityXattr {
return 0, unix.EOPNOTSUPP
}
@@ -701,7 +701,7 @@ func (fd *controlFDLisa) GetXattr(c *lisafs.Connection, name string, dataBuf []b
return 0, unix.EOPNOTSUPP
}
n, err := unix.Fgetxattr(fd.hostFD, name, dataBuf)
return uint32(n), err
return uint16(n), err
}
// SetXattr implements lisafs.ControlFDImpl.SetXattr.