diff mbox series

[13/14] cxl/mem: Add limited Get Log command (0401h)

Message ID 20210130002438.1872527-14-ben.widawsky@intel.com (mailing list archive)
State Not Applicable, archived
Headers show
Series CXL 2.0 Support | expand

Commit Message

Ben Widawsky Jan. 30, 2021, 12:24 a.m. UTC
The Get Log command returns the actual log entries that are advertised
via the Get Supported Logs command (0400h). CXL device logs are selected
by UUID which is part of the CXL spec. Because the driver tries to
sanitize what is sent to hardware, there becomes a need to restrict the
types of logs which can be accessed by userspace. For example, the
vendor specific log might only be consumable by proprietary, or offline
applications, and therefore a good candidate for userspace.

The current driver infrastructure does allow basic validation for all
commands, but doesn't inspect any of the payload data. Along with Get
Log support comes new infrastructure to add a hook for payload
validation. This infrastructure is used to filter out the CEL UUID,
which the userspace driver doesn't have business knowing, and taints on
invalid UUIDs being sent to hardware.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/mem.c            | 42 +++++++++++++++++++++++++++++++++++-
 include/uapi/linux/cxl_mem.h |  1 +
 2 files changed, 42 insertions(+), 1 deletion(-)

Comments

Konrad Rzeszutek Wilk Feb. 1, 2021, 6:28 p.m. UTC | #1
On Fri, Jan 29, 2021 at 04:24:37PM -0800, Ben Widawsky wrote:
> The Get Log command returns the actual log entries that are advertised
> via the Get Supported Logs command (0400h). CXL device logs are selected
> by UUID which is part of the CXL spec. Because the driver tries to
> sanitize what is sent to hardware, there becomes a need to restrict the
> types of logs which can be accessed by userspace. For example, the
> vendor specific log might only be consumable by proprietary, or offline
> applications, and therefore a good candidate for userspace.
> 
> The current driver infrastructure does allow basic validation for all
> commands, but doesn't inspect any of the payload data. Along with Get
> Log support comes new infrastructure to add a hook for payload
> validation. This infrastructure is used to filter out the CEL UUID,
> which the userspace driver doesn't have business knowing, and taints on
> invalid UUIDs being sent to hardware.

Perhaps a better option is to reject invalid UUIDs?

And if you really really want to use invalid UUIDs then:

1) Make that code wrapped in CONFIG_CXL_DEBUG_THIS_IS_GOING_TO..?

2) Wrap it with lockdown code so that you can't do this at all
   when in LOCKDOWN_INTEGRITY or such?

> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/mem.c            | 42 +++++++++++++++++++++++++++++++++++-
>  include/uapi/linux/cxl_mem.h |  1 +
>  2 files changed, 42 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index b8ca6dff37b5..086268f1dd6c 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -119,6 +119,8 @@ static const uuid_t log_uuid[] = {
>  				 0x07, 0x19, 0x40, 0x3d, 0x86)
>  };
>  
> +static int validate_log_uuid(void __user *payload, size_t size);
> +
>  /**
>   * struct cxl_mem_command - Driver representation of a memory device command
>   * @info: Command information as it exists for the UAPI
> @@ -132,6 +134,10 @@ static const uuid_t log_uuid[] = {
>   *  * %CXL_CMD_INTERNAL_FLAG_PSEUDO: This is a pseudo command which doesn't have
>   *    a direct mapping to hardware. They are implicitly always enabled.
>   *
> + * @validate_payload: A function called after the command is validated but
> + * before it's sent to the hardware. The primary purpose is to validate, or
> + * fixup the actual payload.
> + *
>   * The cxl_mem_command is the driver's internal representation of commands that
>   * are supported by the driver. Some of these commands may not be supported by
>   * the hardware. The driver will use @info to validate the fields passed in by
> @@ -147,9 +153,11 @@ struct cxl_mem_command {
>  #define CXL_CMD_INTERNAL_FLAG_HIDDEN BIT(0)
>  #define CXL_CMD_INTERNAL_FLAG_MANDATORY BIT(1)
>  #define CXL_CMD_INTERNAL_FLAG_PSEUDO BIT(2)
> +
> +	int (*validate_payload)(void __user *payload, size_t size);
>  };
>  
> -#define CXL_CMD(_id, _flags, sin, sout, f)                                     \
> +#define CXL_CMD_VALIDATE(_id, _flags, sin, sout, f, v)                         \
>  	[CXL_MEM_COMMAND_ID_##_id] = {                                         \
>  	.info =	{                                                              \
>  			.id = CXL_MEM_COMMAND_ID_##_id,                        \
> @@ -159,8 +167,12 @@ struct cxl_mem_command {
>  		},                                                             \
>  	.flags = CXL_CMD_INTERNAL_FLAG_##f,                                    \
>  	.opcode = CXL_MBOX_OP_##_id,                                           \
> +	.validate_payload = v,                                                 \
>  	}
>  
> +#define CXL_CMD(_id, _flags, sin, sout, f)                                     \
> +	CXL_CMD_VALIDATE(_id, _flags, sin, sout, f, NULL)
> +
>  /*
>   * This table defines the supported mailbox commands for the driver. This table
>   * is made up of a UAPI structure. Non-negative values as parameters in the
> @@ -176,6 +188,8 @@ static struct cxl_mem_command mem_commands[] = {
>  	CXL_CMD(GET_PARTITION_INFO, NONE, 0, 0x20, NONE),
>  	CXL_CMD(GET_LSA, NONE, 0x8, ~0, MANDATORY),
>  	CXL_CMD(GET_HEALTH_INFO, NONE, 0, 0x12, MANDATORY),
> +	CXL_CMD_VALIDATE(GET_LOG, MUTEX, 0x18, ~0, MANDATORY,
> +			 validate_log_uuid),
>  };
>  
>  /*
> @@ -563,6 +577,13 @@ static int handle_mailbox_cmd_from_user(struct cxl_memdev *cxlmd,
>  			kvzalloc(cxlm->mbox.payload_size, GFP_KERNEL);
>  
>  	if (cmd->info.size_in) {
> +		if (cmd->validate_payload) {
> +			rc = cmd->validate_payload(u64_to_user_ptr(in_payload),
> +						   cmd->info.size_in);
> +			if (rc)
> +				goto out;
> +		}
> +
>  		mbox_cmd.payload_in = kvzalloc(cmd->info.size_in, GFP_KERNEL);
>  		if (!mbox_cmd.payload_in) {
>  			rc = -ENOMEM;
> @@ -1205,6 +1226,25 @@ struct cxl_mbox_get_log {
>  	__le32 length;
>  } __packed;
>  
> +static int validate_log_uuid(void __user *input, size_t size)
> +{
> +	struct cxl_mbox_get_log __user *get_log = input;
> +	uuid_t payload_uuid;
> +
> +	if (copy_from_user(&payload_uuid, &get_log->uuid, sizeof(uuid_t)))
> +		return -EFAULT;
> +
> +	/* All unspec'd logs shall taint */
> +	if (uuid_equal(&payload_uuid, &log_uuid[CEL_UUID]))
> +		return 0;
> +	if (uuid_equal(&payload_uuid, &log_uuid[DEBUG_UUID]))
> +		return 0;
> +
> +	add_taint(TAINT_RAW_PASSTHROUGH, LOCKDEP_STILL_OK);
> +
> +	return 0;
> +}
> +
>  static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out)
>  {
>  	u32 remaining = size;
> diff --git a/include/uapi/linux/cxl_mem.h b/include/uapi/linux/cxl_mem.h
> index 766c231d6150..7cdc7f7ce7ec 100644
> --- a/include/uapi/linux/cxl_mem.h
> +++ b/include/uapi/linux/cxl_mem.h
> @@ -39,6 +39,7 @@ extern "C" {
>  	___C(GET_PARTITION_INFO, "Get Partition Information"),            \
>  	___C(GET_LSA, "Get Label Storage Area"),                          \
>  	___C(GET_HEALTH_INFO, "Get Health Info"),                         \
> +	___C(GET_LOG, "Get Log"),                                         \
>  	___C(MAX, "Last command")
>  
>  #define ___C(a, b) CXL_MEM_COMMAND_ID_##a
> -- 
> 2.30.0
>
Ben Widawsky Feb. 2, 2021, 11:51 p.m. UTC | #2
On 21-02-01 13:28:48, Konrad Rzeszutek Wilk wrote:
> On Fri, Jan 29, 2021 at 04:24:37PM -0800, Ben Widawsky wrote:
> > The Get Log command returns the actual log entries that are advertised
> > via the Get Supported Logs command (0400h). CXL device logs are selected
> > by UUID which is part of the CXL spec. Because the driver tries to
> > sanitize what is sent to hardware, there becomes a need to restrict the
> > types of logs which can be accessed by userspace. For example, the
> > vendor specific log might only be consumable by proprietary, or offline
> > applications, and therefore a good candidate for userspace.
> > 
> > The current driver infrastructure does allow basic validation for all
> > commands, but doesn't inspect any of the payload data. Along with Get
> > Log support comes new infrastructure to add a hook for payload
> > validation. This infrastructure is used to filter out the CEL UUID,
> > which the userspace driver doesn't have business knowing, and taints on
> > invalid UUIDs being sent to hardware.
> 
> Perhaps a better option is to reject invalid UUIDs?
> 
> And if you really really want to use invalid UUIDs then:
> 
> 1) Make that code wrapped in CONFIG_CXL_DEBUG_THIS_IS_GOING_TO..?
> 
> 2) Wrap it with lockdown code so that you can't do this at all
>    when in LOCKDOWN_INTEGRITY or such?
> 

The commit message needs update btw as CEL is allowed in the latest rev of the
patches.

We could potentially combine this with the now added (in a branch) CONFIG_RAW
config option. Indeed I think that makes sense. Dan, thoughts?

> > 
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > ---
> >  drivers/cxl/mem.c            | 42 +++++++++++++++++++++++++++++++++++-
> >  include/uapi/linux/cxl_mem.h |  1 +
> >  2 files changed, 42 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > index b8ca6dff37b5..086268f1dd6c 100644
> > --- a/drivers/cxl/mem.c
> > +++ b/drivers/cxl/mem.c
> > @@ -119,6 +119,8 @@ static const uuid_t log_uuid[] = {
> >  				 0x07, 0x19, 0x40, 0x3d, 0x86)
> >  };
> >  
> > +static int validate_log_uuid(void __user *payload, size_t size);
> > +
> >  /**
> >   * struct cxl_mem_command - Driver representation of a memory device command
> >   * @info: Command information as it exists for the UAPI
> > @@ -132,6 +134,10 @@ static const uuid_t log_uuid[] = {
> >   *  * %CXL_CMD_INTERNAL_FLAG_PSEUDO: This is a pseudo command which doesn't have
> >   *    a direct mapping to hardware. They are implicitly always enabled.
> >   *
> > + * @validate_payload: A function called after the command is validated but
> > + * before it's sent to the hardware. The primary purpose is to validate, or
> > + * fixup the actual payload.
> > + *
> >   * The cxl_mem_command is the driver's internal representation of commands that
> >   * are supported by the driver. Some of these commands may not be supported by
> >   * the hardware. The driver will use @info to validate the fields passed in by
> > @@ -147,9 +153,11 @@ struct cxl_mem_command {
> >  #define CXL_CMD_INTERNAL_FLAG_HIDDEN BIT(0)
> >  #define CXL_CMD_INTERNAL_FLAG_MANDATORY BIT(1)
> >  #define CXL_CMD_INTERNAL_FLAG_PSEUDO BIT(2)
> > +
> > +	int (*validate_payload)(void __user *payload, size_t size);
> >  };
> >  
> > -#define CXL_CMD(_id, _flags, sin, sout, f)                                     \
> > +#define CXL_CMD_VALIDATE(_id, _flags, sin, sout, f, v)                         \
> >  	[CXL_MEM_COMMAND_ID_##_id] = {                                         \
> >  	.info =	{                                                              \
> >  			.id = CXL_MEM_COMMAND_ID_##_id,                        \
> > @@ -159,8 +167,12 @@ struct cxl_mem_command {
> >  		},                                                             \
> >  	.flags = CXL_CMD_INTERNAL_FLAG_##f,                                    \
> >  	.opcode = CXL_MBOX_OP_##_id,                                           \
> > +	.validate_payload = v,                                                 \
> >  	}
> >  
> > +#define CXL_CMD(_id, _flags, sin, sout, f)                                     \
> > +	CXL_CMD_VALIDATE(_id, _flags, sin, sout, f, NULL)
> > +
> >  /*
> >   * This table defines the supported mailbox commands for the driver. This table
> >   * is made up of a UAPI structure. Non-negative values as parameters in the
> > @@ -176,6 +188,8 @@ static struct cxl_mem_command mem_commands[] = {
> >  	CXL_CMD(GET_PARTITION_INFO, NONE, 0, 0x20, NONE),
> >  	CXL_CMD(GET_LSA, NONE, 0x8, ~0, MANDATORY),
> >  	CXL_CMD(GET_HEALTH_INFO, NONE, 0, 0x12, MANDATORY),
> > +	CXL_CMD_VALIDATE(GET_LOG, MUTEX, 0x18, ~0, MANDATORY,
> > +			 validate_log_uuid),
> >  };
> >  
> >  /*
> > @@ -563,6 +577,13 @@ static int handle_mailbox_cmd_from_user(struct cxl_memdev *cxlmd,
> >  			kvzalloc(cxlm->mbox.payload_size, GFP_KERNEL);
> >  
> >  	if (cmd->info.size_in) {
> > +		if (cmd->validate_payload) {
> > +			rc = cmd->validate_payload(u64_to_user_ptr(in_payload),
> > +						   cmd->info.size_in);
> > +			if (rc)
> > +				goto out;
> > +		}
> > +
> >  		mbox_cmd.payload_in = kvzalloc(cmd->info.size_in, GFP_KERNEL);
> >  		if (!mbox_cmd.payload_in) {
> >  			rc = -ENOMEM;
> > @@ -1205,6 +1226,25 @@ struct cxl_mbox_get_log {
> >  	__le32 length;
> >  } __packed;
> >  
> > +static int validate_log_uuid(void __user *input, size_t size)
> > +{
> > +	struct cxl_mbox_get_log __user *get_log = input;
> > +	uuid_t payload_uuid;
> > +
> > +	if (copy_from_user(&payload_uuid, &get_log->uuid, sizeof(uuid_t)))
> > +		return -EFAULT;
> > +
> > +	/* All unspec'd logs shall taint */
> > +	if (uuid_equal(&payload_uuid, &log_uuid[CEL_UUID]))
> > +		return 0;
> > +	if (uuid_equal(&payload_uuid, &log_uuid[DEBUG_UUID]))
> > +		return 0;
> > +
> > +	add_taint(TAINT_RAW_PASSTHROUGH, LOCKDEP_STILL_OK);
> > +
> > +	return 0;
> > +}
> > +
> >  static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out)
> >  {
> >  	u32 remaining = size;
> > diff --git a/include/uapi/linux/cxl_mem.h b/include/uapi/linux/cxl_mem.h
> > index 766c231d6150..7cdc7f7ce7ec 100644
> > --- a/include/uapi/linux/cxl_mem.h
> > +++ b/include/uapi/linux/cxl_mem.h
> > @@ -39,6 +39,7 @@ extern "C" {
> >  	___C(GET_PARTITION_INFO, "Get Partition Information"),            \
> >  	___C(GET_LSA, "Get Label Storage Area"),                          \
> >  	___C(GET_HEALTH_INFO, "Get Health Info"),                         \
> > +	___C(GET_LOG, "Get Log"),                                         \
> >  	___C(MAX, "Last command")
> >  
> >  #define ___C(a, b) CXL_MEM_COMMAND_ID_##a
> > -- 
> > 2.30.0
> >
Dan Williams Feb. 2, 2021, 11:57 p.m. UTC | #3
On Tue, Feb 2, 2021 at 3:51 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> On 21-02-01 13:28:48, Konrad Rzeszutek Wilk wrote:
> > On Fri, Jan 29, 2021 at 04:24:37PM -0800, Ben Widawsky wrote:
> > > The Get Log command returns the actual log entries that are advertised
> > > via the Get Supported Logs command (0400h). CXL device logs are selected
> > > by UUID which is part of the CXL spec. Because the driver tries to
> > > sanitize what is sent to hardware, there becomes a need to restrict the
> > > types of logs which can be accessed by userspace. For example, the
> > > vendor specific log might only be consumable by proprietary, or offline
> > > applications, and therefore a good candidate for userspace.
> > >
> > > The current driver infrastructure does allow basic validation for all
> > > commands, but doesn't inspect any of the payload data. Along with Get
> > > Log support comes new infrastructure to add a hook for payload
> > > validation. This infrastructure is used to filter out the CEL UUID,
> > > which the userspace driver doesn't have business knowing, and taints on
> > > invalid UUIDs being sent to hardware.
> >
> > Perhaps a better option is to reject invalid UUIDs?
> >
> > And if you really really want to use invalid UUIDs then:
> >
> > 1) Make that code wrapped in CONFIG_CXL_DEBUG_THIS_IS_GOING_TO..?
> >
> > 2) Wrap it with lockdown code so that you can't do this at all
> >    when in LOCKDOWN_INTEGRITY or such?
> >
>
> The commit message needs update btw as CEL is allowed in the latest rev of the
> patches.
>
> We could potentially combine this with the now added (in a branch) CONFIG_RAW
> config option. Indeed I think that makes sense. Dan, thoughts?

Yeah, unknown UUIDs blocking is the same risk as raw commands as a
vendor can trigger any behavior they want. A "CONFIG_RAW depends on
!CONFIG_INTEGRITY" policy sounds reasonable as well.
Ben Widawsky Feb. 3, 2021, 5:16 p.m. UTC | #4
On 21-02-02 15:57:03, Dan Williams wrote:
> On Tue, Feb 2, 2021 at 3:51 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> >
> > On 21-02-01 13:28:48, Konrad Rzeszutek Wilk wrote:
> > > On Fri, Jan 29, 2021 at 04:24:37PM -0800, Ben Widawsky wrote:
> > > > The Get Log command returns the actual log entries that are advertised
> > > > via the Get Supported Logs command (0400h). CXL device logs are selected
> > > > by UUID which is part of the CXL spec. Because the driver tries to
> > > > sanitize what is sent to hardware, there becomes a need to restrict the
> > > > types of logs which can be accessed by userspace. For example, the
> > > > vendor specific log might only be consumable by proprietary, or offline
> > > > applications, and therefore a good candidate for userspace.
> > > >
> > > > The current driver infrastructure does allow basic validation for all
> > > > commands, but doesn't inspect any of the payload data. Along with Get
> > > > Log support comes new infrastructure to add a hook for payload
> > > > validation. This infrastructure is used to filter out the CEL UUID,
> > > > which the userspace driver doesn't have business knowing, and taints on
> > > > invalid UUIDs being sent to hardware.
> > >
> > > Perhaps a better option is to reject invalid UUIDs?
> > >
> > > And if you really really want to use invalid UUIDs then:
> > >
> > > 1) Make that code wrapped in CONFIG_CXL_DEBUG_THIS_IS_GOING_TO..?
> > >
> > > 2) Wrap it with lockdown code so that you can't do this at all
> > >    when in LOCKDOWN_INTEGRITY or such?
> > >
> >
> > The commit message needs update btw as CEL is allowed in the latest rev of the
> > patches.
> >
> > We could potentially combine this with the now added (in a branch) CONFIG_RAW
> > config option. Indeed I think that makes sense. Dan, thoughts?
> 
> Yeah, unknown UUIDs blocking is the same risk as raw commands as a
> vendor can trigger any behavior they want. A "CONFIG_RAW depends on
> !CONFIG_INTEGRITY" policy sounds reasonable as well.

What about LOCKDOWN_NONE though? I think we need something runtime for this.

Can we summarize the CONFIG options here?

CXL_MEM_INSECURE_DEBUG // no change
CXL_MEM_RAW_COMMANDS // if !security_locked_down(LOCKDOWN_NONE)

bool cxl_unsafe()
{
#ifndef CXL_MEM_RAW_COMMANDS
	return false;
#else
	return !security_locked_down(LOCKDOWN_NONE);
#endif
}

---

Did I get that right?
Konrad Rzeszutek Wilk Feb. 3, 2021, 6:14 p.m. UTC | #5
On Wed, Feb 03, 2021 at 09:16:10AM -0800, Ben Widawsky wrote:
> On 21-02-02 15:57:03, Dan Williams wrote:
> > On Tue, Feb 2, 2021 at 3:51 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > >
> > > On 21-02-01 13:28:48, Konrad Rzeszutek Wilk wrote:
> > > > On Fri, Jan 29, 2021 at 04:24:37PM -0800, Ben Widawsky wrote:
> > > > > The Get Log command returns the actual log entries that are advertised
> > > > > via the Get Supported Logs command (0400h). CXL device logs are selected
> > > > > by UUID which is part of the CXL spec. Because the driver tries to
> > > > > sanitize what is sent to hardware, there becomes a need to restrict the
> > > > > types of logs which can be accessed by userspace. For example, the
> > > > > vendor specific log might only be consumable by proprietary, or offline
> > > > > applications, and therefore a good candidate for userspace.
> > > > >
> > > > > The current driver infrastructure does allow basic validation for all
> > > > > commands, but doesn't inspect any of the payload data. Along with Get
> > > > > Log support comes new infrastructure to add a hook for payload
> > > > > validation. This infrastructure is used to filter out the CEL UUID,
> > > > > which the userspace driver doesn't have business knowing, and taints on
> > > > > invalid UUIDs being sent to hardware.
> > > >
> > > > Perhaps a better option is to reject invalid UUIDs?
> > > >
> > > > And if you really really want to use invalid UUIDs then:
> > > >
> > > > 1) Make that code wrapped in CONFIG_CXL_DEBUG_THIS_IS_GOING_TO..?
> > > >
> > > > 2) Wrap it with lockdown code so that you can't do this at all
> > > >    when in LOCKDOWN_INTEGRITY or such?
> > > >
> > >
> > > The commit message needs update btw as CEL is allowed in the latest rev of the
> > > patches.
> > >
> > > We could potentially combine this with the now added (in a branch) CONFIG_RAW
> > > config option. Indeed I think that makes sense. Dan, thoughts?
> > 
> > Yeah, unknown UUIDs blocking is the same risk as raw commands as a
> > vendor can trigger any behavior they want. A "CONFIG_RAW depends on
> > !CONFIG_INTEGRITY" policy sounds reasonable as well.
> 
> What about LOCKDOWN_NONE though? I think we need something runtime for this.
> 
> Can we summarize the CONFIG options here?
> 
> CXL_MEM_INSECURE_DEBUG // no change
> CXL_MEM_RAW_COMMANDS // if !security_locked_down(LOCKDOWN_NONE)
> 
> bool cxl_unsafe()

Would it be better if this inverted? Aka cxl_safe()..
?
> {
> #ifndef CXL_MEM_RAW_COMMANDS
> 	return false;
> #else
> 	return !security_locked_down(LOCKDOWN_NONE);

:thumbsup:

(Naturally this would inverted if this was cxl_safe()).


> #endif
> }
> 
> ---
> 
> Did I get that right?

:nods:
Dan Williams Feb. 3, 2021, 8:31 p.m. UTC | #6
On Wed, Feb 3, 2021 at 10:16 AM Konrad Rzeszutek Wilk
<konrad.wilk@oracle.com> wrote:
>
> On Wed, Feb 03, 2021 at 09:16:10AM -0800, Ben Widawsky wrote:
> > On 21-02-02 15:57:03, Dan Williams wrote:
> > > On Tue, Feb 2, 2021 at 3:51 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > > >
> > > > On 21-02-01 13:28:48, Konrad Rzeszutek Wilk wrote:
> > > > > On Fri, Jan 29, 2021 at 04:24:37PM -0800, Ben Widawsky wrote:
> > > > > > The Get Log command returns the actual log entries that are advertised
> > > > > > via the Get Supported Logs command (0400h). CXL device logs are selected
> > > > > > by UUID which is part of the CXL spec. Because the driver tries to
> > > > > > sanitize what is sent to hardware, there becomes a need to restrict the
> > > > > > types of logs which can be accessed by userspace. For example, the
> > > > > > vendor specific log might only be consumable by proprietary, or offline
> > > > > > applications, and therefore a good candidate for userspace.
> > > > > >
> > > > > > The current driver infrastructure does allow basic validation for all
> > > > > > commands, but doesn't inspect any of the payload data. Along with Get
> > > > > > Log support comes new infrastructure to add a hook for payload
> > > > > > validation. This infrastructure is used to filter out the CEL UUID,
> > > > > > which the userspace driver doesn't have business knowing, and taints on
> > > > > > invalid UUIDs being sent to hardware.
> > > > >
> > > > > Perhaps a better option is to reject invalid UUIDs?
> > > > >
> > > > > And if you really really want to use invalid UUIDs then:
> > > > >
> > > > > 1) Make that code wrapped in CONFIG_CXL_DEBUG_THIS_IS_GOING_TO..?
> > > > >
> > > > > 2) Wrap it with lockdown code so that you can't do this at all
> > > > >    when in LOCKDOWN_INTEGRITY or such?
> > > > >
> > > >
> > > > The commit message needs update btw as CEL is allowed in the latest rev of the
> > > > patches.
> > > >
> > > > We could potentially combine this with the now added (in a branch) CONFIG_RAW
> > > > config option. Indeed I think that makes sense. Dan, thoughts?
> > >
> > > Yeah, unknown UUIDs blocking is the same risk as raw commands as a
> > > vendor can trigger any behavior they want. A "CONFIG_RAW depends on
> > > !CONFIG_INTEGRITY" policy sounds reasonable as well.
> >
> > What about LOCKDOWN_NONE though? I think we need something runtime for this.
> >
> > Can we summarize the CONFIG options here?
> >
> > CXL_MEM_INSECURE_DEBUG // no change
> > CXL_MEM_RAW_COMMANDS // if !security_locked_down(LOCKDOWN_NONE)
> >
> > bool cxl_unsafe()
>
> Would it be better if this inverted? Aka cxl_safe()..
> ?
> > {
> > #ifndef CXL_MEM_RAW_COMMANDS

nit use IS_ENABLED() if this function lives in a C file, or provide
whole alternate static inline versions in a header gated by ifdefs.

> >       return false;
> > #else
> >       return !security_locked_down(LOCKDOWN_NONE);
>
> :thumbsup:
>
> (Naturally this would inverted if this was cxl_safe()).
>
>
> > #endif
> > }
> >
> > ---
> >
> > Did I get that right?
>
> :nods:

Looks good which means it's time to bikeshed the naming. I'd call it
cxl_raw_allowed(). As "safety" isn't the only reason for blocking raw,
it's also to corral the userspace api. I.e. things like enforcing
security passphrase material through the Linux keys api.
Ben Widawsky Feb. 4, 2021, 6:55 p.m. UTC | #7
On 21-02-03 12:31:00, Dan Williams wrote:
> On Wed, Feb 3, 2021 at 10:16 AM Konrad Rzeszutek Wilk
> <konrad.wilk@oracle.com> wrote:
> >
> > On Wed, Feb 03, 2021 at 09:16:10AM -0800, Ben Widawsky wrote:
> > > On 21-02-02 15:57:03, Dan Williams wrote:
> > > > On Tue, Feb 2, 2021 at 3:51 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > > > >
> > > > > On 21-02-01 13:28:48, Konrad Rzeszutek Wilk wrote:
> > > > > > On Fri, Jan 29, 2021 at 04:24:37PM -0800, Ben Widawsky wrote:
> > > > > > > The Get Log command returns the actual log entries that are advertised
> > > > > > > via the Get Supported Logs command (0400h). CXL device logs are selected
> > > > > > > by UUID which is part of the CXL spec. Because the driver tries to
> > > > > > > sanitize what is sent to hardware, there becomes a need to restrict the
> > > > > > > types of logs which can be accessed by userspace. For example, the
> > > > > > > vendor specific log might only be consumable by proprietary, or offline
> > > > > > > applications, and therefore a good candidate for userspace.
> > > > > > >
> > > > > > > The current driver infrastructure does allow basic validation for all
> > > > > > > commands, but doesn't inspect any of the payload data. Along with Get
> > > > > > > Log support comes new infrastructure to add a hook for payload
> > > > > > > validation. This infrastructure is used to filter out the CEL UUID,
> > > > > > > which the userspace driver doesn't have business knowing, and taints on
> > > > > > > invalid UUIDs being sent to hardware.
> > > > > >
> > > > > > Perhaps a better option is to reject invalid UUIDs?
> > > > > >
> > > > > > And if you really really want to use invalid UUIDs then:
> > > > > >
> > > > > > 1) Make that code wrapped in CONFIG_CXL_DEBUG_THIS_IS_GOING_TO..?
> > > > > >
> > > > > > 2) Wrap it with lockdown code so that you can't do this at all
> > > > > >    when in LOCKDOWN_INTEGRITY or such?
> > > > > >
> > > > >
> > > > > The commit message needs update btw as CEL is allowed in the latest rev of the
> > > > > patches.
> > > > >
> > > > > We could potentially combine this with the now added (in a branch) CONFIG_RAW
> > > > > config option. Indeed I think that makes sense. Dan, thoughts?
> > > >
> > > > Yeah, unknown UUIDs blocking is the same risk as raw commands as a
> > > > vendor can trigger any behavior they want. A "CONFIG_RAW depends on
> > > > !CONFIG_INTEGRITY" policy sounds reasonable as well.
> > >
> > > What about LOCKDOWN_NONE though? I think we need something runtime for this.
> > >
> > > Can we summarize the CONFIG options here?
> > >
> > > CXL_MEM_INSECURE_DEBUG // no change
> > > CXL_MEM_RAW_COMMANDS // if !security_locked_down(LOCKDOWN_NONE)
> > >
> > > bool cxl_unsafe()
> >
> > Would it be better if this inverted? Aka cxl_safe()..
> > ?
> > > {
> > > #ifndef CXL_MEM_RAW_COMMANDS
> 
> nit use IS_ENABLED() if this function lives in a C file, or provide
> whole alternate static inline versions in a header gated by ifdefs.
> 

I had done this independently since... but agreed.

> > >       return false;
> > > #else
> > >       return !security_locked_down(LOCKDOWN_NONE);
> >
> > :thumbsup:
> >
> > (Naturally this would inverted if this was cxl_safe()).
> >
> >
> > > #endif
> > > }
> > >
> > > ---
> > >
> > > Did I get that right?
> >
> > :nods:
> 
> Looks good which means it's time to bikeshed the naming. I'd call it
> cxl_raw_allowed(). As "safety" isn't the only reason for blocking raw,
> it's also to corral the userspace api. I.e. things like enforcing
> security passphrase material through the Linux keys api.

It actually got pushed into cxl_mem_raw_command_allowed()

static bool cxl_mem_raw_command_allowed(u16 opcode)
{
        int i;

        if (!IS_ENABLED(CONFIG_CXL_MEM_RAW_COMMANDS))
                return false;

        if (security_locked_down(LOCKDOWN_NONE))
                return false;

        if (raw_allow_all)
                return true;

        if (is_security_command(opcode))
                return false;

        for (i = 0; i < ARRAY_SIZE(disabled_raw_commands); i++)
                if (disabled_raw_commands[i] == opcode)
                        return false;

        return true;
}

That work for you?
Dan Williams Feb. 4, 2021, 9:01 p.m. UTC | #8
On Thu, Feb 4, 2021 at 10:56 AM Ben Widawsky <ben.widawsky@intel.com> wrote:
[..]
> It actually got pushed into cxl_mem_raw_command_allowed()
>
> static bool cxl_mem_raw_command_allowed(u16 opcode)
> {
>         int i;
>
>         if (!IS_ENABLED(CONFIG_CXL_MEM_RAW_COMMANDS))
>                 return false;
>
>         if (security_locked_down(LOCKDOWN_NONE))
>                 return false;
>
>         if (raw_allow_all)
>                 return true;
>
>         if (is_security_command(opcode))
>                 return false;
>
>         for (i = 0; i < ARRAY_SIZE(disabled_raw_commands); i++)
>                 if (disabled_raw_commands[i] == opcode)
>                         return false;
>
>         return true;
> }
>
> That work for you?

Looks good to me.
diff mbox series

Patch

diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index b8ca6dff37b5..086268f1dd6c 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -119,6 +119,8 @@  static const uuid_t log_uuid[] = {
 				 0x07, 0x19, 0x40, 0x3d, 0x86)
 };
 
+static int validate_log_uuid(void __user *payload, size_t size);
+
 /**
  * struct cxl_mem_command - Driver representation of a memory device command
  * @info: Command information as it exists for the UAPI
@@ -132,6 +134,10 @@  static const uuid_t log_uuid[] = {
  *  * %CXL_CMD_INTERNAL_FLAG_PSEUDO: This is a pseudo command which doesn't have
  *    a direct mapping to hardware. They are implicitly always enabled.
  *
+ * @validate_payload: A function called after the command is validated but
+ * before it's sent to the hardware. The primary purpose is to validate, or
+ * fixup the actual payload.
+ *
  * The cxl_mem_command is the driver's internal representation of commands that
  * are supported by the driver. Some of these commands may not be supported by
  * the hardware. The driver will use @info to validate the fields passed in by
@@ -147,9 +153,11 @@  struct cxl_mem_command {
 #define CXL_CMD_INTERNAL_FLAG_HIDDEN BIT(0)
 #define CXL_CMD_INTERNAL_FLAG_MANDATORY BIT(1)
 #define CXL_CMD_INTERNAL_FLAG_PSEUDO BIT(2)
+
+	int (*validate_payload)(void __user *payload, size_t size);
 };
 
-#define CXL_CMD(_id, _flags, sin, sout, f)                                     \
+#define CXL_CMD_VALIDATE(_id, _flags, sin, sout, f, v)                         \
 	[CXL_MEM_COMMAND_ID_##_id] = {                                         \
 	.info =	{                                                              \
 			.id = CXL_MEM_COMMAND_ID_##_id,                        \
@@ -159,8 +167,12 @@  struct cxl_mem_command {
 		},                                                             \
 	.flags = CXL_CMD_INTERNAL_FLAG_##f,                                    \
 	.opcode = CXL_MBOX_OP_##_id,                                           \
+	.validate_payload = v,                                                 \
 	}
 
+#define CXL_CMD(_id, _flags, sin, sout, f)                                     \
+	CXL_CMD_VALIDATE(_id, _flags, sin, sout, f, NULL)
+
 /*
  * This table defines the supported mailbox commands for the driver. This table
  * is made up of a UAPI structure. Non-negative values as parameters in the
@@ -176,6 +188,8 @@  static struct cxl_mem_command mem_commands[] = {
 	CXL_CMD(GET_PARTITION_INFO, NONE, 0, 0x20, NONE),
 	CXL_CMD(GET_LSA, NONE, 0x8, ~0, MANDATORY),
 	CXL_CMD(GET_HEALTH_INFO, NONE, 0, 0x12, MANDATORY),
+	CXL_CMD_VALIDATE(GET_LOG, MUTEX, 0x18, ~0, MANDATORY,
+			 validate_log_uuid),
 };
 
 /*
@@ -563,6 +577,13 @@  static int handle_mailbox_cmd_from_user(struct cxl_memdev *cxlmd,
 			kvzalloc(cxlm->mbox.payload_size, GFP_KERNEL);
 
 	if (cmd->info.size_in) {
+		if (cmd->validate_payload) {
+			rc = cmd->validate_payload(u64_to_user_ptr(in_payload),
+						   cmd->info.size_in);
+			if (rc)
+				goto out;
+		}
+
 		mbox_cmd.payload_in = kvzalloc(cmd->info.size_in, GFP_KERNEL);
 		if (!mbox_cmd.payload_in) {
 			rc = -ENOMEM;
@@ -1205,6 +1226,25 @@  struct cxl_mbox_get_log {
 	__le32 length;
 } __packed;
 
+static int validate_log_uuid(void __user *input, size_t size)
+{
+	struct cxl_mbox_get_log __user *get_log = input;
+	uuid_t payload_uuid;
+
+	if (copy_from_user(&payload_uuid, &get_log->uuid, sizeof(uuid_t)))
+		return -EFAULT;
+
+	/* All unspec'd logs shall taint */
+	if (uuid_equal(&payload_uuid, &log_uuid[CEL_UUID]))
+		return 0;
+	if (uuid_equal(&payload_uuid, &log_uuid[DEBUG_UUID]))
+		return 0;
+
+	add_taint(TAINT_RAW_PASSTHROUGH, LOCKDEP_STILL_OK);
+
+	return 0;
+}
+
 static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out)
 {
 	u32 remaining = size;
diff --git a/include/uapi/linux/cxl_mem.h b/include/uapi/linux/cxl_mem.h
index 766c231d6150..7cdc7f7ce7ec 100644
--- a/include/uapi/linux/cxl_mem.h
+++ b/include/uapi/linux/cxl_mem.h
@@ -39,6 +39,7 @@  extern "C" {
 	___C(GET_PARTITION_INFO, "Get Partition Information"),            \
 	___C(GET_LSA, "Get Label Storage Area"),                          \
 	___C(GET_HEALTH_INFO, "Get Health Info"),                         \
+	___C(GET_LOG, "Get Log"),                                         \
 	___C(MAX, "Last command")
 
 #define ___C(a, b) CXL_MEM_COMMAND_ID_##a