diff mbox series

XDMA crashes on zero-length sg entries

Message ID 529849b0-2ba9-85bf-c57f-0abe93cfdc42@gpxsee.org (mailing list archive)
State Changes Requested
Headers show
Series XDMA crashes on zero-length sg entries | expand

Commit Message

Martin Tůma March 16, 2023, 5:03 p.m. UTC
Hi,
The Xilinx XDMA driver crashes when the scatterlist provided to
xdma_prep_device_sg() contains an empty entry, i.e. sg_dma_len()
returns zero. As I do get such sgl from v4l2 I suppose that this
is a valid scenario and not a bug in our parent mgb4 driver. Also
the documentation for sg_dma_len() suggests that there may be
zero-length entries.

The following trivial patch fixes the crash:

                         len = min_t(u32, rest, XDMA_DESC_BLEN_MAX);

M.

Comments

Lizhi Hou March 16, 2023, 6:25 p.m. UTC | #1
Hi Martin,

Based on the dmaengine document

https://kernel.org/doc/html/v6.3-rc2/driver-api/dmaengine/client.html

The sg_len used for dmaengine_prep_slave_sg should be returned by 
dma_map_sg.

This implies there should not be zero segment for the first sg_len segments.

I think that is why we need to provide 'sg_len' for 
dmaengine_prep_slave_sg().

With 'sg_len', we do not need to worry about zero length sg in the 
driver callback.


Thanks,

Lizhi

On 3/16/23 10:03, Martin Tůma wrote:
> Hi,
> The Xilinx XDMA driver crashes when the scatterlist provided to
> xdma_prep_device_sg() contains an empty entry, i.e. sg_dma_len()
> returns zero. As I do get such sgl from v4l2 I suppose that this
> is a valid scenario and not a bug in our parent mgb4 driver. Also
> the documentation for sg_dma_len() suggests that there may be
> zero-length entries.
>
> The following trivial patch fixes the crash:
>
> diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c
> index 462109c61653..cd5fcd911c50 100644
> --- a/drivers/dma/xilinx/xdma.c
> +++ b/drivers/dma/xilinx/xdma.c
> @@ -487,6 +487,8 @@ xdma_prep_device_sg(struct dma_chan *chan, struct 
> scatterlist *sgl,
>         for_each_sg(sgl, sg, sg_len, i) {
>                 addr = sg_dma_address(sg);
>                 rest = sg_dma_len(sg);
> +               if (!rest)
> +                       break;
>
>                 do {
>                         len = min_t(u32, rest, XDMA_DESC_BLEN_MAX);
>
> M.
Martin Tůma March 17, 2023, 11:18 a.m. UTC | #2
Hi

On 16. 03. 23 19:25, Lizhi Hou wrote:
> Hi Martin,
> 
> Based on the dmaengine document
> 
> https://kernel.org/doc/html/v6.3-rc2/driver-api/dmaengine/client.html
> 
> The sg_len used for dmaengine_prep_slave_sg should be returned by 
> dma_map_sg.
> 
> This implies there should not be zero segment for the first sg_len 
> segments.
> 

I do get the mapping from vb2_dma_sg_plane_desc() which internally
uses dma_map_sg_attrs() and yet in some special cases sg_dma_len()
returns zero. So this is a "real-life" scenario. I'm still investigating
what the root cause is for getting such empty mappings from v4l2, but
the DMA driver should IMHO not crash the kernel in such case anyway.
I have looked into some randomly chosen kernel code that uses
sg_dma_len() and all usages that I have seen are prepared for the case
the length is zero. All but one driver simply stop the iteration while
the remaining one reported this case as an error.

M.

> I think that is why we need to provide 'sg_len' for 
> dmaengine_prep_slave_sg().
> 
> With 'sg_len', we do not need to worry about zero length sg in the 
> driver callback.
> 
> 
> Thanks,
> 
> Lizhi
> 
> On 3/16/23 10:03, Martin Tůma wrote:
>> Hi,
>> The Xilinx XDMA driver crashes when the scatterlist provided to
>> xdma_prep_device_sg() contains an empty entry, i.e. sg_dma_len()
>> returns zero. As I do get such sgl from v4l2 I suppose that this
>> is a valid scenario and not a bug in our parent mgb4 driver. Also
>> the documentation for sg_dma_len() suggests that there may be
>> zero-length entries.
>>
>> The following trivial patch fixes the crash:
>>
>> diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c
>> index 462109c61653..cd5fcd911c50 100644
>> --- a/drivers/dma/xilinx/xdma.c
>> +++ b/drivers/dma/xilinx/xdma.c
>> @@ -487,6 +487,8 @@ xdma_prep_device_sg(struct dma_chan *chan, struct 
>> scatterlist *sgl,
>>         for_each_sg(sgl, sg, sg_len, i) {
>>                 addr = sg_dma_address(sg);
>>                 rest = sg_dma_len(sg);
>> +               if (!rest)
>> +                       break;
>>
>>                 do {
>>                         len = min_t(u32, rest, XDMA_DESC_BLEN_MAX);
>>
>> M.
Lizhi Hou March 17, 2023, 5:19 p.m. UTC | #3
Hi Martin,

On 3/17/23 04:18, Martin Tůma wrote:
> Hi
>
> On 16. 03. 23 19:25, Lizhi Hou wrote:
>> Hi Martin,
>>
>> Based on the dmaengine document
>>
>> https://kernel.org/doc/html/v6.3-rc2/driver-api/dmaengine/client.html
>>
>> The sg_len used for dmaengine_prep_slave_sg should be returned by 
>> dma_map_sg.
>>
>> This implies there should not be zero segment for the first sg_len 
>> segments.
>>
>
> I do get the mapping from vb2_dma_sg_plane_desc() which internally
> uses dma_map_sg_attrs() and yet in some special cases sg_dma_len()
> returns zero. So this is a "real-life" scenario. I'm still investigating
This sounds odd to me. dma_map_sg() is supposed to return number of 
Non-zero sg because the zero length sg will be merged.
> what the root cause is for getting such empty mappings from v4l2, but
> the DMA driver should IMHO not crash the kernel in such case anyway.

I checked some dma driver and I did not see obvious code to check the sg 
len. e.g.

https://elixir.bootlin.com/linux/v6.3-rc2/source/drivers/dma/k3dma.c#L531

> I have looked into some randomly chosen kernel code that uses
> sg_dma_len() and all usages that I have seen are prepared for the case
> the length is zero. All but one driver simply stop the iteration while
> the remaining one reported this case as an error.

Are those dma drivers?

Adding a check sounds fine to me. And I am going to create a patch 
anyway.  :)


Thanks,

Lizhi

>
> M.
>
>> I think that is why we need to provide 'sg_len' for 
>> dmaengine_prep_slave_sg().
>>
>> With 'sg_len', we do not need to worry about zero length sg in the 
>> driver callback.
>>
>>
>> Thanks,
>>
>> Lizhi
>>
>> On 3/16/23 10:03, Martin Tůma wrote:
>>> Hi,
>>> The Xilinx XDMA driver crashes when the scatterlist provided to
>>> xdma_prep_device_sg() contains an empty entry, i.e. sg_dma_len()
>>> returns zero. As I do get such sgl from v4l2 I suppose that this
>>> is a valid scenario and not a bug in our parent mgb4 driver. Also
>>> the documentation for sg_dma_len() suggests that there may be
>>> zero-length entries.
>>>
>>> The following trivial patch fixes the crash:
>>>
>>> diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c
>>> index 462109c61653..cd5fcd911c50 100644
>>> --- a/drivers/dma/xilinx/xdma.c
>>> +++ b/drivers/dma/xilinx/xdma.c
>>> @@ -487,6 +487,8 @@ xdma_prep_device_sg(struct dma_chan *chan, 
>>> struct scatterlist *sgl,
>>>         for_each_sg(sgl, sg, sg_len, i) {
>>>                 addr = sg_dma_address(sg);
>>>                 rest = sg_dma_len(sg);
>>> +               if (!rest)
>>> +                       break;
>>>
>>>                 do {
>>>                         len = min_t(u32, rest, XDMA_DESC_BLEN_MAX);
>>>
>>> M.
>
Martin Tůma March 17, 2023, 5:52 p.m. UTC | #4
Hi,

On 17. 03. 23 18:19, Lizhi Hou wrote:
> Hi Martin,
> 
> On 3/17/23 04:18, Martin Tůma wrote:
>> Hi
>>
>> On 16. 03. 23 19:25, Lizhi Hou wrote:
>>> Hi Martin,
>>>
>>> Based on the dmaengine document
>>>
>>> https://kernel.org/doc/html/v6.3-rc2/driver-api/dmaengine/client.html
>>>
>>> The sg_len used for dmaengine_prep_slave_sg should be returned by 
>>> dma_map_sg.
>>>
>>> This implies there should not be zero segment for the first sg_len 
>>> segments.
>>>
>>
>> I do get the mapping from vb2_dma_sg_plane_desc() which internally
>> uses dma_map_sg_attrs() and yet in some special cases sg_dma_len()
>> returns zero. So this is a "real-life" scenario. I'm still investigating
> This sounds odd to me. dma_map_sg() is supposed to return number of 
> Non-zero sg because the zero length sg will be merged.
>> what the root cause is for getting such empty mappings from v4l2, but
>> the DMA driver should IMHO not crash the kernel in such case anyway.
> 
> I checked some dma driver and I did not see obvious code to check the sg 
> len. e.g.
> 
> https://elixir.bootlin.com/linux/v6.3-rc2/source/drivers/dma/k3dma.c#L531
> 

Didn't examinate this one in detail, but I don't think this one would
crash either. In your case, the problem is, that you do not pre-allocate
the descriptor in case sg_dma_len(sg) is zero and then access it in:

desc->bytes = cpu_to_le32(len);

which is the exact line where xdma crashes.

M.

>> I have looked into some randomly chosen kernel code that uses
>> sg_dma_len() and all usages that I have seen are prepared for the case
>> the length is zero. All but one driver simply stop the iteration while
>> the remaining one reported this case as an error.
> 
> Are those dma drivers?
> 
> Adding a check sounds fine to me. And I am going to create a patch 
> anyway.  :)
> 
> 
> Thanks,
> 
> Lizhi
> 
>>
>> M.
>>
>>> I think that is why we need to provide 'sg_len' for 
>>> dmaengine_prep_slave_sg().
>>>
>>> With 'sg_len', we do not need to worry about zero length sg in the 
>>> driver callback.
>>>
>>>
>>> Thanks,
>>>
>>> Lizhi
>>>
>>> On 3/16/23 10:03, Martin Tůma wrote:
>>>> Hi,
>>>> The Xilinx XDMA driver crashes when the scatterlist provided to
>>>> xdma_prep_device_sg() contains an empty entry, i.e. sg_dma_len()
>>>> returns zero. As I do get such sgl from v4l2 I suppose that this
>>>> is a valid scenario and not a bug in our parent mgb4 driver. Also
>>>> the documentation for sg_dma_len() suggests that there may be
>>>> zero-length entries.
>>>>
>>>> The following trivial patch fixes the crash:
>>>>
>>>> diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c
>>>> index 462109c61653..cd5fcd911c50 100644
>>>> --- a/drivers/dma/xilinx/xdma.c
>>>> +++ b/drivers/dma/xilinx/xdma.c
>>>> @@ -487,6 +487,8 @@ xdma_prep_device_sg(struct dma_chan *chan, 
>>>> struct scatterlist *sgl,
>>>>         for_each_sg(sgl, sg, sg_len, i) {
>>>>                 addr = sg_dma_address(sg);
>>>>                 rest = sg_dma_len(sg);
>>>> +               if (!rest)
>>>> +                       break;
>>>>
>>>>                 do {
>>>>                         len = min_t(u32, rest, XDMA_DESC_BLEN_MAX);
>>>>
>>>> M.
>>
diff mbox series

Patch

diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c
index 462109c61653..cd5fcd911c50 100644
--- a/drivers/dma/xilinx/xdma.c
+++ b/drivers/dma/xilinx/xdma.c
@@ -487,6 +487,8 @@  xdma_prep_device_sg(struct dma_chan *chan, struct 
scatterlist *sgl,
         for_each_sg(sgl, sg, sg_len, i) {
                 addr = sg_dma_address(sg);
                 rest = sg_dma_len(sg);
+               if (!rest)
+                       break;

                 do {