On 02/10/20 3:52 pm, Hans de Goede wrote: > Hi, > > On 10/1/20 9:43 PM, Anant Thazhemadam wrote: >> When h5_close() gets called, the memory allocated for the hu gets >> freed only if hu->serdev doesn't exist. This leads to a memory leak. >> So when h5_close() is requested, close the serdev device instance and >> free the memory allocated to the hu entirely instead. >> >> Fixes: ce945552fde4 ("Bluetooth: hci_h5: Add support for serdev enumerated devices") >> Reported-by: syzbot+6ce141c55b2f7aafd1c4@xxxxxxxxxxxxxxxxxxxxxxxxx >> Tested-by: syzbot+6ce141c55b2f7aafd1c4@xxxxxxxxxxxxxxxxxxxxxxxxx >> Signed-off-by: Anant Thazhemadam <anant.thazhemadam@xxxxxxxxx> > > So 2 comments to this: > > 1. You claim this change fixes a memory-leak, but in the serdev case > the memory is allocated in h5_serdev_probe() like this: > > h5 = devm_kzalloc(dev, sizeof(*h5), GFP_KERNEL); > > So its lifetime is tied to the lifetime of the driver being bound > to the serdev and it is automatically freed when the driver gets > unbound. If you had looked at where the h5 struct gets allocated > in h5_close()-s counterpart, h5_open() then you could have seen > this there: > > if (hu->serdev) { > h5 = serdev_device_get_drvdata(hu->serdev); > } else { > h5 = kzalloc(sizeof(*h5), GFP_KERNEL); > if (!h5) > return -ENOMEM; > } > > So it is very clear here, that the kzalloc only happens in the > if (!hu->serdev) which clearly makes the kfree() have the same > condition the right thing todo. In the hu->serdev the data which > was allocated by h5_serdev_probe() is used instead and no alloc > happens inside h5_open() > > In general when looking at resource teardown you should also look > at where they are allocated in the mirror function > and the teardown should mirror the alloc code. > > So the main change of your commit is wrong: > > NACK. > > > 2. You are making multiple changes in a single commit here, I count at > least 3. When ever you are making multiple changes like this, you should really > split the commit up in 1 commit per change and explain in each commit > message why that change is being made (why it is necessary). Writing > commit messages like this also leads to you double-checking your own > work by carefully considering why you are making the changes. > > So about the 3 different changes: > > 2a) Make the kfree(h5) call unconditionally, which as mentioned above > is wrong. > Hmm, I understand, thank you. > 2b) Introduce a call to kfree_skb(h5->rx_skb); which is not mentioned in > the commit message at all. This looks like it would actually be a sensible > change, at least in the "if (!hu->serdev)" case. When using the serdev > interface then just freeing it will leave a dangling pointer, so it > would be better (for both the hu->serdev and the !hu->serdev cases) > to call h5_reset_rx() on close instead which does: > > if (h5->rx_skb) { > kfree_skb(h5->rx_skb); > h5->rx_skb = NULL; > } > This was the first thing I did! (and I tested and it works too) https://syzkaller.appspot.com/text?tag=Patch&x=13acf917900000 However, I was worried that doing so might mean freeing an skb that might be required somewhere by the serdev connection (which could possibly lead to a null ptr dereference), so I thought closing the connection altogether might be a better alternative. But now, since I've been told better, I'll submit a v3, that makes the more sensible change and doesn't close the serdev_device, with a much more informative commit message! I also found a similar implementation in mrvl_close() where the serdev_device was being closed and felt it might be the right way to go. But I now know that I shouldn't have overlooked the fact that the open functions for both differ. I'm sorry for this. Like you mentioned, I should've given more priority to the mirror function, and looked to see if even they were along the same lines, and I will be sure to keep that in mind. > 2c) Introduce a call to serdev_device_close(), without really explaining > why in the commit message. Again if you would have looked at the mirror > h5_close() function then you see no serdev_device_open() there. > Actually serdev_device_open() is not called anywhere in the hci_h5.c code. > > Digging a little deeper (using grep) shows that hci_uart_register_device() > calls serdev_device_open() and hci_uart_register_device() gets called from: > h5_serdev_probe(), likewise hci_uart_unregister_device() calls > serdev_device_close() for us and that gets called from h5_serdev_remove(), > so there is no need to call serdev_device_close() from h5_close() and doing > so will actually break things, because then the serdev will be left closed > on a subsequent h5_open() call. > > Regards, > > Hans > Thank you so much for this review, and for your time! :) Thanks, Anant