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

From: Christian Loehle
Date: Tue May 16 2023 - 11:37:40 EST




-----Original Message-----
From: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
Sent: Monday, May 15, 2023 3:56 PM
To: Christian Loehle <CLoehle@xxxxxxxxxxxxxx>
Cc: Adrian Hunter <adrian.hunter@xxxxxxxxx>; linux-mmc@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; Avri Altman <avri.altman@xxxxxxx>
Subject: Re: [PATCH 3/3] mmc: block: ioctl: Add error aggregation flag

>>
>> 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.

I'll retest, it was mostly about the new bit with e.g. a switch command to be more
explicit, it doesn't actually break anything to enter, but to emphasize
that write_flag != 0 does not imply idata->buf, which is counter-intuitive.
Will rework in v2

>
>> 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?
>
Strictly speaking no, i chose it for three reasons:
a) it's more flexible, don't have to hardcode some operations
b) it doesn't change current userspace interface, acts like a versioning of the ioctl interface.
c) it's easy to check if userspace makes use of it, one could even think of adding a WARN_ON if e.g.
sanitize is called without MMC_AGGREGATE_PROG_ERRORS as it might create a false sense of security -> is insecure.


>> 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.
It doesn't affect mmc-utils and if we consider it the only client that's fair, but with thoughts about removing the flag
it felt a bit off tbh. The status and ready for data field will of course be non-sense. (I think I will overwrite the status fields
with the last polled status and only aggregate the error flags, to at least accomodate for that.)

>> + */
>> + 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.
Makes sense.

>> /* 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 */

So summarizing I will change:
- use __mmc_poll_for_busy with custom mmc_blk_busy_cb to aggregate error flags
- set non-error flags to last polled CMD13.
- put everything in resp[0]
- enable for all with write_flag != 0 or R1b response
- (use early return for spi write case)

Thanks to the both of you for taking a look at it.

Regards,
Christian

Hyperstone GmbH | Reichenaustr. 39a | 78467 Konstanz
Managing Director: Dr. Jan Peter Berns.
Commercial register of local courts: Freiburg HRB381782