Message ID | 20200902202650.14189-1-trix@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v2] soundwire: fix double free of dangling pointer | expand |
Wed, 2 Sep 2020 13:26:50 -0700 > From: Tom Rix <trix@redhat.com> > > clang static analysis flags this problem > > stream.c:844:9: warning: Use of memory after > it is freed > kfree(bus->defer_msg.msg->buf); > ^~~~~~~~~~~~~~~~~~~~~~~ > > This happens in an error handler cleaning up memory > allocated for elements in a list. > > list_for_each_entry(m_rt, &stream->master_list, stream_node) { > bus = m_rt->bus; > > kfree(bus->defer_msg.msg->buf); > kfree(bus->defer_msg.msg); > } > > And is triggered when the call to sdw_bank_switch() fails. > There are a two problems. > > First, when sdw_bank_switch() fails, though it frees memory it > does not clear bus's reference 'defer_msg.msg' to that memory. > > The second problem is the freeing msg->buf. In some cases > msg will be NULL so this will dereference a null pointer. > Need to check before freeing. > > Fixes: 99b8a5d608a6 ("soundwire: Add bank switch routine") > Signed-off-by: Tom Rix <trix@redhat.com> > Reviewed-by: Nick Desaulniers <ndesaulniers@google.com> > Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> > --- > v2 : change title, was 'soundwire: fix error handling' > --- > drivers/soundwire/stream.c | 8 +++++--- > 1 file changed, 5 insertions(+), 3 deletions(-) > > diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c > index 37290a799023..6e36deb505b1 100644 > --- a/drivers/soundwire/stream.c > +++ b/drivers/soundwire/stream.c > @@ -717,6 +717,7 @@ static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count) > kfree(wbuf); > error_1: > kfree(wr_msg); > + bus->defer_msg.msg = NULL; > return ret; > } > > @@ -840,9 +841,10 @@ static int do_bank_switch(struct sdw_stream_runtime *stream) > error: > list_for_each_entry(m_rt, &stream->master_list, stream_node) { > bus = m_rt->bus; > - > - kfree(bus->defer_msg.msg->buf); > - kfree(bus->defer_msg.msg); > + if (bus->defer_msg.msg) { > + kfree(bus->defer_msg.msg->buf); > + kfree(bus->defer_msg.msg); > + } > } > > msg_unlock: > -- > 2.18.1 Looks like it needs also to release the current buf before putting the new one into place. --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -665,6 +665,10 @@ static int sdw_bank_switch(struct sdw_bu if (!wr_msg) return -ENOMEM; + if (bus->defer_msg.msg) { + kfree(bus->defer_msg.msg->buf); + kfree(bus->defer_msg.msg); + } bus->defer_msg.msg = wr_msg; wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL);
On 02-09-20, 13:26, trix@redhat.com wrote: > From: Tom Rix <trix@redhat.com> > > clang static analysis flags this problem > > stream.c:844:9: warning: Use of memory after > it is freed > kfree(bus->defer_msg.msg->buf); > ^~~~~~~~~~~~~~~~~~~~~~~ > > This happens in an error handler cleaning up memory > allocated for elements in a list. > > list_for_each_entry(m_rt, &stream->master_list, stream_node) { > bus = m_rt->bus; > > kfree(bus->defer_msg.msg->buf); > kfree(bus->defer_msg.msg); > } > > And is triggered when the call to sdw_bank_switch() fails. > There are a two problems. > > First, when sdw_bank_switch() fails, though it frees memory it > does not clear bus's reference 'defer_msg.msg' to that memory. > > The second problem is the freeing msg->buf. In some cases > msg will be NULL so this will dereference a null pointer. > Need to check before freeing. Applied, thanks
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 37290a799023..6e36deb505b1 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -717,6 +717,7 @@ static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count) kfree(wbuf); error_1: kfree(wr_msg); + bus->defer_msg.msg = NULL; return ret; } @@ -840,9 +841,10 @@ static int do_bank_switch(struct sdw_stream_runtime *stream) error: list_for_each_entry(m_rt, &stream->master_list, stream_node) { bus = m_rt->bus; - - kfree(bus->defer_msg.msg->buf); - kfree(bus->defer_msg.msg); + if (bus->defer_msg.msg) { + kfree(bus->defer_msg.msg->buf); + kfree(bus->defer_msg.msg); + } } msg_unlock: