mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add Checked methods to go_marshal.
This is as per proposal in #6450. I have gated this behind a tag because this is a very sparsely used feature and otherwise will leads to a lot of unused generated code. Secondly, we can not generate the CheckUnmarshal method for dynamic types. So the dynamic tag would now require its users to additionally implement CheckUnmarshal method which is more cumbersome. Fixes #6450 PiperOrigin-RevId: 411197734
This commit is contained in:
@@ -242,7 +242,7 @@ const (
|
||||
|
||||
// Statx represents struct statx.
|
||||
//
|
||||
// +marshal slice:StatxSlice
|
||||
// +marshal boundCheck slice:StatxSlice
|
||||
type Statx struct {
|
||||
Mask uint32
|
||||
Blksize uint32
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Replacing 9P
|
||||
|
||||
NOTE: LISAFS is **NOT** production ready. There are still some security concerns
|
||||
that must be resolved first.
|
||||
|
||||
## Background
|
||||
|
||||
The Linux filesystem model consists of the following key aspects (modulo mounts,
|
||||
|
||||
+13
-5
@@ -101,7 +101,7 @@ func NewClient(sock *unet.Socket, mountPath string) (*Client, *Inode, error) {
|
||||
MountPath: SizedString(mountPath),
|
||||
}
|
||||
var mountResp MountResp
|
||||
if err := c.SndRcvMessage(Mount, uint32(mountMsg.SizeBytes()), mountMsg.MarshalBytes, mountResp.UnmarshalBytes, nil); err != nil {
|
||||
if err := c.SndRcvMessage(Mount, uint32(mountMsg.SizeBytes()), mountMsg.MarshalBytes, mountResp.CheckedUnmarshal, nil); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ func (c *Client) Close() {
|
||||
func (c *Client) createChannel() (*channel, error) {
|
||||
var chanResp ChannelResp
|
||||
var fds [2]int
|
||||
if err := c.SndRcvMessage(Channel, 0, NoopMarshal, chanResp.UnmarshalUnsafe, fds[:]); err != nil {
|
||||
if err := c.SndRcvMessage(Channel, 0, NoopMarshal, chanResp.CheckedUnmarshal, fds[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fds[0] < 0 || fds[1] < 0 {
|
||||
@@ -313,12 +313,12 @@ func (c *Client) SyncFDs(ctx context.Context, fds []FDID) error {
|
||||
// implicit conversion to an interface leads to an allocation.
|
||||
//
|
||||
// Precondition: reqMarshal and respUnmarshal must be non-nil.
|
||||
func (c *Client) SndRcvMessage(m MID, payloadLen uint32, reqMarshal func(dst []byte) []byte, respUnmarshal func(src []byte) []byte, respFDs []int) error {
|
||||
func (c *Client) SndRcvMessage(m MID, payloadLen uint32, reqMarshal func(dst []byte) []byte, respUnmarshal func(src []byte) ([]byte, bool), respFDs []int) error {
|
||||
if !c.IsSupported(m) {
|
||||
return unix.EOPNOTSUPP
|
||||
}
|
||||
if payloadLen > c.maxMessageSize {
|
||||
log.Warningf("message %d has message size = %d which is larger than client.maxMessageSize = %d", m, payloadLen, c.maxMessageSize)
|
||||
log.Warningf("message %d has payload which is too large: %d bytes", m, payloadLen)
|
||||
return unix.EIO
|
||||
}
|
||||
wantFDs := len(respFDs)
|
||||
@@ -357,6 +357,11 @@ func (c *Client) SndRcvMessage(m MID, payloadLen uint32, reqMarshal func(dst []b
|
||||
closeFDs(respFDs)
|
||||
return err
|
||||
}
|
||||
if respPayloadLen > c.maxMessageSize {
|
||||
log.Warningf("server response for message %d is too large: %d bytes", respM, respPayloadLen)
|
||||
closeFDs(respFDs)
|
||||
return unix.EIO
|
||||
}
|
||||
if respM == Error {
|
||||
closeFDs(respFDs)
|
||||
var resp ErrorResp
|
||||
@@ -370,7 +375,10 @@ func (c *Client) SndRcvMessage(m MID, payloadLen uint32, reqMarshal func(dst []b
|
||||
}
|
||||
|
||||
// Success. The payload must be unmarshalled *before* comm is released.
|
||||
respUnmarshal(comm.PayloadBuf(respPayloadLen))
|
||||
if _, ok := respUnmarshal(comm.PayloadBuf(respPayloadLen)); !ok {
|
||||
log.Warningf("server response unmarshalling for %d message failed", respM)
|
||||
return unix.EIO
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+19
-19
@@ -82,7 +82,7 @@ func (f *ClientFD) OpenAt(ctx context.Context, flags uint32) (FDID, int, error)
|
||||
var respFD [1]int
|
||||
var resp OpenAtResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(OpenAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.UnmarshalUnsafe, respFD[:])
|
||||
err := f.client.SndRcvMessage(OpenAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, respFD[:])
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return resp.NewFD, respFD[0], err
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func (f *ClientFD) OpenCreateAt(ctx context.Context, name string, flags uint32,
|
||||
var respFD [1]int
|
||||
var resp OpenCreateAtResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(OpenCreateAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalUnsafe, respFD[:])
|
||||
err := f.client.SndRcvMessage(OpenCreateAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, respFD[:])
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return resp.Child, resp.NewFD, respFD[0], err
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func (f *ClientFD) OpenCreateAt(ctx context.Context, name string, flags uint32,
|
||||
func (f *ClientFD) StatTo(ctx context.Context, stat *linux.Statx) error {
|
||||
req := StatReq{FD: f.fd}
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(FStat, uint32(req.SizeBytes()), req.MarshalUnsafe, stat.UnmarshalUnsafe, nil)
|
||||
err := f.client.SndRcvMessage(FStat, uint32(req.SizeBytes()), req.MarshalUnsafe, stat.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
@@ -178,10 +178,10 @@ func (f *ClientFD) Read(ctx context.Context, dst []byte, offset uint64) (uint64,
|
||||
|
||||
// This will be unmarshalled into. Already set Buf so that we don't need to
|
||||
// allocate a temporary buffer during unmarshalling.
|
||||
// PReadResp.UnmarshalBytes expects this to be set.
|
||||
// PReadResp.CheckedUnmarshal expects this to be set.
|
||||
resp.Buf = buf
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(PRead, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.UnmarshalBytes, nil)
|
||||
err := f.client.SndRcvMessage(PRead, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return uint64(resp.NumBytes), err
|
||||
})
|
||||
@@ -205,7 +205,7 @@ func (f *ClientFD) Write(ctx context.Context, src []byte, offset uint64) (uint64
|
||||
|
||||
var resp PWriteResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(PWrite, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalUnsafe, nil)
|
||||
err := f.client.SndRcvMessage(PWrite, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return resp.Count, err
|
||||
})
|
||||
@@ -222,7 +222,7 @@ func (f *ClientFD) MkdirAt(ctx context.Context, name string, mode linux.FileMode
|
||||
|
||||
var resp MkdirAtResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(MkdirAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalUnsafe, nil)
|
||||
err := f.client.SndRcvMessage(MkdirAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return &resp.ChildDir, err
|
||||
}
|
||||
@@ -239,7 +239,7 @@ func (f *ClientFD) SymlinkAt(ctx context.Context, name, target string, uid UID,
|
||||
|
||||
var resp SymlinkAtResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(SymlinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalUnsafe, nil)
|
||||
err := f.client.SndRcvMessage(SymlinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return &resp.Symlink, err
|
||||
}
|
||||
@@ -254,7 +254,7 @@ func (f *ClientFD) LinkAt(ctx context.Context, targetFD FDID, name string) (*Ino
|
||||
|
||||
var resp LinkAtResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(LinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalUnsafe, nil)
|
||||
err := f.client.SndRcvMessage(LinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return &resp.Link, err
|
||||
}
|
||||
@@ -272,7 +272,7 @@ func (f *ClientFD) MknodAt(ctx context.Context, name string, mode linux.FileMode
|
||||
|
||||
var resp MknodAtResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(MknodAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalUnsafe, nil)
|
||||
err := f.client.SndRcvMessage(MknodAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return &resp.Child, err
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func (f *ClientFD) SetStat(ctx context.Context, stat *linux.Statx) (uint32, erro
|
||||
|
||||
var resp SetStatResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(SetStat, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.UnmarshalUnsafe, nil)
|
||||
err := f.client.SndRcvMessage(SetStat, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return resp.FailureMask, unix.Errno(resp.FailureErrNo), err
|
||||
}
|
||||
@@ -312,7 +312,7 @@ func (f *ClientFD) WalkMultiple(ctx context.Context, names []string) (WalkStatus
|
||||
|
||||
var resp WalkResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalBytes, nil)
|
||||
err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return resp.Status, resp.Inodes, err
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func (f *ClientFD) Walk(ctx context.Context, name string) (*Inode, error) {
|
||||
var inode [1]Inode
|
||||
resp := WalkResp{Inodes: inode[:]}
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalBytes, nil)
|
||||
err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -363,7 +363,7 @@ func (f *ClientFD) WalkStat(ctx context.Context, names []string) ([]linux.Statx,
|
||||
|
||||
var resp WalkStatResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(WalkStat, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalBytes, nil)
|
||||
err := f.client.SndRcvMessage(WalkStat, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return resp.Stats, err
|
||||
}
|
||||
@@ -372,7 +372,7 @@ func (f *ClientFD) WalkStat(ctx context.Context, names []string) ([]linux.Statx,
|
||||
func (f *ClientFD) StatFSTo(ctx context.Context, statFS *StatFS) error {
|
||||
req := FStatFSReq{FD: f.fd}
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(FStatFS, uint32(req.SizeBytes()), req.MarshalUnsafe, statFS.UnmarshalUnsafe, nil)
|
||||
err := f.client.SndRcvMessage(FStatFS, uint32(req.SizeBytes()), req.MarshalUnsafe, statFS.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
@@ -396,7 +396,7 @@ func (f *ClientFD) ReadLinkAt(ctx context.Context) (string, error) {
|
||||
req := ReadLinkAtReq{FD: f.fd}
|
||||
var resp ReadLinkAtResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(ReadLinkAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.UnmarshalBytes, nil)
|
||||
err := f.client.SndRcvMessage(ReadLinkAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return string(resp.Target), err
|
||||
}
|
||||
@@ -465,7 +465,7 @@ func (f *ClientFD) Getdents64(ctx context.Context, count int32) ([]Dirent64, err
|
||||
|
||||
var resp Getdents64Resp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(Getdents64, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.UnmarshalBytes, nil)
|
||||
err := f.client.SndRcvMessage(Getdents64, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return resp.Dirents, err
|
||||
}
|
||||
@@ -479,7 +479,7 @@ func (f *ClientFD) ListXattr(ctx context.Context, size uint64) ([]string, error)
|
||||
|
||||
var resp FListXattrResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(FListXattr, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.UnmarshalBytes, nil)
|
||||
err := f.client.SndRcvMessage(FListXattr, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return resp.Xattrs, err
|
||||
}
|
||||
@@ -494,7 +494,7 @@ func (f *ClientFD) GetXattr(ctx context.Context, name string, size uint64) (stri
|
||||
|
||||
var resp FGetXattrResp
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.client.SndRcvMessage(FGetXattr, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalBytes, nil)
|
||||
err := f.client.SndRcvMessage(FGetXattr, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return string(resp.Value), err
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ type Connection struct {
|
||||
// associated with it for its entire lifetime.
|
||||
server *Server
|
||||
|
||||
// maxMessageSize is the cached value of server.impl.MaxMessageSize().
|
||||
maxMessageSize uint32
|
||||
|
||||
// mounted is a one way flag indicating whether this connection has been
|
||||
// mounted correctly and the server is initialized properly.
|
||||
mounted bool
|
||||
@@ -72,12 +75,13 @@ type Connection struct {
|
||||
// required. The connection must be started separately.
|
||||
func (s *Server) CreateConnection(sock *unet.Socket, readonly bool) (*Connection, error) {
|
||||
c := &Connection{
|
||||
sockComm: newSockComm(sock),
|
||||
server: s,
|
||||
readonly: readonly,
|
||||
channels: make([]*channel, 0, maxChannels()),
|
||||
fds: make(map[FDID]genericFD),
|
||||
nextFDID: InvalidFDID + 1,
|
||||
sockComm: newSockComm(sock),
|
||||
server: s,
|
||||
maxMessageSize: s.impl.MaxMessageSize(),
|
||||
readonly: readonly,
|
||||
channels: make([]*channel, 0, maxChannels()),
|
||||
fds: make(map[FDID]genericFD),
|
||||
nextFDID: InvalidFDID + 1,
|
||||
}
|
||||
|
||||
alloc, err := flipcall.NewPacketWindowAllocator()
|
||||
@@ -153,6 +157,10 @@ func (c *Connection) respondError(comm Communicator, err unix.Errno) (MID, uint3
|
||||
}
|
||||
|
||||
func (c *Connection) handleMsg(comm Communicator, m MID, payloadLen uint32) (MID, uint32, []int) {
|
||||
if payloadLen > c.maxMessageSize {
|
||||
log.Warningf("received payload is too large: %d bytes", payloadLen)
|
||||
return c.respondError(comm, unix.EIO)
|
||||
}
|
||||
if !c.reqGate.Enter() {
|
||||
// c.close() has been called; the connection is shutting down.
|
||||
return c.respondError(comm, unix.ECONNRESET)
|
||||
@@ -177,6 +185,11 @@ func (c *Connection) handleMsg(comm Communicator, m MID, payloadLen uint32) (MID
|
||||
closeFDs(fds)
|
||||
return c.respondError(comm, p9.ExtractErrno(err))
|
||||
}
|
||||
if respPayloadLen > c.maxMessageSize {
|
||||
log.Warningf("handler for message %d responded with payload which is too large: %d bytes", m, respPayloadLen)
|
||||
closeFDs(fds)
|
||||
return c.respondError(comm, unix.EIO)
|
||||
}
|
||||
|
||||
return m, respPayloadLen, fds
|
||||
}
|
||||
|
||||
@@ -119,7 +119,9 @@ func TestUnsupportedMessage(t *testing.T) {
|
||||
|
||||
func dynamicMsgHandler(c *lisafs.Connection, comm lisafs.Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req lisafs.MsgDynamic
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
// Just echo back the message.
|
||||
respPayloadLen := uint32(req.SizeBytes())
|
||||
@@ -144,7 +146,7 @@ func TestStress(t *testing.T) {
|
||||
req.Randomize(100)
|
||||
|
||||
var resp lisafs.MsgDynamic
|
||||
if err := c.SndRcvMessage(dynamicMsgID, uint32(req.SizeBytes()), req.MarshalBytes, resp.UnmarshalBytes, nil); err != nil {
|
||||
if err := c.SndRcvMessage(dynamicMsgID, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil); err != nil {
|
||||
t.Errorf("SndRcvMessage: received unexpected error %v", err)
|
||||
return
|
||||
}
|
||||
@@ -163,7 +165,9 @@ func versionHandler(c *lisafs.Connection, comm lisafs.Communicator, payloadLen u
|
||||
// To be fair, usually handlers will create their own objects and return a
|
||||
// pointer to those. Might be tempting to reuse above variables, but don't.
|
||||
var rv lisafs.P9Version
|
||||
rv.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := rv.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
// Create a new response.
|
||||
sv := lisafs.P9Version{
|
||||
@@ -186,7 +190,7 @@ func BenchmarkSendRecv(b *testing.B) {
|
||||
var recvV lisafs.P9Version
|
||||
runServerClient(b, func(c *lisafs.Client) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := c.SndRcvMessage(versionMsgID, uint32(sendV.SizeBytes()), sendV.MarshalBytes, recvV.UnmarshalBytes, nil); err != nil {
|
||||
if err := c.SndRcvMessage(versionMsgID, uint32(sendV.SizeBytes()), sendV.MarshalBytes, recvV.CheckedUnmarshal, nil); err != nil {
|
||||
b.Fatalf("unexpected error occurred: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import (
|
||||
// FDID (file descriptor identifier) is used to identify FDs on a connection.
|
||||
// Each connection has its own FDID namespace.
|
||||
//
|
||||
// +marshal slice:FDIDSlice
|
||||
// +marshal boundCheck slice:FDIDSlice
|
||||
type FDID uint32
|
||||
|
||||
// InvalidFDID represents an invalid FDID.
|
||||
|
||||
+82
-27
@@ -85,7 +85,9 @@ func ErrorHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32,
|
||||
// has been successfully mounted can other channels be created.
|
||||
func MountHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req MountReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
mountPath := path.Clean(string(req.MountPath))
|
||||
if !filepath.IsAbs(mountPath) {
|
||||
@@ -160,7 +162,9 @@ func ChannelHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32
|
||||
// FStatHandler handles the FStat RPC.
|
||||
func FStatHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req StatReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.lookupFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -185,7 +189,9 @@ func SetStatHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32
|
||||
}
|
||||
|
||||
var req SetStatReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -203,7 +209,9 @@ func SetStatHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32
|
||||
// WalkHandler handles the Walk RPC.
|
||||
func WalkHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req WalkReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.DirFD)
|
||||
if err != nil {
|
||||
@@ -225,7 +233,9 @@ func WalkHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, e
|
||||
// WalkStatHandler handles the WalkStat RPC.
|
||||
func WalkStatHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req WalkReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.DirFD)
|
||||
if err != nil {
|
||||
@@ -256,7 +266,9 @@ func WalkStatHandler(c *Connection, comm Communicator, payloadLen uint32) (uint3
|
||||
// OpenAtHandler handles the OpenAt RPC.
|
||||
func OpenAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req OpenAtReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
// Only keep allowed open flags.
|
||||
if allowedFlags := req.Flags & allowedOpenFlags; allowedFlags != req.Flags {
|
||||
@@ -291,7 +303,9 @@ func OpenCreateAtHandler(c *Connection, comm Communicator, payloadLen uint32) (u
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req OpenCreateAtReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
// Only keep allowed open flags.
|
||||
if allowedFlags := req.Flags & allowedOpenFlags; allowedFlags != req.Flags {
|
||||
@@ -319,7 +333,9 @@ func OpenCreateAtHandler(c *Connection, comm Communicator, payloadLen uint32) (u
|
||||
// CloseHandler handles the Close RPC.
|
||||
func CloseHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req CloseReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
for _, fd := range req.FDs {
|
||||
c.RemoveFD(fd)
|
||||
}
|
||||
@@ -331,7 +347,9 @@ func CloseHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32,
|
||||
// FSyncHandler handles the FSync RPC.
|
||||
func FSyncHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req FsyncReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
// Return the first error we encounter, but sync everything we can
|
||||
// regardless.
|
||||
@@ -363,7 +381,10 @@ func PWriteHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32,
|
||||
// Note that it is an optimized Unmarshal operation which avoids any buffer
|
||||
// allocation and copying. req.Buf just points to payload. This is safe to do
|
||||
// as the handler owns payload and req's lifetime is limited to the handler.
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupOpenFD(req.FD)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -377,7 +398,9 @@ func PWriteHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32,
|
||||
// PReadHandler handles the PRead RPC.
|
||||
func PReadHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req PReadReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupOpenFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -396,7 +419,9 @@ func MkdirAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req MkdirAtReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
name := string(req.Name)
|
||||
if err := checkSafeName(name); err != nil {
|
||||
@@ -420,7 +445,9 @@ func MknodAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req MknodAtReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
name := string(req.Name)
|
||||
if err := checkSafeName(name); err != nil {
|
||||
@@ -444,7 +471,9 @@ func SymlinkAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req SymlinkAtReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
name := string(req.Name)
|
||||
if err := checkSafeName(name); err != nil {
|
||||
@@ -468,7 +497,9 @@ func LinkAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32,
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req LinkAtReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
name := string(req.Name)
|
||||
if err := checkSafeName(name); err != nil {
|
||||
@@ -494,7 +525,9 @@ func LinkAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32,
|
||||
// FStatFSHandler handles the FStatFS RPC.
|
||||
func FStatFSHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req FStatFSReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -510,7 +543,9 @@ func FAllocateHandler(c *Connection, comm Communicator, payloadLen uint32) (uint
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req FAllocateReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupOpenFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -526,7 +561,9 @@ func FAllocateHandler(c *Connection, comm Communicator, payloadLen uint32) (uint
|
||||
// ReadLinkAtHandler handles the ReadLinkAt RPC.
|
||||
func ReadLinkAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req ReadLinkAtReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -542,7 +579,9 @@ func ReadLinkAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uin
|
||||
// FlushHandler handles the Flush RPC.
|
||||
func FlushHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req FlushReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupOpenFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -556,7 +595,9 @@ func FlushHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32,
|
||||
// ConnectHandler handles the Connect RPC.
|
||||
func ConnectHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req ConnectReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -575,7 +616,9 @@ func UnlinkAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint3
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req UnlinkAtReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
name := string(req.Name)
|
||||
if err := checkSafeName(name); err != nil {
|
||||
@@ -599,7 +642,9 @@ func RenameAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint3
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req RenameAtReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
newName := string(req.NewName)
|
||||
if err := checkSafeName(newName); err != nil {
|
||||
@@ -682,7 +727,9 @@ func (fd *ControlFD) renameRecursiveLocked(newDir *ControlFD, newName string, pi
|
||||
// Getdents64Handler handles the Getdents64 RPC.
|
||||
func Getdents64Handler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req Getdents64Req
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupOpenFD(req.DirFD)
|
||||
if err != nil {
|
||||
@@ -704,7 +751,9 @@ func Getdents64Handler(c *Connection, comm Communicator, payloadLen uint32) (uin
|
||||
// FGetXattrHandler handles the FGetXattr RPC.
|
||||
func FGetXattrHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req FGetXattrReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -720,7 +769,9 @@ func FSetXattrHandler(c *Connection, comm Communicator, payloadLen uint32) (uint
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req FSetXattrReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -733,7 +784,9 @@ func FSetXattrHandler(c *Connection, comm Communicator, payloadLen uint32) (uint
|
||||
// FListXattrHandler handles the FListXattr RPC.
|
||||
func FListXattrHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, error) {
|
||||
var req FListXattrReq
|
||||
req.UnmarshalUnsafe(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.FD)
|
||||
if err != nil {
|
||||
@@ -749,7 +802,9 @@ func FRemoveXattrHandler(c *Connection, comm Communicator, payloadLen uint32) (u
|
||||
return 0, unix.EROFS
|
||||
}
|
||||
var req FRemoveXattrReq
|
||||
req.UnmarshalBytes(comm.PayloadBuf(payloadLen))
|
||||
if _, ok := req.CheckedUnmarshal(comm.PayloadBuf(payloadLen)); !ok {
|
||||
return 0, unix.EIO
|
||||
}
|
||||
|
||||
fd, err := c.LookupControlFD(req.FD)
|
||||
if err != nil {
|
||||
|
||||
+313
-172
File diff suppressed because it is too large
Load Diff
@@ -65,6 +65,24 @@ func (m *MsgDynamic) UnmarshalBytes(src []byte) []byte {
|
||||
return UnmarshalUnsafeMsg1Slice(m.Arr, src)
|
||||
}
|
||||
|
||||
// CheckedUnmarshal implements marshal.CheckedMarshallable.CheckedUnmarshal.
|
||||
func (m *MsgDynamic) CheckedUnmarshal(src []byte) ([]byte, bool) {
|
||||
m.Arr = m.Arr[:0]
|
||||
if m.SizeBytes() > len(src) {
|
||||
return nil, false
|
||||
}
|
||||
src = m.N.UnmarshalUnsafe(src)
|
||||
if int(m.N) > cap(m.Arr) {
|
||||
m.Arr = make([]MsgSimple, m.N)
|
||||
} else {
|
||||
m.Arr = m.Arr[:m.N]
|
||||
}
|
||||
if int(m.N)*(*MsgSimple)(nil).SizeBytes() > len(src) {
|
||||
return nil, false
|
||||
}
|
||||
return UnmarshalUnsafeMsg1Slice(m.Arr, src), true
|
||||
}
|
||||
|
||||
// Randomize randomizes the contents of m.
|
||||
func (m *MsgDynamic) Randomize(arrLen int) {
|
||||
m.N = primitive.Uint32(arrLen)
|
||||
@@ -75,8 +93,6 @@ func (m *MsgDynamic) Randomize(arrLen int) {
|
||||
}
|
||||
|
||||
// P9Version mimics p9.TVersion and p9.Rversion.
|
||||
//
|
||||
// +marshal dynamic
|
||||
type P9Version struct {
|
||||
MSize primitive.Uint32
|
||||
Version string
|
||||
@@ -95,11 +111,18 @@ func (v *P9Version) MarshalBytes(dst []byte) []byte {
|
||||
return dst[copy(dst, v.Version):]
|
||||
}
|
||||
|
||||
// UnmarshalBytes implements marshal.Marshallable.UnmarshalBytes.
|
||||
func (v *P9Version) UnmarshalBytes(src []byte) []byte {
|
||||
// CheckedUnmarshal implements marshal.CheckedMarshallable.CheckedUnmarshal.
|
||||
func (v *P9Version) CheckedUnmarshal(src []byte) ([]byte, bool) {
|
||||
v.Version = ""
|
||||
if v.SizeBytes() > len(src) {
|
||||
return nil, false
|
||||
}
|
||||
src = v.MSize.UnmarshalUnsafe(src)
|
||||
var versionLen primitive.Uint16
|
||||
src = versionLen.UnmarshalUnsafe(src)
|
||||
if int(versionLen) > len(src) {
|
||||
return nil, false
|
||||
}
|
||||
v.Version = string(src[:versionLen])
|
||||
return src[versionLen:]
|
||||
return src[versionLen:], true
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func TestSndRcvMessageNoPayload(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func checkMessageReceive(t *testing.T, comm *sockCommunicator, wantM MID, wantMsg marshal.Marshallable) {
|
||||
func checkMessageReceive(t *testing.T, comm *sockCommunicator, wantM MID, wantMsg interface{}) {
|
||||
gotM, payloadLen, err := comm.rcvMsg(0)
|
||||
if err != nil {
|
||||
t.Fatalf("readMessageFrom failed: %v", err)
|
||||
|
||||
@@ -132,6 +132,26 @@ type Marshallable interface {
|
||||
CopyOutN(cc CopyContext, addr hostarch.Addr, limit int) (int, error)
|
||||
}
|
||||
|
||||
// CheckedMarshallable represents operations on a type that can be marshalled
|
||||
// to and from memory and additionally does bound checking.
|
||||
type CheckedMarshallable interface {
|
||||
// CheckedMarshal is the same as Marshallable.MarshalUnsafe but without the
|
||||
// precondition that dst must at least have some appropriate length. Similar
|
||||
// to Marshallable.MarshalBytes, it returns a shifted slice according to how
|
||||
// much data is consumed. Additionally it returns a bool indicating whether
|
||||
// marshalling was successful. Unsuccessful marshalling doesn't consume any
|
||||
// data.
|
||||
CheckedMarshal(dst []byte) ([]byte, bool)
|
||||
|
||||
// CheckedUnmarshal is the same as Marshallable.UmarshalUnsafe but without
|
||||
// the precondition that src must at least have some appropriate length.
|
||||
// Similar to Marshallable.UnmarshalBytes, it returns a shifted slice
|
||||
// according to how much data is consumed. Additionally it returns a bool
|
||||
// indicating whether marshalling was successful. Unsuccessful marshalling
|
||||
// doesn't consume any data.
|
||||
CheckedUnmarshal(src []byte) ([]byte, bool)
|
||||
}
|
||||
|
||||
// go-marshal generates additional functions for a type based on additional
|
||||
// clauses to the +marshal directive. They are documented below.
|
||||
//
|
||||
@@ -187,3 +207,12 @@ type Marshallable interface {
|
||||
// func CopyInt32SliceIn(cc marshal.CopyContext, addr hostarch.Addr, dst []Int32) (int, error) { ... }
|
||||
//
|
||||
// This may help avoid a cast depending on how the generated functions are used.
|
||||
//
|
||||
// Bound Checking
|
||||
// ==============
|
||||
//
|
||||
// Some users might want to do bound checking on marshal and unmarshal. This is
|
||||
// is useful when the user does not control the buffer size. To prevent
|
||||
// repeated bound checking code around Marshallable, users can add a
|
||||
// "boundCheck" clause to the +marshal directive. go_marshal will generate the
|
||||
// CheckedMarshallable interface methods on the type.
|
||||
|
||||
@@ -25,42 +25,42 @@ import (
|
||||
|
||||
// Int8 is a marshal.Marshallable implementation for int8.
|
||||
//
|
||||
// +marshal slice:Int8Slice:inner
|
||||
// +marshal boundCheck slice:Int8Slice:inner
|
||||
type Int8 int8
|
||||
|
||||
// Uint8 is a marshal.Marshallable implementation for uint8.
|
||||
//
|
||||
// +marshal slice:Uint8Slice:inner
|
||||
// +marshal boundCheck slice:Uint8Slice:inner
|
||||
type Uint8 uint8
|
||||
|
||||
// Int16 is a marshal.Marshallable implementation for int16.
|
||||
//
|
||||
// +marshal slice:Int16Slice:inner
|
||||
// +marshal boundCheck slice:Int16Slice:inner
|
||||
type Int16 int16
|
||||
|
||||
// Uint16 is a marshal.Marshallable implementation for uint16.
|
||||
//
|
||||
// +marshal slice:Uint16Slice:inner
|
||||
// +marshal boundCheck slice:Uint16Slice:inner
|
||||
type Uint16 uint16
|
||||
|
||||
// Int32 is a marshal.Marshallable implementation for int32.
|
||||
//
|
||||
// +marshal slice:Int32Slice:inner
|
||||
// +marshal boundCheck slice:Int32Slice:inner
|
||||
type Int32 int32
|
||||
|
||||
// Uint32 is a marshal.Marshallable implementation for uint32.
|
||||
//
|
||||
// +marshal slice:Uint32Slice:inner
|
||||
// +marshal boundCheck slice:Uint32Slice:inner
|
||||
type Uint32 uint32
|
||||
|
||||
// Int64 is a marshal.Marshallable implementation for int64.
|
||||
//
|
||||
// +marshal slice:Int64Slice:inner
|
||||
// +marshal boundCheck slice:Int64Slice:inner
|
||||
type Int64 int64
|
||||
|
||||
// Uint64 is a marshal.Marshallable implementation for uint64.
|
||||
//
|
||||
// +marshal slice:Uint64Slice:inner
|
||||
// +marshal boundCheck slice:Uint64Slice:inner
|
||||
type Uint64 uint64
|
||||
|
||||
// ByteSlice is a marshal.Marshallable implementation for []byte.
|
||||
|
||||
@@ -217,10 +217,11 @@ type sliceAPI struct {
|
||||
// marshallableType carries information about a type marked with the '+marshal'
|
||||
// directive.
|
||||
type marshallableType struct {
|
||||
spec *ast.TypeSpec
|
||||
slice *sliceAPI
|
||||
recv string
|
||||
dynamic bool
|
||||
spec *ast.TypeSpec
|
||||
slice *sliceAPI
|
||||
recv string
|
||||
dynamic bool
|
||||
boundCheck bool
|
||||
}
|
||||
|
||||
func newMarshallableType(fset *token.FileSet, tagLine *ast.Comment, spec *ast.TypeSpec) *marshallableType {
|
||||
@@ -258,6 +259,9 @@ func newMarshallableType(fset *token.FileSet, tagLine *ast.Comment, spec *ast.Ty
|
||||
} else if tag == "dynamic" {
|
||||
mt.dynamic = true
|
||||
continue
|
||||
} else if tag == "boundCheck" {
|
||||
mt.boundCheck = true
|
||||
continue
|
||||
}
|
||||
|
||||
unhandledTags = append(unhandledTags, tag)
|
||||
@@ -391,6 +395,9 @@ func (g *Generator) generateOne(t *marshallableType, fset *token.FileSet) *inter
|
||||
if t.slice != nil {
|
||||
abortAt(fset.Position(t.slice.comment.Slash), "Slice API is not supported for dynamic types because it assumes that each slice element is statically sized.")
|
||||
}
|
||||
if t.boundCheck {
|
||||
abortAt(fset.Position(t.slice.comment.Slash), "Can not generate Checked methods for dynamic types. Has to be implemented manually.")
|
||||
}
|
||||
// No validation needed, assume the user knows what they are doing.
|
||||
i.emitMarshallableForDynamicType()
|
||||
return i
|
||||
@@ -399,12 +406,18 @@ func (g *Generator) generateOne(t *marshallableType, fset *token.FileSet) *inter
|
||||
case *ast.StructType:
|
||||
i.validateStruct(t.spec, ty)
|
||||
i.emitMarshallableForStruct(ty)
|
||||
if t.boundCheck {
|
||||
i.emitCheckedMarshallableForStruct()
|
||||
}
|
||||
if t.slice != nil {
|
||||
i.emitMarshallableSliceForStruct(ty, t.slice)
|
||||
}
|
||||
case *ast.Ident:
|
||||
i.validatePrimitiveNewtype(ty)
|
||||
i.emitMarshallableForPrimitiveNewtype(ty)
|
||||
if t.boundCheck {
|
||||
i.emitCheckedMarshallableForPrimitiveNewtype()
|
||||
}
|
||||
if t.slice != nil {
|
||||
i.emitMarshallableSliceForPrimitiveNewtype(ty, t.slice)
|
||||
}
|
||||
@@ -412,6 +425,9 @@ func (g *Generator) generateOne(t *marshallableType, fset *token.FileSet) *inter
|
||||
i.validateArrayNewtype(t.spec.Name, ty)
|
||||
// After validate, we can safely call arrayLen.
|
||||
i.emitMarshallableForArrayNewtype(t.spec.Name, ty, ty.Elt.(*ast.Ident))
|
||||
if t.boundCheck {
|
||||
i.emitCheckedMarshallableForArrayNewtype()
|
||||
}
|
||||
if t.slice != nil {
|
||||
abortAt(fset.Position(t.slice.comment.Slash), "Array type marked as '+marshal slice:...', but this is not supported. Perhaps fold one of the dimensions?")
|
||||
}
|
||||
@@ -426,7 +442,7 @@ func (g *Generator) generateOne(t *marshallableType, fset *token.FileSet) *inter
|
||||
// implementations type t.
|
||||
func (g *Generator) generateOneTestSuite(t *marshallableType) *testGenerator {
|
||||
i := newTestGenerator(t.spec, t.recv)
|
||||
i.emitTests(t.slice)
|
||||
i.emitTests(t.slice, t.boundCheck)
|
||||
return i
|
||||
}
|
||||
|
||||
|
||||
@@ -150,3 +150,33 @@ func (g *interfaceGenerator) emitMarshallableForArrayNewtype(n *ast.Ident, a *as
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitCheckedMarshallableForArrayNewtype() {
|
||||
g.emit("// CheckedMarshal implements marshal.CheckedMarshallable.CheckedMarshal.\n")
|
||||
g.emit("func (%s *%s) CheckedMarshal(dst []byte) ([]byte, bool) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("size := %s.SizeBytes()\n", g.r)
|
||||
g.emit("if size > len(dst) {\n")
|
||||
g.inIndent(func() {
|
||||
g.emit("return dst, false\n")
|
||||
})
|
||||
g.emit("}\n")
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&%s[0]), uintptr(size))\n", g.r)
|
||||
g.emit("return dst[size:], true\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// CheckedUnmarshal implements marshal.CheckedMarshallable.CheckedUnmarshal.\n")
|
||||
g.emit("func (%s *%s) CheckedUnmarshal(src []byte) ([]byte, bool) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("size := %s.SizeBytes()\n", g.r)
|
||||
g.emit("if size > len(src) {\n")
|
||||
g.inIndent(func() {
|
||||
g.emit("return src, false\n")
|
||||
})
|
||||
g.emit("}\n")
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(size))\n", g.r)
|
||||
g.emit("return src[size:], true\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
@@ -211,6 +211,36 @@ func (g *interfaceGenerator) emitMarshallableForPrimitiveNewtype(nt *ast.Ident)
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitCheckedMarshallableForPrimitiveNewtype() {
|
||||
g.emit("// CheckedMarshal implements marshal.CheckedMarshallable.CheckedMarshal.\n")
|
||||
g.emit("func (%s *%s) CheckedMarshal(dst []byte) ([]byte, bool) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("size := %s.SizeBytes()\n", g.r)
|
||||
g.emit("if size > len(dst) {\n")
|
||||
g.inIndent(func() {
|
||||
g.emit("return dst, false\n")
|
||||
})
|
||||
g.emit("}\n")
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(%s), uintptr(size))\n", g.r)
|
||||
g.emit("return dst[size:], true\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// CheckedUnmarshal implements marshal.CheckedMarshallable.CheckedUnmarshal.\n")
|
||||
g.emit("func (%s *%s) CheckedUnmarshal(src []byte) ([]byte, bool) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("size := %s.SizeBytes()\n", g.r)
|
||||
g.emit("if size > len(src) {\n")
|
||||
g.inIndent(func() {
|
||||
g.emit("return src, false\n")
|
||||
})
|
||||
g.emit("}\n")
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(size))\n", g.r)
|
||||
g.emit("return src[size:], true\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitMarshallableSliceForPrimitiveNewtype(nt *ast.Ident, slice *sliceAPI) {
|
||||
g.recordUsedImport("marshal")
|
||||
g.recordUsedImport("hostarch")
|
||||
|
||||
@@ -435,6 +435,32 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) {
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitCheckedMarshallableForStruct() {
|
||||
g.emit("// CheckedMarshal implements marshal.CheckedMarshallable.CheckedMarshal.\n")
|
||||
g.emit("func (%s *%s) CheckedMarshal(dst []byte) ([]byte, bool) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("if %s.SizeBytes() > len(dst) {\n", g.r)
|
||||
g.inIndent(func() {
|
||||
g.emit("return dst, false\n")
|
||||
})
|
||||
g.emit("}\n")
|
||||
g.emit("return %s.MarshalUnsafe(dst), true\n", g.r)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// CheckedUnmarshal implements marshal.CheckedMarshallable.CheckedUnmarshal.\n")
|
||||
g.emit("func (%s *%s) CheckedUnmarshal(src []byte) ([]byte, bool) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("if %s.SizeBytes() > len(src) {\n", g.r)
|
||||
g.inIndent(func() {
|
||||
g.emit("return src, false\n")
|
||||
})
|
||||
g.emit("}\n")
|
||||
g.emit("return %s.UnmarshalUnsafe(src), true\n", g.r)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitMarshallableSliceForStruct(st *ast.StructType, slice *sliceAPI) {
|
||||
thisPacked := g.isStructPacked(st)
|
||||
|
||||
|
||||
@@ -216,7 +216,37 @@ func (g *testGenerator) emitTestSizeBytesOnTypedNilPtr() {
|
||||
})
|
||||
}
|
||||
|
||||
func (g *testGenerator) emitTests(slice *sliceAPI) {
|
||||
func (g *testGenerator) emitTestBoundCheck() {
|
||||
g.inTestFunction("TestCheckedMethods", func() {
|
||||
g.emit("var x %s\n", g.typeName())
|
||||
g.emit("size := x.SizeBytes()\n")
|
||||
g.emit("b := make([]byte, size)\n\n")
|
||||
|
||||
g.emit("if _, ok := x.CheckedMarshal(b[:size-1]); ok {\n")
|
||||
g.inIndent(func() {
|
||||
g.emit("t.Errorf(\"CheckedMarshal should have failed because buffer is small\")\n")
|
||||
})
|
||||
g.emit("}\n")
|
||||
g.emit("if _, ok := x.CheckedMarshal(b); !ok {\n")
|
||||
g.inIndent(func() {
|
||||
g.emit("t.Errorf(\"CheckedMarshal should have succeeded because buffer size is okay\")\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("if _, ok := x.CheckedUnmarshal(b[:size-1]); ok {\n")
|
||||
g.inIndent(func() {
|
||||
g.emit("t.Errorf(\"CheckedUnmarshal should have failed because buffer is small\")\n")
|
||||
})
|
||||
g.emit("}\n")
|
||||
g.emit("if _, ok := x.CheckedUnmarshal(b); !ok {\n")
|
||||
g.inIndent(func() {
|
||||
g.emit("t.Errorf(\"CheckedUnmarshal should have succeeded because buffer size is okay\")\n")
|
||||
})
|
||||
g.emit("}\n")
|
||||
})
|
||||
}
|
||||
|
||||
func (g *testGenerator) emitTests(slice *sliceAPI, boundCheck bool) {
|
||||
g.emitTestNonZeroSize()
|
||||
g.emitTestSuspectAlignment()
|
||||
g.emitTestMarshalUnmarshalPreservesData()
|
||||
@@ -226,6 +256,9 @@ func (g *testGenerator) emitTests(slice *sliceAPI) {
|
||||
if slice != nil {
|
||||
g.emitTestMarshalUnmarshalSlicePreservesData(slice)
|
||||
}
|
||||
if boundCheck {
|
||||
g.emitTestBoundCheck()
|
||||
}
|
||||
}
|
||||
|
||||
func (g *testGenerator) write(out io.Writer) error {
|
||||
|
||||
@@ -39,7 +39,7 @@ type Type1 struct {
|
||||
|
||||
// Type2 is a test data type.
|
||||
//
|
||||
// +marshal
|
||||
// +marshal boundCheck
|
||||
type Type2 struct {
|
||||
n int64
|
||||
c byte
|
||||
@@ -134,12 +134,12 @@ type Stat struct {
|
||||
|
||||
// InetAddr is an example marshallable newtype on an array.
|
||||
//
|
||||
// +marshal
|
||||
// +marshal boundCheck
|
||||
type InetAddr [4]byte
|
||||
|
||||
// SignalSet is an example marshallable newtype on a primitive.
|
||||
//
|
||||
// +marshal slice:SignalSetSlice:inner
|
||||
// +marshal slice:SignalSetSlice:inner boundCheck
|
||||
type SignalSet uint64
|
||||
|
||||
// SignalSetAlias is an example newtype on another marshallable type.
|
||||
|
||||
Reference in New Issue
Block a user