[libcamera-devel] [PATCH v5 4/4] android: camera_device: Protect descriptor status_ with lock

Laurent Pinchart laurent.pinchart at ideasonboard.com
Thu Oct 21 03:46:07 CEST 2021


On Wed, Oct 20, 2021 at 04:12:12PM +0530, Umang Jain wrote:
> From: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> 
> The Camera3RequestDescriptor::status_ is checked as a stop condition for
> the sendCaptureResults() loop, protected by the descriptorsMutex_. The
> status is however not set with the mutex locked, which can cause a race
> condition with a concurrent sendCaptureResults() call (from the
> post-processor thread for instance).
> 
> This should be harmless in practice, as the reader thread will either
> see the old status (Pending) and stop iterating over descriptors, or the
> new status and continue. Still, if the Camera3RequestDescriptor state
> machine were to change in the future, this could introduce hard to debug
> issues. Close the race window by always setting the status with the lock
> taken.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> Signed-off-by: Umang Jain <umang.jain at ideasonboard.com>
> ---
>  src/android/camera_device.cpp | 29 +++++++++++++++++++----------
>  src/android/camera_device.h   |  5 ++++-
>  2 files changed, 23 insertions(+), 11 deletions(-)
> 
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index b14416ce..4e8fb2ee 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -803,8 +803,6 @@ void CameraDevice::abortRequest(Camera3RequestDescriptor *descriptor) const
>  
>  	for (auto &buffer : descriptor->buffers_)
>  		buffer.status = Camera3RequestDescriptor::Status::Error;
> -
> -	descriptor->status_ = Camera3RequestDescriptor::Status::Error;
>  }
>  
>  bool CameraDevice::isValidRequest(camera3_capture_request_t *camera3Request) const
> @@ -988,7 +986,8 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
>  			descriptors_.push(std::move(descriptor));
>  		}
>  
> -		sendCaptureResults();
> +		completeDescriptor(descriptor.get(),

descriptor will be a nullptr here as it has been moved just above :-/
I think one option would be to apply the following fixup.

diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index 4e8fb2ee49f2..e876d2ce8bfa 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -803,6 +803,9 @@ void CameraDevice::abortRequest(Camera3RequestDescriptor *descriptor) const

 	for (auto &buffer : descriptor->buffers_)
 		buffer.status = Camera3RequestDescriptor::Status::Error;
+
+	MutexLocker lock(descriptorsMutex_);
+	descriptor->status_ = Camera3RequestDescriptor::Status::Error;
 }

 bool CameraDevice::isValidRequest(camera3_capture_request_t *camera3Request) const
@@ -986,8 +989,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
 			descriptors_.push(std::move(descriptor));
 		}

-		completeDescriptor(descriptor.get(),
-				   Camera3RequestDescriptor::Status::Error);
+		sendCaptureResults();

 		return 0;
 	}
@@ -1057,8 +1059,7 @@ void CameraDevice::requestComplete(Request *request)
 				<< request->status();

 		abortRequest(descriptor);
-		completeDescriptor(descriptor,
-				   Camera3RequestDescriptor::Status::Error);
+		sendCaptureResults();

 		return;
 	}
@@ -1153,9 +1154,10 @@ void CameraDevice::requestComplete(Request *request)
 void CameraDevice::completeDescriptor(Camera3RequestDescriptor *descriptor,
 				      Camera3RequestDescriptor::Status status)
 {
-	MutexLocker lock(descriptorsMutex_);
-	descriptor->status_ = status;
-	lock.unlock();
+	{
+		MutexLocker lock(descriptorsMutex_);
+		descriptor->status_ = status;
+	}

 	sendCaptureResults();
 }
@@ -1262,14 +1264,14 @@ void CameraDevice::streamProcessingComplete(CameraStream *cameraStream,
 			hasPostProcessingErrors = true;
 	}

+	locker.unlock();
+
 	Camera3RequestDescriptor::Status descriptorStatus;
 	if (hasPostProcessingErrors)
 		descriptorStatus = Camera3RequestDescriptor::Status::Error;
 	else
 		descriptorStatus = Camera3RequestDescriptor::Status::Success;

-	locker.unlock();
-
 	completeDescriptor(request, descriptorStatus);
 }

diff --git a/src/android/camera_device.h b/src/android/camera_device.h
index 46fb93ee777b..a85602cf178f 100644
--- a/src/android/camera_device.h
+++ b/src/android/camera_device.h
@@ -124,7 +124,7 @@ private:
 	std::vector<CameraStream> streams_;

 	/* Protects descriptors_ and Camera3RequestDescriptor::status_. */
-	libcamera::Mutex descriptorsMutex_;
+	mutable libcamera::Mutex descriptorsMutex_;
 	std::queue<std::unique_ptr<Camera3RequestDescriptor>> descriptors_;

 	std::string maker_;


> +				   Camera3RequestDescriptor::Status::Error);
>  
>  		return 0;
>  	}
> @@ -1058,7 +1057,8 @@ void CameraDevice::requestComplete(Request *request)
>  				<< request->status();
>  
>  		abortRequest(descriptor);
> -		sendCaptureResults();
> +		completeDescriptor(descriptor,
> +				   Camera3RequestDescriptor::Status::Error);
>  
>  		return;
>  	}
> @@ -1135,20 +1135,28 @@ void CameraDevice::requestComplete(Request *request)
>  
>  	if (needsPostProcessing) {
>  		if (processingStatus == Camera3RequestDescriptor::Status::Error) {
> -			descriptor->status_ = processingStatus;
>  			/*
>  			 * \todo This is problematic when some streams are
>  			 * queued successfully, but some fail to get queued.
>  			 * We might end up with use-after-free situation for
>  			 * descriptor in streamProcessingComplete().
>  			 */
> -			sendCaptureResults();
> +			completeDescriptor(descriptor, processingStatus);
>  		}
>  
>  		return;
>  	}
>  
> -	descriptor->status_ = Camera3RequestDescriptor::Status::Success;
> +	completeDescriptor(descriptor, Camera3RequestDescriptor::Status::Success);
> +}
> +
> +void CameraDevice::completeDescriptor(Camera3RequestDescriptor *descriptor,
> +				      Camera3RequestDescriptor::Status status)
> +{
> +	MutexLocker lock(descriptorsMutex_);
> +	descriptor->status_ = status;
> +	lock.unlock();
> +
>  	sendCaptureResults();
>  }
>  
> @@ -1254,14 +1262,15 @@ void CameraDevice::streamProcessingComplete(CameraStream *cameraStream,
>  			hasPostProcessingErrors = true;
>  	}
>  
> +	Camera3RequestDescriptor::Status descriptorStatus;
>  	if (hasPostProcessingErrors)
> -		request->status_ = Camera3RequestDescriptor::Status::Error;
> +		descriptorStatus = Camera3RequestDescriptor::Status::Error;
>  	else
> -		request->status_ = Camera3RequestDescriptor::Status::Success;
> +		descriptorStatus = Camera3RequestDescriptor::Status::Success;
>  
>  	locker.unlock();
>  
> -	sendCaptureResults();
> +	completeDescriptor(request, descriptorStatus);
>  }
>  
>  std::string CameraDevice::logPrefix() const
> diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> index 1ef933da..46fb93ee 100644
> --- a/src/android/camera_device.h
> +++ b/src/android/camera_device.h
> @@ -96,6 +96,8 @@ private:
>  	void notifyError(uint32_t frameNumber, camera3_stream_t *stream,
>  			 camera3_error_msg_code code) const;
>  	int processControls(Camera3RequestDescriptor *descriptor);
> +	void completeDescriptor(Camera3RequestDescriptor *descriptor,
> +				Camera3RequestDescriptor::Status status);
>  	void sendCaptureResults();
>  	void setBufferStatus(CameraStream *cameraStream,
>  			     Camera3RequestDescriptor::StreamBuffer &buffer,
> @@ -121,7 +123,8 @@ private:
>  
>  	std::vector<CameraStream> streams_;
>  
> -	libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
> +	/* Protects descriptors_ and Camera3RequestDescriptor::status_. */
> +	libcamera::Mutex descriptorsMutex_;
>  	std::queue<std::unique_ptr<Camera3RequestDescriptor>> descriptors_;
>  
>  	std::string maker_;

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list