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:
Ayush Ranjan
2021-11-19 20:16:54 -08:00
committed by gVisor bot
parent 889190828c
commit 0fd9b69d5c
19 changed files with 666 additions and 261 deletions
+1 -1
View File
@@ -242,7 +242,7 @@ const (
// Statx represents struct statx.
//
// +marshal slice:StatxSlice
// +marshal boundCheck slice:StatxSlice
type Statx struct {
Mask uint32
Blksize uint32
-3
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+19 -6
View File
@@ -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
}
+8 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+28 -5
View File
@@ -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
}
+1 -1
View File
@@ -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)
+29
View File
@@ -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.
+8 -8
View File
@@ -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.
+21 -5
View File
@@ -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)
+34 -1
View File
@@ -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 {
+3 -3
View File
@@ -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.