[libcamera-devel] libcamera: pipeline: simple: Fix crash when storing timestamp in metadata
diff mbox series

Message ID 20210616234422.24789-1-laurent.pinchart@ideasonboard.com
State Accepted
Commit 73b823b22009b87fc1d33a8000a870fe9223e7bb
Headers show
Series
  • [libcamera-devel] libcamera: pipeline: simple: Fix crash when storing timestamp in metadata
Related show

Commit Message

Laurent Pinchart June 16, 2021, 11:44 p.m. UTC
Commit 922833f774f6 ("libcamera: simple: Report sensor timestamp")
unconditionally tries to access the request through the capture buffer
to store the capture timestamp in the metadata. This causes a null
pointer dereference when using a converter, as the capture buffers are
free-wheeling in that case, and not associated with a request.

Fix this by getting the request from the user-facing buffer, which can
be the capture buffer when no converter is used.

Fixes: 922833f774f6 ("libcamera: simple: Report sensor timestamp")
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/libcamera/pipeline/simple/simple.cpp | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

Comments

Niklas Söderlund June 17, 2021, 6:01 a.m. UTC | #1
Hi Laurent,

Thanks for your work.

On 2021-06-17 02:44:22 +0300, Laurent Pinchart wrote:
> Commit 922833f774f6 ("libcamera: simple: Report sensor timestamp")
> unconditionally tries to access the request through the capture buffer
> to store the capture timestamp in the metadata. This causes a null
> pointer dereference when using a converter, as the capture buffers are
> free-wheeling in that case, and not associated with a request.
> 
> Fix this by getting the request from the user-facing buffer, which can
> be the capture buffer when no converter is used.
> 
> Fixes: 922833f774f6 ("libcamera: simple: Report sensor timestamp")
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>

> ---
>  src/libcamera/pipeline/simple/simple.cpp | 20 +++++++++++++++++---
>  1 file changed, 17 insertions(+), 3 deletions(-)
> 
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index 36fd9b852b33..e63d0bfd2fcb 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -1127,14 +1127,28 @@ void SimplePipelineHandler::bufferReady(FrameBuffer *buffer)
>  	}
>  
>  	/*
> -	 * Record the sensor's timestamp in the request metadata.
> +	 * Record the sensor's timestamp in the request metadata. The request
> +	 * needs to be obtained from the user-facing buffer, as internal
> +	 * buffers are free-wheeling and have no request associated with them.
>  	 *
>  	 * \todo The sensor timestamp should be better estimated by connecting
>  	 * to the V4L2Device::frameStart signal if the platform provides it.
>  	 */
>  	Request *request = buffer->request();
> -	request->metadata().set(controls::SensorTimestamp,
> -				buffer->metadata().timestamp);
> +
> +	if (data->useConverter_ && !data->converterQueue_.empty()) {
> +		const std::map<unsigned int, FrameBuffer *> &outputs =
> +			data->converterQueue_.front();
> +		if (!outputs.empty()) {
> +			FrameBuffer *outputBuffer = outputs.begin()->second;
> +			if (outputBuffer)
> +				request = outputBuffer->request();
> +		}
> +	}
> +
> +	if (request)
> +		request->metadata().set(controls::SensorTimestamp,
> +					buffer->metadata().timestamp);
>  
>  	/*
>  	 * Queue the captured and the request buffer to the converter if format
> -- 
> Regards,
> 
> Laurent Pinchart
>
Jacopo Mondi June 17, 2021, 8:10 a.m. UTC | #2
Hi Laurent,

On Thu, Jun 17, 2021 at 02:44:22AM +0300, Laurent Pinchart wrote:
> Commit 922833f774f6 ("libcamera: simple: Report sensor timestamp")
> unconditionally tries to access the request through the capture buffer
> to store the capture timestamp in the metadata. This causes a null
> pointer dereference when using a converter, as the capture buffers are
> free-wheeling in that case, and not associated with a request.
>
> Fix this by getting the request from the user-facing buffer, which can
> be the capture buffer when no converter is used.
>
> Fixes: 922833f774f6 ("libcamera: simple: Report sensor timestamp")
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

My bad, my understanding of the converters in the simple pipeline is
limited. Thanks for fixing

> ---
>  src/libcamera/pipeline/simple/simple.cpp | 20 +++++++++++++++++---
>  1 file changed, 17 insertions(+), 3 deletions(-)
>
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index 36fd9b852b33..e63d0bfd2fcb 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -1127,14 +1127,28 @@ void SimplePipelineHandler::bufferReady(FrameBuffer *buffer)
>  	}
>
>  	/*
> -	 * Record the sensor's timestamp in the request metadata.
> +	 * Record the sensor's timestamp in the request metadata. The request
> +	 * needs to be obtained from the user-facing buffer, as internal
> +	 * buffers are free-wheeling and have no request associated with them.
>  	 *
>  	 * \todo The sensor timestamp should be better estimated by connecting
>  	 * to the V4L2Device::frameStart signal if the platform provides it.
>  	 */
>  	Request *request = buffer->request();
> -	request->metadata().set(controls::SensorTimestamp,
> -				buffer->metadata().timestamp);
> +
> +	if (data->useConverter_ && !data->converterQueue_.empty()) {
> +		const std::map<unsigned int, FrameBuffer *> &outputs =
> +			data->converterQueue_.front();
> +		if (!outputs.empty()) {

Can outputs be empty if !data->converterQueue_.empty() ?

> +			FrameBuffer *outputBuffer = outputs.begin()->second;
> +			if (outputBuffer)
> +				request = outputBuffer->request();
> +		}
> +	}
> +
> +	if (request)
> +		request->metadata().set(controls::SensorTimestamp,
> +					buffer->metadata().timestamp);

We could end up without a request
Before 922833f774f6 the Request * was simply retreived with

        Request *request = buffer->request();

If we can now end up without a Request *, how can we complete it here
below ?

Thanks
   j

>
>  	/*
>  	 * Queue the captured and the request buffer to the converter if format
> --
> Regards,
>
> Laurent Pinchart
>
Laurent Pinchart June 17, 2021, 9:12 a.m. UTC | #3
Hi Jacopo,

On Thu, Jun 17, 2021 at 10:10:21AM +0200, Jacopo Mondi wrote:
> On Thu, Jun 17, 2021 at 02:44:22AM +0300, Laurent Pinchart wrote:
> > Commit 922833f774f6 ("libcamera: simple: Report sensor timestamp")
> > unconditionally tries to access the request through the capture buffer
> > to store the capture timestamp in the metadata. This causes a null
> > pointer dereference when using a converter, as the capture buffers are
> > free-wheeling in that case, and not associated with a request.
> >
> > Fix this by getting the request from the user-facing buffer, which can
> > be the capture buffer when no converter is used.
> >
> > Fixes: 922833f774f6 ("libcamera: simple: Report sensor timestamp")
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> My bad, my understanding of the converters in the simple pipeline is
> limited. Thanks for fixing

No worries. I missed it during review too :-)

> > ---
> >  src/libcamera/pipeline/simple/simple.cpp | 20 +++++++++++++++++---
> >  1 file changed, 17 insertions(+), 3 deletions(-)
> >
> > diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> > index 36fd9b852b33..e63d0bfd2fcb 100644
> > --- a/src/libcamera/pipeline/simple/simple.cpp
> > +++ b/src/libcamera/pipeline/simple/simple.cpp
> > @@ -1127,14 +1127,28 @@ void SimplePipelineHandler::bufferReady(FrameBuffer *buffer)
> >  	}
> >
> >  	/*
> > -	 * Record the sensor's timestamp in the request metadata.
> > +	 * Record the sensor's timestamp in the request metadata. The request
> > +	 * needs to be obtained from the user-facing buffer, as internal
> > +	 * buffers are free-wheeling and have no request associated with them.
> >  	 *
> >  	 * \todo The sensor timestamp should be better estimated by connecting
> >  	 * to the V4L2Device::frameStart signal if the platform provides it.
> >  	 */
> >  	Request *request = buffer->request();
> > -	request->metadata().set(controls::SensorTimestamp,
> > -				buffer->metadata().timestamp);
> > +
> > +	if (data->useConverter_ && !data->converterQueue_.empty()) {
> > +		const std::map<unsigned int, FrameBuffer *> &outputs =
> > +			data->converterQueue_.front();
> > +		if (!outputs.empty()) {
> 
> Can outputs be empty if !data->converterQueue_.empty() ?

They shouldn't, but as there's the same sanity check in
converter->queueBuffers(), which is called *after* this code block, I
thought it would be best to be cautious. Eventually we should probably
rework this to only queue buffers for capture when there are request
queued, the same way te IPU3 pipeline handler does it, but that was a
too big change for a fix.

> > +			FrameBuffer *outputBuffer = outputs.begin()->second;
> > +			if (outputBuffer)
> > +				request = outputBuffer->request();
> > +		}
> > +	}
> > +
> > +	if (request)
> > +		request->metadata().set(controls::SensorTimestamp,
> > +					buffer->metadata().timestamp);
> 
> We could end up without a request
> Before 922833f774f6 the Request * was simply retreived with
> 
>         Request *request = buffer->request();
> 
> If we can now end up without a Request *, how can we complete it here
> below ?

request can be null here if

	data->useConverter && data->converterQueue_.empty()

In that case we'll take the `if (data->useConverter_)` branch, and won't
reach the completeBuffer() call.

It's a little bit convoluted, the other option was to deplicate the
request->metadata().set() call, which I didn't like much.

> >  	/*
> >  	 * Queue the captured and the request buffer to the converter if format
Jacopo Mondi June 17, 2021, 9:27 a.m. UTC | #4
Hi Laurent,

On Thu, Jun 17, 2021 at 12:12:28PM +0300, Laurent Pinchart wrote:
> Hi Jacopo,
>
> On Thu, Jun 17, 2021 at 10:10:21AM +0200, Jacopo Mondi wrote:
> > On Thu, Jun 17, 2021 at 02:44:22AM +0300, Laurent Pinchart wrote:
> > > Commit 922833f774f6 ("libcamera: simple: Report sensor timestamp")
> > > unconditionally tries to access the request through the capture buffer
> > > to store the capture timestamp in the metadata. This causes a null
> > > pointer dereference when using a converter, as the capture buffers are
> > > free-wheeling in that case, and not associated with a request.
> > >
> > > Fix this by getting the request from the user-facing buffer, which can
> > > be the capture buffer when no converter is used.
> > >
> > > Fixes: 922833f774f6 ("libcamera: simple: Report sensor timestamp")
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >
> > My bad, my understanding of the converters in the simple pipeline is
> > limited. Thanks for fixing
>
> No worries. I missed it during review too :-)
>
> > > ---
> > >  src/libcamera/pipeline/simple/simple.cpp | 20 +++++++++++++++++---
> > >  1 file changed, 17 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> > > index 36fd9b852b33..e63d0bfd2fcb 100644
> > > --- a/src/libcamera/pipeline/simple/simple.cpp
> > > +++ b/src/libcamera/pipeline/simple/simple.cpp
> > > @@ -1127,14 +1127,28 @@ void SimplePipelineHandler::bufferReady(FrameBuffer *buffer)
> > >  	}
> > >
> > >  	/*
> > > -	 * Record the sensor's timestamp in the request metadata.
> > > +	 * Record the sensor's timestamp in the request metadata. The request
> > > +	 * needs to be obtained from the user-facing buffer, as internal
> > > +	 * buffers are free-wheeling and have no request associated with them.
> > >  	 *
> > >  	 * \todo The sensor timestamp should be better estimated by connecting
> > >  	 * to the V4L2Device::frameStart signal if the platform provides it.
> > >  	 */
> > >  	Request *request = buffer->request();
> > > -	request->metadata().set(controls::SensorTimestamp,
> > > -				buffer->metadata().timestamp);
> > > +
> > > +	if (data->useConverter_ && !data->converterQueue_.empty()) {
> > > +		const std::map<unsigned int, FrameBuffer *> &outputs =
> > > +			data->converterQueue_.front();
> > > +		if (!outputs.empty()) {
> >
> > Can outputs be empty if !data->converterQueue_.empty() ?
>
> They shouldn't, but as there's the same sanity check in
> converter->queueBuffers(), which is called *after* this code block, I
> thought it would be best to be cautious. Eventually we should probably
> rework this to only queue buffers for capture when there are request
> queued, the same way te IPU3 pipeline handler does it, but that was a
> too big change for a fix.
>
> > > +			FrameBuffer *outputBuffer = outputs.begin()->second;
> > > +			if (outputBuffer)
> > > +				request = outputBuffer->request();
> > > +		}
> > > +	}
> > > +
> > > +	if (request)
> > > +		request->metadata().set(controls::SensorTimestamp,
> > > +					buffer->metadata().timestamp);
> >
> > We could end up without a request
> > Before 922833f774f6 the Request * was simply retreived with
> >
> >         Request *request = buffer->request();
> >
> > If we can now end up without a Request *, how can we complete it here
> > below ?
>
> request can be null here if
>
> 	data->useConverter && data->converterQueue_.empty()
>
> In that case we'll take the `if (data->useConverter_)` branch, and won't
> reach the completeBuffer() call.

Oh right, I missed the condition check

>
> It's a little bit convoluted, the other option was to deplicate the
> request->metadata().set() call, which I didn't like much.

Yes, not easy to follow in full, thanks for the clarifications

Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

Thanks
  j

>
> > >  	/*
> > >  	 * Queue the captured and the request buffer to the converter if format
>
> --
> Regards,
>
> Laurent Pinchart

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 36fd9b852b33..e63d0bfd2fcb 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -1127,14 +1127,28 @@  void SimplePipelineHandler::bufferReady(FrameBuffer *buffer)
 	}
 
 	/*
-	 * Record the sensor's timestamp in the request metadata.
+	 * Record the sensor's timestamp in the request metadata. The request
+	 * needs to be obtained from the user-facing buffer, as internal
+	 * buffers are free-wheeling and have no request associated with them.
 	 *
 	 * \todo The sensor timestamp should be better estimated by connecting
 	 * to the V4L2Device::frameStart signal if the platform provides it.
 	 */
 	Request *request = buffer->request();
-	request->metadata().set(controls::SensorTimestamp,
-				buffer->metadata().timestamp);
+
+	if (data->useConverter_ && !data->converterQueue_.empty()) {
+		const std::map<unsigned int, FrameBuffer *> &outputs =
+			data->converterQueue_.front();
+		if (!outputs.empty()) {
+			FrameBuffer *outputBuffer = outputs.begin()->second;
+			if (outputBuffer)
+				request = outputBuffer->request();
+		}
+	}
+
+	if (request)
+		request->metadata().set(controls::SensorTimestamp,
+					buffer->metadata().timestamp);
 
 	/*
 	 * Queue the captured and the request buffer to the converter if format