[libcamera-devel] [PATCH 8/8] [RFC-Only] libcamera: request: A request canary

Laurent Pinchart laurent.pinchart at ideasonboard.com
Mon Mar 15 17:28:01 CET 2021


Hi Kieran,

On Mon, Mar 15, 2021 at 11:24:45AM +0000, Kieran Bingham wrote:
> On 14/03/2021 02:13, Laurent Pinchart wrote:
> > On Fri, Mar 12, 2021 at 06:11:31AM +0000, Kieran Bingham wrote:
> >> Request objects are created and owned by the application, but are of
> >> course utilised widely throughout the internals of libcamera.
> >>
> >> If the application free's the requests while they are still active
> >> within libcamera a use after free will occur. While this can be trapped
> >> by tools such as valgrind, given the importance of this object and the
> >> relationship of external ownership, it may have some value to provide
> >> Debug build (disabled at Release build) assertions on the condition of
> >> these objects.
> >>
> >> Make sure the request fails an assertion immediately if used after free.
> >>
> >> Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
> >>
> >> ---
> >> RFC only, as I was going to dispose of this patch. We added it while
> >> debugging the IPU3 stability, and it may prove useful to catch errors if
> >> requests are used after they are released.
> >>
> >> However this may be redundant as pipeline handlers should guarantee that
> >> requests are fully completed when they stop(). Of course the IPU3 wasn't
> >> meeting that requirement, and we do not have a specific assertion that
> >> validates that requirement on all pipeline handlers. So perhaps this
> >> canary might serve as a beneficial guard?
> > 
> > I think we should move that check from the IPU3 pipeline handler to the
> > core, as commented on the corresponding patch, so this patch wouldn't
> > really be needed.
> > 
> >> If not, at least posting it will mean it can be used in the future if it
> >> comes up again, or the concept could be applied to other objects if
> >> appropriate.
> >>
> >>  include/libcamera/request.h |  2 ++
> >>  src/libcamera/request.cpp   | 15 +++++++++++++++
> >>  2 files changed, 17 insertions(+)
> >>
> >> diff --git a/include/libcamera/request.h b/include/libcamera/request.h
> >> index 59d7f4bac0d2..fc56d63c8c67 100644
> >> --- a/include/libcamera/request.h
> >> +++ b/include/libcamera/request.h
> >> @@ -78,6 +78,8 @@ private:
> >>  	const uint64_t cookie_;
> >>  	Status status_;
> >>  	bool cancelled_;
> >> +
> >> +	int canary_;
> > 
> > If we want to do this, couldn't we add a RequestFreed status to avoid
> > adding a separate field ?
> 
> It's tempting to add/handle this to the state machine indeed, but we are
> limited on the guarantees we can make there.
> 
> After a free - we can't guarantee that Request->status() == RequestFreed :-)

Can't we, if we set status_ to RequestFreed in the destructor ? If
you're concerned that someone would override the status, you could add
an assertion in setStatus().

> The point of the canary is to assert that it has not been free'd and
> that the values match exactly.
> 
> Maybe a RequestFreed state will add some protection though - but as this
> is RFC only - we can leave it to think about if we end up digging in
> here again.

Sounds good to me.

> >>  };
> >>  
> >>  } /* namespace libcamera */
> >> diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
> >> index 12c2e7d425f9..83169a11e1e5 100644
> >> --- a/src/libcamera/request.cpp
> >> +++ b/src/libcamera/request.cpp
> >> @@ -18,6 +18,8 @@
> >>  #include "libcamera/internal/log.h"
> >>  #include "libcamera/internal/tracepoints.h"
> >>  
> >> +#define REQUEST_CANARY 0x1F2E3D4C
> >> +
> >>  /**
> >>   * \file request.h
> >>   * \brief Describes a frame capture request to be processed by a camera
> >> @@ -89,6 +91,8 @@ Request::Request(Camera *camera, uint64_t cookie)
> >>  
> >>  	LIBCAMERA_TRACEPOINT(request_construct, this);
> >>  
> >> +	canary_ = REQUEST_CANARY;
> >> +
> >>  	LOG(Request, Debug) << "Created request - cookie: " << cookie_;
> >>  }
> >>  
> >> @@ -99,6 +103,8 @@ Request::~Request()
> >>  	delete metadata_;
> >>  	delete controls_;
> >>  	delete validator_;
> >> +
> >> +	canary_ = 0;
> >>  }
> >>  
> >>  /**
> >> @@ -113,6 +119,8 @@ Request::~Request()
> >>   */
> >>  void Request::reuse(ReuseFlag flags)
> >>  {
> >> +	ASSERT(canary_ == REQUEST_CANARY);
> >> +
> >>  	LIBCAMERA_TRACEPOINT(request_reuse, this);
> >>  
> >>  	pending_.clear();
> >> @@ -176,6 +184,8 @@ void Request::reuse(ReuseFlag flags)
> >>   */
> >>  int Request::addBuffer(const Stream *stream, FrameBuffer *buffer)
> >>  {
> >> +	ASSERT(canary_ == REQUEST_CANARY);
> >> +
> >>  	if (!stream) {
> >>  		LOG(Request, Error) << "Invalid stream reference";
> >>  		return -EINVAL;
> >> @@ -211,6 +221,8 @@ int Request::addBuffer(const Stream *stream, FrameBuffer *buffer)
> >>   */
> >>  FrameBuffer *Request::findBuffer(const Stream *stream) const
> >>  {
> >> +	ASSERT(canary_ == REQUEST_CANARY);
> >> +
> >>  	const auto it = bufferMap_.find(stream);
> >>  	if (it == bufferMap_.end())
> >>  		return nullptr;
> >> @@ -262,6 +274,7 @@ FrameBuffer *Request::findBuffer(const Stream *stream) const
> >>   */
> >>  void Request::complete()
> >>  {
> >> +	ASSERT(canary_ == REQUEST_CANARY);
> >>  	ASSERT(status_ == RequestPending);
> >>  	ASSERT(!hasPendingBuffers());
> >>  
> >> @@ -289,6 +302,8 @@ void Request::complete()
> >>   */
> >>  bool Request::completeBuffer(FrameBuffer *buffer)
> >>  {
> >> +	ASSERT(canary_ == REQUEST_CANARY);
> >> +
> >>  	LIBCAMERA_TRACEPOINT(request_complete_buffer, this, buffer);
> >>  
> >>  	int ret = pending_.erase(buffer);

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list