diff --git a/testutil/tpm.go b/testutil/tpm.go index 7ab34db..29b421a 100644 --- a/testutil/tpm.go +++ b/testutil/tpm.go @@ -173,7 +173,14 @@ var ( wrapMssimTransport = WrapTransport + // ErrSkipNoTPM is expected to be returned from tpm2.TPMDevice implementations + // in order to cause a test to be skipped if no TPM connection is available. ErrSkipNoTPM = errors.New("no TPM configured for the test") + + // ErrNoTPMDevice can be returned from tpm2.TPMDevice implementations to indicate that + // no TPM is available, but this will generally cause a test to fail rather than + // be skipped. + ErrNoTPMDevice = errors.New("no TPM device available") ) type tpmBackendFlag TPMBackendType @@ -647,6 +654,7 @@ type TransportBackedDevice struct { mu sync.Mutex transport *Transport closable bool + maxOpen int opened int } @@ -665,16 +673,20 @@ type TransportBackedDevice struct { // but calling Close on any returned transport will not actually close the // underlying transport - it will mark that specific one as closed. // +// The maxOpen argument can limit the number of open transports. If set to +// a value <= 0, then there is no limit. +// // Consider that whilst [tpm2.TPMDevice] implementations can generally be // used by more than one goroutine, each opened [tpm2.Transport] is only // safe to use from a single goroutine, and this device returns multiple // pointers to the same transport. // // Note that this device does not work with [OpenTPMDevice] or [OpenTPMDeviceT]. -func NewTransportBackedDevice(transport *Transport, closable bool) *TransportBackedDevice { +func NewTransportBackedDevice(transport *Transport, closable bool, maxOpen int) *TransportBackedDevice { return &TransportBackedDevice{ transport: transport, closable: closable, + maxOpen: maxOpen, } } @@ -722,6 +734,10 @@ func (d *TransportBackedDevice) Open() (tpm2.Transport, error) { d.mu.Lock() defer d.mu.Unlock() + if d.maxOpen > 0 && d.opened >= d.maxOpen { + return nil, ErrNoTPMDevice + } + d.opened += 1 return &duplicateTransport{ Transport: d.transport, diff --git a/testutil/tpm_test.go b/testutil/tpm_test.go index 48cca1b..ecb43dc 100644 --- a/testutil/tpm_test.go +++ b/testutil/tpm_test.go @@ -17,7 +17,7 @@ type tpmSuite struct { var _ = Suite(&tpmSuite{}) func (s *tpmSuite) TestNewTransportBackedDeviceClosable(c *C) { - device := NewTransportBackedDevice(s.Transport, true) + device := NewTransportBackedDevice(s.Transport, true, -1) c.Check(device.NumberOpen(), internal_testutil.IntEqual, 0) transport, err := device.Open() @@ -41,7 +41,7 @@ func (s *tpmSuite) TestNewTransportBackedDeviceClosable(c *C) { } func (s *tpmSuite) TestNewTransportBackedDeviceNotClosable(c *C) { - device := NewTransportBackedDevice(s.Transport, false) + device := NewTransportBackedDevice(s.Transport, false, -1) c.Check(device.NumberOpen(), internal_testutil.IntEqual, 0) transport, err := device.Open() @@ -61,7 +61,7 @@ func (s *tpmSuite) TestNewTransportBackedDeviceNotClosable(c *C) { } func (s *tpmSuite) TestNewTransportBackedDeviceMultipleOpen(c *C) { - device := NewTransportBackedDevice(s.Transport, false) + device := NewTransportBackedDevice(s.Transport, false, 2) c.Check(device.NumberOpen(), internal_testutil.IntEqual, 0) transport1, err := device.Open() @@ -80,3 +80,28 @@ func (s *tpmSuite) TestNewTransportBackedDeviceMultipleOpen(c *C) { // The test fixture will fail if the underlying transport was closed // unexpectedly } + +func (s *tpmSuite) TestNewTransportBackedDeviceMaxOpen(c *C) { + device := NewTransportBackedDevice(s.Transport, false, 2) + c.Check(device.NumberOpen(), internal_testutil.IntEqual, 0) + + transport1, err := device.Open() + c.Assert(err, IsNil) + c.Check(device.NumberOpen(), internal_testutil.IntEqual, 1) + + transport2, err := device.Open() + c.Assert(err, IsNil) + c.Check(device.NumberOpen(), internal_testutil.IntEqual, 2) + + _, err = device.Open() + c.Check(err, Equals, ErrNoTPMDevice) + c.Check(device.NumberOpen(), internal_testutil.IntEqual, 2) + + c.Check(transport1.Close(), IsNil) + c.Check(device.NumberOpen(), internal_testutil.IntEqual, 1) + c.Check(transport2.Close(), IsNil) + c.Check(device.NumberOpen(), internal_testutil.IntEqual, 0) + + // The test fixture will fail if the underlying transport was closed + // unexpectedly +}