Re: [PATCH 3/3] mmc: block: ioctl: Add error aggregation flag

From: Ulf Hansson
Date: Mon May 15 2023 - 09:56:54 EST


On Wed, 5 Apr 2023 at 13:57, Christian Löhle <CLoehle@xxxxxxxxxxxxxx> wrote:
>
> Userspace currently has no way of checking for error bits of
> detection mode X. These are error bits that are only detected by
> the card when executing the command. For e.g. a sanitize operation
> this may be minutes after the RSP was seen by the host.
>
> Currently userspace programs cannot see these error bits reliably.
> They could issue a multi ioctl cmd with a CMD13 immediately following
> it, but since errors of detection mode X are automatically cleared
> (they are all clear condition B).
> mmc_poll_for_busy of the first ioctl may have already hidden such an
> error flag.
>
> In case of the security operations: sanitize, secure erases and
> RPMB writes, this could lead to the operation not being performed
> successfully by the card with the user not knowing.
> If the user trusts that this operation is completed
> (e.g. their data is sanitized), this could be a security issue.
> An attacker could e.g. provoke a eMMC (VCC) flash fail, where a
> successful sanitize of a card is not possible. A card may move out
> of PROG state but issue a bit 19 R1 error.
>
> This patch therefore will also have the consequence of a mmc-utils
> patch, which enables the bit for the security-sensitive operations.
>
> Signed-off-by: Christian Loehle <cloehle@xxxxxxxxxxxxxx>
> ---
> drivers/mmc/core/block.c | 34 ++++++++++++++++++++++++++++++++--
> include/uapi/linux/mmc/ioctl.h | 2 ++
> 2 files changed, 34 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
> index 35ff7101cbb1..386a508bd720 100644
> --- a/drivers/mmc/core/block.c
> +++ b/drivers/mmc/core/block.c
> @@ -457,7 +457,7 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr,
> sizeof(ic->response)))
> return -EFAULT;
>
> - if (!idata->ic.write_flag) {
> + if (!idata->ic.write_flag && idata->buf) {

If needed, this looks like it should be a separate change.

> if (copy_to_user((void __user *)(unsigned long)ic->data_ptr,
> idata->buf, idata->buf_bytes))
> return -EFAULT;
> @@ -610,13 +610,43 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
> usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us);
>
> /* No need to poll when using HW busy detection. */
> - if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
> + if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp &&
> + !(idata->ic.write_flag & MMC_AGGREGATE_PROG_ERRORS))

Do we really need a new flag for this? Can't we just collect the error
code always for writes? Or collect the errors based upon a selection
of commands?

> return 0;
>
> if (mmc_host_is_spi(card->host)) {
> if (idata->ic.write_flag)
> err = mmc_spi_err_check(card);
> }
> + /*
> + * We want to receive a meaningful R1 response for errors of detection
> + * type X, which are only set after the card has executed the command.
> + * In that case poll CMD13 until PROG is left and return the
> + * accumulated error bits.
> + * If we're lucky host controller has busy detection for R1B and
> + * this is just a single CMD13.
> + * We can abuse resp[1] as the post-PROG R1 here, as all commands
> + * that go through PRG have an R1 response and therefore only
> + * use resp[0].

Hmm, for these cases, is the resp[0] containing any interesting
information? Probably not, right?

In that case, why not override the resp[0], this should make the
behaviour more consistent from user space point of view.

> + */
> + else if (idata->ic.write_flag & MMC_AGGREGATE_PROG_ERRORS) {
> + unsigned long timeout = jiffies +
> + msecs_to_jiffies(busy_timeout_ms);
> + bool done = false;
> + unsigned long delay_ms = 1;
> + u32 status;
> +
> + do {
> + done = time_after(jiffies, timeout);
> + msleep(delay_ms++);
> + err = __mmc_send_status(card, &status, 1);
> + if (err)
> + return err;
> + idata->ic.response[1] |= status;
> + } while (R1_CURRENT_STATE(status) != R1_STATE_TRAN && !done);
> + if (done)
> + return -ETIMEDOUT;
> + }

We have moved away from open-coding polling loops. Let's not introduce
a new one. In fact, it looks a bit like we could re-use the
mmc_blk_busy_cb() for this, as it seems to be collecting the error
codes too.

In any case, let's at least make use of __mmc_poll_for_busy() to avoid
the open-coding.

> /* Ensure RPMB/R1B command has completed by polling with CMD13. */
> else if (idata->rpmb || r1b_resp)
> err = mmc_poll_for_busy(card, busy_timeout_ms, false,
> diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h
> index b2ff7f5be87b..a9d84ae8d57d 100644
> --- a/include/uapi/linux/mmc/ioctl.h
> +++ b/include/uapi/linux/mmc/ioctl.h
> @@ -8,9 +8,11 @@
> struct mmc_ioc_cmd {
> /*
> * Direction of data: nonzero = write, zero = read.
> + * Bit 30 selects error aggregation post-PROG state.
> * Bit 31 selects 'Reliable Write' for RPMB.
> */
> int write_flag;
> +#define MMC_AGGREGATE_PROG_ERRORS (1 << 30)
> #define MMC_RPMB_RELIABLE_WRITE (1 << 31)
>
> /* Application-specific command. true = precede with CMD55 */

Kind regards
Uffe