/* * Copyright (C) 2021 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. */ #define LOG_TAG "LargeParcelable" #include "LargeParcelableBase.h" #include "MappedFile.h" #include "SharedMemory.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { namespace automotive { namespace car_binder_lib { using ::android::base::borrowed_fd; using ::android::base::unique_fd; using ::ndk::ScopedAParcel; using ::ndk::ScopedFileDescriptor; borrowed_fd LargeParcelableBase::scopedFdToBorrowedFd(const ScopedFileDescriptor& fd) { borrowed_fd memoryFd(fd.get()); return memoryFd; } unique_fd LargeParcelableBase::scopeFdToUniqueFd(ScopedFileDescriptor&& fd) { // ScopedFileDescriptor does not have release function, so we have to directly modify its // underlying fd pointer to remove ownership. unique_fd memoryFd(fd.get()); *(fd.getR()) = INVALID_MEMORY_FD; return memoryFd; } binder_status_t LargeParcelableBase::readFromParcel(const AParcel* in) { mHasDeserializedParcelable = false; // Make this compatible with stable AIDL // payloadSize + Nullable Parcelable + Nullable ParcelFileDescriptor int32_t startPosition = AParcel_getDataPosition(in); int32_t totalPayloadSize; if (binder_status_t status = AParcel_readInt32(in, &totalPayloadSize); status != STATUS_OK) { ALOGE("failed to read Int32: %d", status); return status; } if (binder_status_t status = deserialize(*in); status != STATUS_OK) { ALOGE("failed to deserialize: %d", status); return status; } int32_t sharedMemoryPosition = AParcel_getDataPosition(in); ScopedFileDescriptor descriptor; if (binder_status_t status = AParcel_readNullableParcelFileDescriptor(in, &descriptor); status != STATUS_OK) { ALOGE("invalid data, failed to read file descirptor: %d", status); return status; } bool hasSharedMemory = (descriptor.get() != INVALID_MEMORY_FD); if (hasSharedMemory) { // Release descriptor to memoryFd. unique_fd memoryFd = scopeFdToUniqueFd(std::move(descriptor)); if (binder_status_t status = deserializeSharedMemoryAndClose(std::move(memoryFd)); status != STATUS_OK) { return status; } } if (DBG_PAYLOAD) { ALOGD("Read, start:%d totalPayloadSize:%d sharedMemoryPosition:%d hasSharedMemory:%d", startPosition, totalPayloadSize, sharedMemoryPosition, hasSharedMemory); } mHasDeserializedParcelable = true; return STATUS_OK; } binder_status_t LargeParcelableBase::prepareSharedMemory(AParcel* parcel) const { int32_t startPosition = AParcel_getDataPosition(parcel); if (binder_status_t status = serializeMemoryFdOrPayload(parcel, nullptr); status != STATUS_OK) { ALOGE("failed to serialize: %d", status); return status; } int32_t payloadSize = AParcel_getDataPosition(parcel) - startPosition; bool noSharedMemory = (payloadSize <= MAX_DIRECT_PAYLOAD_SIZE); if (noSharedMemory) { // Do nothing. mNeedSharedMemory = false; return STATUS_OK; } binder_status_t status; std::unique_ptr sharedMemory = serializeParcelToSharedMemory(*parcel, startPosition, payloadSize, &status); if (status != STATUS_OK) { ALOGE("failed to serialize parcel to shared memory: %d", status); return status; } mNeedSharedMemory = true; mSharedMemory = std::move(sharedMemory); return STATUS_OK; } binder_status_t LargeParcelableBase::writeToParcel(AParcel* dest) const { // Make this compatible with stable AIDL // payloadSize + Nullable Parcelable + Nullable ParcelFileDescriptor int startPosition = AParcel_getDataPosition(dest); if (!mNeedSharedMemory.has_value()) { if (binder_status_t status = prepareSharedMemory(dest); status != STATUS_OK) { ALOGE("failed to serialize payload to parcel: %d", status); return status; } } bool needSharedMemory = mNeedSharedMemory.value(); if (needSharedMemory) { const SharedMemory* sharedMemory = mSharedMemory.get(); AParcel_setDataPosition(dest, startPosition); if (binder_status_t status = serializeMemoryFdOrPayload(dest, sharedMemory); status != STATUS_OK) { ALOGE("failed to serialize shared memory fd to parcel: %d", status); return status; } } int32_t totalPayloadSize = AParcel_getDataPosition(dest) - startPosition; if (DBG_PAYLOAD) { ALOGD("Write, start:%d totalPayloadSize:%d hasSharedMemory:%d", startPosition, totalPayloadSize, needSharedMemory); } return OK; } binder_status_t LargeParcelableBase::deserializeSharedMemoryAndClose(unique_fd memoryFd) { ScopedAParcel parcel(AParcel_create()); // This would close memoryFd after destruction. SharedMemory sharedMemory(std::move(memoryFd)); if (!sharedMemory.isValid()) { ALOGE("invalid shared memory fd, status: %d", sharedMemory.getErr()); return STATUS_FDS_NOT_ALLOWED; } if (binder_status_t status = copyFromSharedMemory(sharedMemory, parcel.get()); status != STATUS_OK) { return status; } int32_t payloadSize; if (binder_status_t status = AParcel_readInt32(parcel.get(), &payloadSize); status != STATUS_OK) { ALOGE("failed to read Int32: %d", status); if (DBG_PAYLOAD) { ALOGD("parse shared memory file, payload size: %d", payloadSize); } return status; } if (binder_status_t status = deserialize(*(parcel.get())); status != STATUS_OK) { return status; } // There is an additional 0 for null file descriptor in the parcel we would ignore. return STATUS_OK; } binder_status_t LargeParcelableBase::copyFromSharedMemory(const SharedMemory& sharedMemory, AParcel* parcel) { std::unique_ptr mappedFile = sharedMemory.mapReadOnly(); size_t mappedFileSize = mappedFile->getSize(); if (!mappedFile->isValid()) { ALOGE("failed to map file for size: %zu, error: %d", sharedMemory.getSize(), mappedFile->getErr()); return STATUS_FDS_NOT_ALLOWED; } if (binder_status_t status = AParcel_unmarshal(parcel, static_cast(mappedFile->getAddr()), mappedFileSize); status != STATUS_OK) { return status; } AParcel_setDataPosition(parcel, 0); if (DBG_PAYLOAD) { size_t dumpSize = std::min(DBG_DUMP_LENGTH, mappedFileSize); bool truncated = (dumpSize < mappedFileSize); std::stringbuf buffer; std::ostream bd(&buffer); if (truncated) { bd << "unmarshalled(truncated):"; } else { bd << "unmarshalled:"; } bd << std::hex; size_t parcelStartPosition = AParcel_getDataPosition(parcel); std::unique_ptr fromParcel(new uint8_t[mappedFileSize]); if (binder_status_t status = AParcel_marshal(parcel, fromParcel.get(), 0, dumpSize); status != STATUS_OK) { ALOGE("failed to marshal parcel: %d", status); return status; } for (size_t i = 0; i < dumpSize; i++) { bd << static_cast(fromParcel[i]); if (i != dumpSize - 1) { bd << ","; } } bd << "=startPosition:" << parcelStartPosition; bd.flush(); ALOGD("%s", buffer.str().c_str()); AParcel_setDataPosition(parcel, parcelStartPosition); } return STATUS_OK; } binder_status_t LargeParcelableBase::writeSharedMemoryCompatibleToParcel( const SharedMemory* sharedMemory, AParcel* dest) { ScopedFileDescriptor descriptor; if (sharedMemory == nullptr) { return ::ndk::AParcel_writeNullableParcelFileDescriptor(dest, descriptor); } // non-null case unique_fd fd = sharedMemory->getDupFd(); descriptor.set(fd.release()); return ::ndk::AParcel_writeNullableParcelFileDescriptor(dest, descriptor); } std::unique_ptr LargeParcelableBase::serializeParcelToSharedMemory( const AParcel& p, int32_t start, int32_t size, binder_status_t* outStatus) { std::unique_ptr memory(new SharedMemory(size)); if (!memory->isValid()) { ALOGE("failed to create memfile for size: %d, status: %d", size, memory->getErr()); *outStatus = STATUS_UNKNOWN_ERROR; return std::unique_ptr(nullptr); } // This would be unmapped after function returns. std::unique_ptr buffer = memory->mapReadWrite(); if (!buffer->isValid()) { ALOGE("failed to map shared memory as read write for size: %d, status: %d", size, buffer->getErr()); *outStatus = STATUS_UNKNOWN_ERROR; return std::unique_ptr(nullptr); } if (binder_status_t status = AParcel_marshal(&p, static_cast(buffer->getWriteAddr()), start, size); status != STATUS_OK) { ALOGE("failed to marshal parcel: %d", status); *outStatus = status; return std::unique_ptr(nullptr); } buffer->sync(); if (status_t astatus = memory->lock() != OK) { ALOGE("Failed to set read-only protection on shared memory: %d", astatus); *outStatus = STATUS_UNKNOWN_ERROR; return std::unique_ptr(nullptr); } if (DBG_PAYLOAD) { size_t dumpSize = std::min(DBG_DUMP_LENGTH, static_cast(size)); std::stringbuf log; std::ostream bd(&log); const uint8_t* addr = static_cast(buffer->getWriteAddr()); bd << "unmarshalled:" << std::hex; for (size_t i = 0; i < dumpSize; i++) { bd << static_cast(addr[i]); if (i != dumpSize - 1) { bd << ","; } } bd.flush(); ALOGD("%s", log.str().c_str()); } *outStatus = STATUS_OK; return memory; } int32_t LargeParcelableBase::updatePayloadSize(AParcel* dest, int32_t startPosition) { int32_t lastPosition = AParcel_getDataPosition(dest); int32_t totalPayloadSize = lastPosition - startPosition; AParcel_setDataPosition(dest, startPosition); AParcel_writeInt32(dest, totalPayloadSize); AParcel_setDataPosition(dest, lastPosition); return totalPayloadSize; } bool LargeParcelableBase::hasDeserializedParcelable() const { return mHasDeserializedParcelable; } binder_status_t LargeParcelableBase::getParcelFromMemoryFile(const ScopedFileDescriptor& fd, AParcel* parcel) { borrowed_fd memoryFd = scopedFdToBorrowedFd(fd); SharedMemory sharedMemory(memoryFd); if (!sharedMemory.isValid()) { ALOGE("invalid shared memory fd, status: %d", sharedMemory.getErr()); return STATUS_FDS_NOT_ALLOWED; } if (binder_status_t status = copyFromSharedMemory(sharedMemory, parcel); status != STATUS_OK) { ALOGE("failed to copy from shared memory: %d", status); return status; } return STATUS_OK; } binder_status_t LargeParcelableBase::parcelToMemoryFile(const AParcel& parcel, ScopedFileDescriptor* sharedMemoryFd) { int32_t payloadSize = AParcel_getDataPosition(&parcel); binder_status_t status = STATUS_OK; std::unique_ptr sharedMemory = serializeParcelToSharedMemory(parcel, /*start=*/0, payloadSize, &status); if (status != STATUS_OK) { ALOGE("failed to serialize parcel to shared memory: %d", status); return status; } unique_fd fd(sharedMemory->getDupFd()); sharedMemoryFd->set(fd.release()); return STATUS_OK; } binder_status_t LargeParcelableBase::serializeMemoryFdOrPayload( AParcel* dest, const SharedMemory* sharedMemory) const { // This is compatible with stable AIDL serialization: // payload size + payload + nullable fd // The shared Memory file might contain a serialized parcel created from this function. int32_t startPosition = AParcel_getDataPosition(dest); AParcel_writeInt32(dest, 0); if (sharedMemory == nullptr) { if (binder_status_t status = serialize(dest); status != STATUS_OK) { ALOGE("failed to serialize: %d", status); return status; } } else { serializeNullPayload(dest); } if (DBG_PAYLOAD) { int sharedMemoryPosition = AParcel_getDataPosition(dest) - startPosition; ALOGD("Serialize shared memory fd: sharedMemoryPosition:%d hasSharedMemory:%d", sharedMemoryPosition, sharedMemory != nullptr); } if (binder_status_t status = writeSharedMemoryCompatibleToParcel(sharedMemory, dest); status != STATUS_OK) { ALOGE("failed to write file descriptor to parcel: %d", status); return status; } updatePayloadSize(dest, startPosition); return STATUS_OK; } } // namespace car_binder_lib } // namespace automotive } // namespace android