/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "camera2common.h" using namespace std; using namespace android; using namespace android::hardware; constexpr int32_t kFrameRateMin = 1; constexpr int32_t kFrameRateMax = 1000; constexpr int32_t kNumMin = 0; constexpr int32_t kNumMax = 1024; constexpr int32_t kMemoryDealerSize = 1000; constexpr int8_t kMinElements = 1; constexpr int8_t kMaxElements = 10; constexpr int32_t kValidCMD[] = {CAMERA_CMD_START_SMOOTH_ZOOM, CAMERA_CMD_STOP_SMOOTH_ZOOM, CAMERA_CMD_SET_DISPLAY_ORIENTATION, CAMERA_CMD_ENABLE_SHUTTER_SOUND, CAMERA_CMD_PLAY_RECORDING_SOUND, CAMERA_CMD_START_FACE_DETECTION, CAMERA_CMD_STOP_FACE_DETECTION, CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG, CAMERA_CMD_PING, CAMERA_CMD_SET_VIDEO_BUFFER_COUNT, CAMERA_CMD_SET_VIDEO_FORMAT}; constexpr int32_t kValidVideoBufferMode[] = {ICamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_YUV, ICamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA, ICamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE}; constexpr int32_t kValidPreviewCallbackFlag[] = { CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK, CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK, CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK, CAMERA_FRAME_CALLBACK_FLAG_NOOP, CAMERA_FRAME_CALLBACK_FLAG_CAMCORDER, CAMERA_FRAME_CALLBACK_FLAG_CAMERA, CAMERA_FRAME_CALLBACK_FLAG_BARCODE_SCANNER}; class TestCameraListener : public CameraListener { public: virtual ~TestCameraListener() = default; void notify(int32_t /*msgType*/, int32_t /*ext1*/, int32_t /*ext2*/) override { return; }; void postData(int32_t /*msgType*/, const sp& /*dataPtr*/, camera_frame_metadata_t* /*metadata*/) override { return; }; void postDataTimestamp(nsecs_t /*timestamp*/, int32_t /*msgType*/, const sp& /*dataPtr*/) override { return; }; void postRecordingFrameHandleTimestamp(nsecs_t /*timestamp*/, native_handle_t* /*handle*/) override { return; }; void postRecordingFrameHandleTimestampBatch( const std::vector& /*timestamps*/, const std::vector& /*handles*/) override { return; }; }; class CameraFuzzer : public ::android::hardware::BnCameraClient { public: void process(const uint8_t* data, size_t size); private: bool initCamera(); void invokeCamera(); void invokeSetParameters(); native_handle_t* createNativeHandle(); sp mCamera = nullptr; FuzzedDataProvider* mFDP = nullptr; // CameraClient interface void notifyCallback(int32_t, int32_t, int32_t) override { return; }; void dataCallback(int32_t, const sp&, camera_frame_metadata_t*) override { return; }; void dataCallbackTimestamp(nsecs_t, int32_t, const sp&) override { return; }; void recordingFrameHandleCallbackTimestamp(nsecs_t, native_handle_t*) override { return; }; void recordingFrameHandleCallbackTimestampBatch(const std::vector&, const std::vector&) override { return; }; }; native_handle_t* CameraFuzzer::createNativeHandle() { int32_t numFds = mFDP->ConsumeIntegralInRange(kMinElements, kMaxElements); int32_t numInts = mFDP->ConsumeIntegralInRange(kNumMin, kNumMax); native_handle_t* handle = native_handle_create(numFds, numInts); for (int32_t i = 0; i < numFds; ++i) { std::string filename = mFDP->ConsumeRandomLengthString(kMaxBytes); int32_t fd = open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC); handle->data[i] = fd; } return handle; } bool CameraFuzzer::initCamera() { ProcessState::self()->startThreadPool(); sp sm = defaultServiceManager(); sp binder = sm->getService(String16("media.camera")); sp cameraService = nullptr; cameraService = interface_cast(binder); sp cameraDevice = nullptr; if (mFDP->ConsumeBool()) { cameraService->connect(this, mFDP->ConsumeIntegral() /* cameraId */, "CAMERAFUZZ", hardware::ICameraService::USE_CALLING_UID, hardware::ICameraService::USE_CALLING_PID, /*targetSdkVersion*/ __ANDROID_API_FUTURE__, /*overrideToPortrait*/ false, /*forceSlowJpegMode*/ false, kDefaultDeviceId, /*devicePolicy*/0, &cameraDevice); } else { cameraService->connect(this, mFDP->ConsumeIntegral() /* cameraId */, mFDP->ConsumeRandomLengthString(kMaxBytes).c_str(), mFDP->ConsumeIntegral() /* clientUid */, mFDP->ConsumeIntegral() /* clientPid */, /*targetSdkVersion*/ mFDP->ConsumeIntegral(), /*overrideToPortrait*/ mFDP->ConsumeBool(), /*forceSlowJpegMode*/ mFDP->ConsumeBool(), kDefaultDeviceId, /*devicePolicy*/0, &cameraDevice); } mCamera = Camera::create(cameraDevice); if (!mCamera) { return false; } return true; } void CameraFuzzer::invokeSetParameters() { String8 s = mCamera->getParameters(); CameraParameters params(s); int32_t width = mFDP->ConsumeIntegral(); int32_t height = mFDP->ConsumeIntegral(); params.setVideoSize(width, height); int32_t frameRate = mFDP->ConsumeIntegralInRange(kFrameRateMin, kFrameRateMax); params.setPreviewFrameRate(frameRate); mCamera->setParameters(params.flatten()); } void CameraFuzzer::invokeCamera() { if (!initCamera()) { return; } int32_t cameraId = mFDP->ConsumeIntegral(); Camera::getNumberOfCameras(kDefaultDeviceId, /*devicePolicy*/0); CameraInfo cameraInfo; cameraInfo.facing = mFDP->ConsumeBool() ? mFDP->PickValueInArray(kValidFacing) : mFDP->ConsumeIntegral(); cameraInfo.orientation = mFDP->ConsumeBool() ? mFDP->PickValueInArray(kValidOrientation) : mFDP->ConsumeIntegral(); Camera::getCameraInfo(cameraId, /*overrideToPortrait*/false, kDefaultDeviceId, /*devicePolicy*/0, &cameraInfo); mCamera->reconnect(); sp composerClient = new SurfaceComposerClient; sp surfaceControl = nullptr; if (mFDP->ConsumeBool()) { surfaceControl = composerClient->createSurface(String8("FUZZSURFACE"), 1280, 800, HAL_PIXEL_FORMAT_YV12); } else { surfaceControl = composerClient->createSurface( static_cast(mFDP->ConsumeRandomLengthString(kMaxBytes).c_str()) /* name */, mFDP->ConsumeIntegral() /* width */, mFDP->ConsumeIntegral() /* height */, mFDP->ConsumeIntegral() /* format */, mFDP->ConsumeIntegral() /* flags */); } if (mFDP->ConsumeBool()) { invokeSetParameters(); } sp surface = nullptr; if (surfaceControl) { surface = surfaceControl->getSurface(); } sp memoryDealer = nullptr; sp iMem = nullptr; sp cameraListener = nullptr; while (mFDP->remaining_bytes()) { auto callCameraAPIs = mFDP->PickValueInArray>({ [&]() { if (surfaceControl) { mCamera->setPreviewTarget(surface->getIGraphicBufferProducer()); } }, [&]() { if (surfaceControl) { mCamera->startPreview(); } }, [&]() { if (surfaceControl) { mCamera->stopPreview(); } }, [&]() { if (surfaceControl) { mCamera->stopPreview(); } }, [&]() { if (surfaceControl) { mCamera->previewEnabled(); } }, [&]() { if (surfaceControl) { mCamera->startRecording(); } }, [&]() { if (surfaceControl) { mCamera->stopRecording(); } }, [&]() { mCamera->lock(); }, [&]() { mCamera->unlock(); }, [&]() { mCamera->autoFocus(); }, [&]() { mCamera->cancelAutoFocus(); }, [&]() { int32_t msgType = mFDP->ConsumeIntegral(); mCamera->takePicture(msgType); }, [&]() { int32_t cmd; cmd = mFDP->ConsumeBool() ? mFDP->PickValueInArray(kValidCMD) : mFDP->ConsumeIntegral(); int32_t arg1 = mFDP->ConsumeIntegral(); int32_t arg2 = mFDP->ConsumeIntegral(); mCamera->sendCommand(cmd, arg1, arg2); }, [&]() { int32_t videoBufferMode = mFDP->ConsumeBool() ? mFDP->PickValueInArray(kValidVideoBufferMode) : mFDP->ConsumeIntegral(); mCamera->setVideoBufferMode(videoBufferMode); }, [&]() { if (surfaceControl) { mCamera->setVideoTarget(surface->getIGraphicBufferProducer()); } }, [&]() { cameraListener = sp::make(); mCamera->setListener(cameraListener); }, [&]() { int32_t previewCallbackFlag; previewCallbackFlag = mFDP->ConsumeBool() ? mFDP->PickValueInArray(kValidPreviewCallbackFlag) : mFDP->ConsumeIntegral(); mCamera->setPreviewCallbackFlags(previewCallbackFlag); }, [&]() { if (surfaceControl) { mCamera->setPreviewCallbackTarget(surface->getIGraphicBufferProducer()); } }, [&]() { mCamera->getRecordingProxy(); }, [&]() { int32_t mode = mFDP->ConsumeIntegral(); mCamera->setAudioRestriction(mode); }, [&]() { mCamera->getGlobalAudioRestriction(); }, [&]() { mCamera->recordingEnabled(); }, [&]() { memoryDealer = new MemoryDealer(kMemoryDealerSize); iMem = memoryDealer->allocate(kMemoryDealerSize); }, [&]() { int32_t msgTypeNC = mFDP->ConsumeIntegral(); int32_t ext = mFDP->ConsumeIntegral(); int32_t ext2 = mFDP->ConsumeIntegral(); mCamera->notifyCallback(msgTypeNC, ext, ext2); }, [&]() { int32_t msgTypeNC = mFDP->ConsumeIntegral(); int64_t timestamp = mFDP->ConsumeIntegral(); mCamera->dataCallbackTimestamp(timestamp, msgTypeNC, iMem); }, [&]() { int64_t timestamp = mFDP->ConsumeIntegral(); native_handle_t* handle = createNativeHandle(); mCamera->recordingFrameHandleCallbackTimestamp(timestamp, handle); }, [&]() { native_handle_t* handle = createNativeHandle(); mCamera->releaseRecordingFrameHandle(handle); }, [&]() { mCamera->releaseRecordingFrame(iMem); }, [&]() { std::vector handles; for (int8_t i = 0; i < mFDP->ConsumeIntegralInRange(kMinElements, kMaxElements); ++i) { native_handle_t* handle = createNativeHandle(); handles.push_back(handle); } mCamera->releaseRecordingFrameHandleBatch(handles); }, [&]() { std::vector handles; for (int8_t i = 0; i < mFDP->ConsumeIntegralInRange(kMinElements, kMaxElements); ++i) { native_handle_t* handle = createNativeHandle(); handles.push_back(handle); } std::vector timestamps; for (int8_t i = 0; i < mFDP->ConsumeIntegralInRange(kMinElements, kMaxElements); ++i) { timestamps.push_back(mFDP->ConsumeIntegral()); } mCamera->recordingFrameHandleCallbackTimestampBatch(timestamps, handles); }, }); callCameraAPIs(); } } void CameraFuzzer::process(const uint8_t* data, size_t size) { mFDP = new FuzzedDataProvider(data, size); invokeCamera(); delete mFDP; } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { sp cameraFuzzer = new CameraFuzzer(); cameraFuzzer->process(data, size); cameraFuzzer.clear(); return 0; }