Hi,

replying to sashiko

>commit ee93b9620650bb02f494ab88ef4e18464c95770f
>Author: Gregor Herburger <[email protected]>
>nvmem: Add the Raspberry Pi OTP driver
>This commit adds a new nvmem driver to support OTP registers on Raspberry Pi
>platforms. It accesses the OTP memory through the videocore firmware
>interface.
>> diff --git a/drivers/nvmem/raspberrypi-otp.c 
>> b/drivers/nvmem/raspberrypi-otp.c
>> new file mode 100644
>> index 0000000000000..1edf2d5538d04
>> --- /dev/null
>> +++ b/drivers/nvmem/raspberrypi-otp.c
>> @@ -0,0 +1,120 @@
>[ ... ]
>> +struct rpi_otp_priv {
>> +    struct rpi_firmware *fw;
>> +    struct device *dev;
>> +    u32 read_tag;
>> +    u32 write_tag;
>> +};
>> +
>> +struct rpi_otp_header {
>> +    __le32 start;
>> +    __le32 count;
>> +    __le32 data[] __counted_by(count);
>Is the __counted_by attribute safe to use with explicitly endian types?
>On big-endian architectures (which are supported via COMPILE_TEST), the
>native bounds checking logic will evaluate the byte-swapped __le32 value as a
>massive integer (e.g., a count of 1 evaluates to 16777216).
>Does this unintentionally bypass the bounds checking defenses?

Yes that is right on compile tests on big-endian it will bypass the run time
check. I can use the __counted_by_le() macro to disable it on big-endian and
keep it on little-endian.

>> +};
>> +
>> +static int rpi_otp_read(void *context, unsigned int offset, void *buf, 
>> size_t bytes)
>> +{
>> +    struct rpi_otp_priv *priv = context;
>> +    struct rpi_otp_header *fwbuf;
>> +    u32 count = bytes / 4;
>> +    int ret;
>> +
>> +    fwbuf = kzalloc(struct_size(fwbuf, data, count), GFP_KERNEL);
>Could an unaligned length request cause an out-of-bounds write here?
>If a caller requests an unaligned length (e.g., bytes = 3), count truncates
>to 0, allocating an 8-byte structure.

I first thought that alignment is checked in the nvmem system (with
nvmem_device::stride and nvmem_device::word_size)  but that is only true for
sysfs accesses. The nvmem_device_read/nvmem_device_write functions call directly
into the callback function of the driver. I guess there should be an alignment
check in this functions as well.

For now I will add an alignment check into my functions.

>> +    if (!fwbuf)
>> +            return -ENOMEM;
>> +
>> +    fwbuf->start = cpu_to_le32(offset / 4);
>> +    fwbuf->count = cpu_to_le32(count);
>> +
>> +    ret = rpi_firmware_property(priv->fw, priv->read_tag, fwbuf,
>> +                                sizeof(struct rpi_otp_header) + bytes);
>> +    if (ret)
>> +            goto out;
>> +
>> +    memcpy(buf, fwbuf->data, bytes);
>With an unaligned bytes request, would this memcpy write past the bounds of
>the 0-count fwbuf array? 
>Should there be an explicit alignment check for bytes and offset at the start
>of the function?

Yes see above.

>> +
>> +out:
>> +    kfree(fwbuf);
>> +    return ret;
>> +}
>> +
>> +static int rpi_otp_write(void *context, unsigned int offset, void *val, 
>> size_t bytes)
>> +{
>> +    struct rpi_otp_priv *priv = context;
>> +    struct rpi_otp_header *fwbuf;
>> +    u32 count = bytes / 4;
>> +    int ret;
>> +
>> +    fwbuf = kzalloc(struct_size(fwbuf, data, count), GFP_KERNEL);
>> +    if (!fwbuf)
>> +            return -ENOMEM;
>> +
>> +    fwbuf->start = cpu_to_le32(offset / 4);
>> +    fwbuf->count = cpu_to_le32(count);
>> +    memcpy(fwbuf->data, val, bytes);
>Similarly, could this memcpy write out-of-bounds if bytes is not aligned to
>a 4-byte boundary?

Yes see above.

>> +
>> +    ret = rpi_firmware_property(priv->fw, priv->write_tag, fwbuf,
>> +                                sizeof(struct rpi_otp_header) + bytes);
>> +
>> +    kfree(fwbuf);
>> +    return ret;
>> +}
>> +
>> +static int rpi_otp_probe(struct platform_device *pdev)
>> +{
>> +    struct device *dev = &pdev->dev;
>> +    struct nvmem_device *nvmem;
>> +    struct rpi_otp_priv *priv;
>> +    const struct rpi_otp_driver_data *data;
>> +    struct nvmem_config config = {
>> +            .read_only = false,
>> +            .word_size = 4,
>> +            .stride = 4,
>> +            .reg_read = rpi_otp_read,
>> +            .reg_write = rpi_otp_write,
>> +            .id = NVMEM_DEVID_NONE,
>> +    };
>> +
>> +    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> +    if (!priv)
>> +            return -ENOMEM;
>> +
>> +    data = dev_get_platdata(dev);
>> +    if (!data)
>> +            return -ENODEV;
>> +
>> +    priv->fw = dev_get_drvdata(dev->parent);
>> +    priv->dev = dev;
>> +    priv->read_tag = data->read_tag;
>> +    priv->write_tag = data->write_tag;
>> +    config.dev = dev;
>> +    config.priv = priv;
>> +    config.name = data->name;
>> +    config.size = data->size;
>> +
>> +    nvmem = devm_nvmem_register(dev, &config);
>Does this devres usage introduce a use-after-free risk during device unbind?
>If an in-kernel consumer holds a reference to the nvmem device,
>devm_nvmem_unregister() drops its refcount but returns while the consumer's
>reference keeps the device alive.
>The devres framework will then proceed to free the priv structure.
>If the consumer subsequently calls nvmem_device_read(), will it dereference
>the freed priv pointer inside rpi_otp_read()?

Wow good catch. This took me a while to understand but i guess it can indeed
cause an use-after-free error error for in-kernel consumers. I will look into
it.

Reply via email to