On Wed, May 30, 2018 at 11:16:10AM +1000, NeilBrown wrote:
> "obviously" we would first fix mtk_rx_free_frags() to do the right thing
> if ring->rx_data were NULL. This is a crucial part of robust error
> handling.
> The very best error handling style is devm_*. When that is not
> practical, kfree() style, which quietly accepts NULL, is a good second
> best.
> Could it be that the various bugs you have seen could be blamed on
> resource-release function which do not handle NULL well? When these
> bugs were fixed, were they fixed by fixing the resource-release
> function (which would benefit many) or were they fixed by making the
> code more robust against a poor interface by adding more checking?
It's way way more complicated to review that sort of code.
With multi label style error handling, I can just track the most
recently allocated resource and tell from the goto if it frees what I
expected.
With one label error handling, I have to scroll down to the bottom of
the function. Then jump to mtk_rx_free_frags(). Then carefully read
through it to see if can handle a partially allocated array. Then I
have to jump back to the original code and scroll up to see if
ring->rx_data is zeroed memory and where ring->rx_buf_size gets set.
>
> >
> > Multi label error handling is self documenting so you don't need to jump
> > to the bottom of the function. You only need to track the most recently
> > allocated resource.
> >
> > foo = allocate_foo();
> > if (!foo)
> > return -ENOMEM;
> >
> > bar = allocate_bar();
> > if (!bar) {
> > ret = -ENOMEM;
> > goto err_free_foo;
> > }
>
> I despise this structure because I cannot simply add
>
> baz = allocate_baz();
> if (!baz) {
> ret = -ENOMEM;
> goto err_???;
> }
>
> in the between these two. I would need to also change the err_free_foo
> to err_free_baz.
In the common case, you only need to update one goto.
foo = allocate_foo();
if (!foo)
return -ENOMEM;
ret = allocate_new();
if (ret)
goto free_foo;
ret = -ENOMEM;
bar = allocate_bar();
if (!bar)
goto free_new; <-- changed
Which ever way you write error handling, it always needs to be updated
so this isn't worse than other styles.
> Possibly if the convention was "goto err_baz_failed" it might be
> workable. Then the failure code would get
>
> free_baz(baz);
> err_baz_failed:
>
> added to it. That would be less despicable, but not as good have
> ensuring that every free_XXX() correctly handled NULL.
When code uses "come from" style label names, the gotos look like this:
foo = kmalloc();
if (!foo)
goto foo_failed;
That's useless, because it doesn't tell what the goto does. Imagine if
functions were named after the call site. It's the same concept. I
have to scroll to the bottom to see what the goto does.
>
> >
> > This style of error handling is the most maintainable. For example, I
> > see bugs where people change a function from returning NULL to returning
> > an error pointer but the error handling was relying on that kfree(NULL)
> > is a no-op. If you only free resources which have been allocated then
> > you avoid that.
>
> You've just made an excellent case for changing kfree() to something like
>
> if (unlikely(ZERO_OR_NULL_PTR(objp) || IS_ERR(objp)))
> return;
>
> I look forward do seeing your patch :-)
>
I mentioned earlier that in the past few days I had seen four bugs
from one label style error handling.
2 were freeing uninitialized pointers (GCC didn't catch it).
2 were doing kfree(foo->bar); where foo was NULL.
1 was missing a free.
Changing kfree() wouldn't have fixed any of those problems. One of the
kfree(foo->bar) bugs was actually slightly involved and we're still
figuring out what the right fix is.
The multi label bug was that was a missing free. It was straight
forward to add a new free to right place.
regards,
dan carpenter
_______________________________________________
devel mailing list
[email protected]
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel