Re: [PATCH v6] misc: Add Nitro Secure Module driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Oct 10, 2023, at 23:34, Alexander Graf wrote:
> This patch adds a driver for NSM that exposes a /dev/nsm device node which
> user space can issue an ioctl on this device with raw NSM CBOR formatted
> commands to request attestation documents, influence PCR states, read
> entropy and enumerate status of the device. In addition, the driver
> implements a hwrng backend.

I haven't had a chance to actually read the v3 submission in enough
detail, but assuming we're going to go with the simple pass-through
interface here, I've looked for some mostly cosmetic improvements
that you may want to integrate.

> +/* Timeout for NSM virtqueue respose in milliseconds. */
> +#define NSM_DEFAULT_TIMEOUT_MSECS (120000) /* 2 minutes */
> +
> +struct nsm {
> +	struct virtio_device *vdev;
> +	struct virtqueue     *vq;
> +	struct mutex          lock;
> +	wait_queue_head_t     wq;
> +	bool                  device_notified;

Instead of a manual wait queue plus a bool, using a
'struct completion' simplifies this a little bit.

> +
> +/* Maximum length input data */
> +struct nsm_data_req {
> +	__u32 len;
> +	__u8  data[NSM_REQUEST_MAX_SIZE];
> +};

> +/* Maximum length output data */
> +struct nsm_data_resp {
> +	__u32 len;
> +	__u8  data[NSM_RESPONSE_MAX_SIZE];
> +};

You have endian-conversion for some of the data fields
but not the 'len field here, I guess these should be
__le32 instead of __u32, with the appropriate le32_to_cpu()
and cpu_to_le32() conversion when passing the native
u32 word from userspace.

It does seem odd that you have a little-endian length field
here, but big-endian length fields inside of the cbor
data. 

> +#define CBOR_HEADER_SIZE_U8  (CBOR_HEADER_SIZE_SHORT + sizeof(u8))
> +#define CBOR_HEADER_SIZE_U16 (CBOR_HEADER_SIZE_SHORT + sizeof(u16))
> +#define CBOR_HEADER_SIZE_U32 (CBOR_HEADER_SIZE_SHORT + sizeof(u32))
> +#define CBOR_HEADER_SIZE_U64 (CBOR_HEADER_SIZE_SHORT + sizeof(u64))

Similarly, I guess these should be __be16/__be32/__be64?

> +	} else if (cbor_short_size == CBOR_LONG_SIZE_U32) {
> +		if (cbor_object_size < CBOR_HEADER_SIZE_U32)
> +			return -EFAULT;
> +		/* 4 bytes */
> +		array_len = cbor_object[1] << 24 |
> +			cbor_object[2] << 16 |
> +			cbor_object[3] << 8  |
> +			cbor_object[4];
> +		array_offset = CBOR_HEADER_SIZE_U32;
> +	} else if (cbor_short_size == CBOR_LONG_SIZE_U64) {
> +		if (cbor_object_size < CBOR_HEADER_SIZE_U64)
> +			return -EFAULT;
> +		/* 8 bytes */
> +		array_len = (u64) cbor_object[1] << 56 |
> +			  (u64) cbor_object[2] << 48 |
> +			  (u64) cbor_object[3] << 40 |
> +			  (u64) cbor_object[4] << 32 |
> +			  (u64) cbor_object[5] << 24 |
> +			  (u64) cbor_object[6] << 16 |
> +			  (u64) cbor_object[7] << 8  |
> +			  (u64) cbor_object[8];

These could use be{!6,32,64}_to_cpup() for clarity.

> +static int nsm_rng_read(struct hwrng *rng, void *data, size_t max, 
> bool wait)
> +{
> +	struct nsm *nsm = hwrng_to_nsm(rng);
> +	struct device *dev = &nsm->vdev->dev;
> +	struct nsm_msg *msg;
> +	int rc = 0;
> +
> +	/* NSM always needs to wait for a response */
> +	if (!wait)
> +		return 0;
> +
> +	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
> +	if (!msg)
> +		return -ENOMEM;
> +
> +	rc = fill_req_get_random(nsm, &msg->req);
> +	if (rc != 0)
> +		goto out;
> +
> +	rc = nsm_sendrecv_msg(nsm, msg);
> +	if (rc != 0)
> +		goto out;
> +
> +	rc = parse_resp_get_random(nsm, &msg->resp, data, max);
> +	if (rc < 0)
> +		goto out;

It looks like the bulk of this function happens inside of
nsm_sendrecv_msg(), which uses a mutex for serialization.

In this case, I think you can replace the dynamic allocation
during read() with a preallocated buffer in the device that
always gets used here, after you extend the mutex out to the
entire fill_req_get_random()/nsm_sendrecv_msg()/parse_resp_get_random()
block.

> +static long nsm_dev_ioctl(struct file *file, unsigned int cmd,
> +	unsigned long arg)
> +{
> +	void __user *argp = u64_to_user_ptr((u64)arg);
> +	struct nsm *nsm = file_to_nsm(file);
> +	struct nsm_msg *msg;
> +	struct nsm_raw raw;
> +	int r = 0;
> +
> +	if (cmd != NSM_IOCTL_RAW)
> +		return -EINVAL;
> +
> +	if (_IOC_SIZE(cmd) != sizeof(raw))
> +		return -EINVAL;
> +
> +	/* Allocate message buffers to device */
> +	r = -ENOMEM;
> +	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
> +	if (!msg)
> +		goto out;
> +
> +	/* Copy user argument struct to kernel argument struct */
> +	r = -EFAULT;
> +	if (copy_from_user(&raw, argp, _IOC_SIZE(cmd)))
> +		goto out;
> +
> +	/* Convert kernel argument struct to device request */
> +	r = fill_req_raw(nsm, &msg->req, &raw);
> +	if (r)
> +		goto out;
> +
> +	/* Send message to NSM and read reply */
> +	r = nsm_sendrecv_msg(nsm, msg);
> +	if (r)
> +		goto out;
> +
> +	/* Parse device response into kernel argument struct */
> +	r = parse_resp_raw(nsm, &msg->resp, &raw);
> +	if (r)
> +		goto out;

And the same is probably true here.

> +static int nsm_dev_file_open(struct inode *node, struct file *file)
> +{
> +	return 0;
> +}
> +
> +static int nsm_dev_file_close(struct inode *inode, struct file *file)
> +{
> +	return 0;
> +}

These are not needed if they don't do anything.

     Arnd



[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]
  Powered by Linux