1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "LargeParcelable"
18 
19 #include "LargeParcelableBase.h"
20 
21 #include "MappedFile.h"
22 #include "SharedMemory.h"
23 
24 #include <android-base/unique_fd.h>
25 #include <android/binder_auto_utils.h>
26 #include <android/binder_parcel.h>
27 #include <android/binder_parcel_utils.h>
28 #include <android/binder_status.h>
29 #include <utils/Errors.h>
30 #include <utils/Log.h>
31 
32 #include <stdint.h>
33 
34 #include <cstring>
35 #include <iostream>
36 #include <memory>
37 #include <optional>
38 #include <sstream>
39 #include <string>
40 
41 namespace android {
42 namespace automotive {
43 namespace car_binder_lib {
44 
45 using ::android::base::borrowed_fd;
46 using ::android::base::unique_fd;
47 using ::ndk::ScopedAParcel;
48 using ::ndk::ScopedFileDescriptor;
49 
scopedFdToBorrowedFd(const ScopedFileDescriptor & fd)50 borrowed_fd LargeParcelableBase::scopedFdToBorrowedFd(const ScopedFileDescriptor& fd) {
51     borrowed_fd memoryFd(fd.get());
52     return memoryFd;
53 }
54 
scopeFdToUniqueFd(ScopedFileDescriptor && fd)55 unique_fd LargeParcelableBase::scopeFdToUniqueFd(ScopedFileDescriptor&& fd) {
56     // ScopedFileDescriptor does not have release function, so we have to directly modify its
57     // underlying fd pointer to remove ownership.
58     unique_fd memoryFd(fd.get());
59     *(fd.getR()) = INVALID_MEMORY_FD;
60     return memoryFd;
61 }
62 
readFromParcel(const AParcel * in)63 binder_status_t LargeParcelableBase::readFromParcel(const AParcel* in) {
64     mHasDeserializedParcelable = false;
65 
66     // Make this compatible with stable AIDL
67     // payloadSize + Nullable Parcelable + Nullable ParcelFileDescriptor
68     int32_t startPosition = AParcel_getDataPosition(in);
69     int32_t totalPayloadSize;
70     if (binder_status_t status = AParcel_readInt32(in, &totalPayloadSize); status != STATUS_OK) {
71         ALOGE("failed to read Int32: %d", status);
72         return status;
73     }
74     if (binder_status_t status = deserialize(*in); status != STATUS_OK) {
75         ALOGE("failed to deserialize: %d", status);
76         return status;
77     }
78     int32_t sharedMemoryPosition = AParcel_getDataPosition(in);
79     ScopedFileDescriptor descriptor;
80     if (binder_status_t status = AParcel_readNullableParcelFileDescriptor(in, &descriptor);
81         status != STATUS_OK) {
82         ALOGE("invalid data, failed to read file descirptor: %d", status);
83         return status;
84     }
85     bool hasSharedMemory = (descriptor.get() != INVALID_MEMORY_FD);
86     if (hasSharedMemory) {
87         // Release descriptor to memoryFd.
88         unique_fd memoryFd = scopeFdToUniqueFd(std::move(descriptor));
89         if (binder_status_t status = deserializeSharedMemoryAndClose(std::move(memoryFd));
90             status != STATUS_OK) {
91             return status;
92         }
93     }
94     if (DBG_PAYLOAD) {
95         ALOGD("Read, start:%d totalPayloadSize:%d sharedMemoryPosition:%d hasSharedMemory:%d",
96               startPosition, totalPayloadSize, sharedMemoryPosition, hasSharedMemory);
97     }
98     mHasDeserializedParcelable = true;
99     return STATUS_OK;
100 }
101 
prepareSharedMemory(AParcel * parcel) const102 binder_status_t LargeParcelableBase::prepareSharedMemory(AParcel* parcel) const {
103     int32_t startPosition = AParcel_getDataPosition(parcel);
104     if (binder_status_t status = serializeMemoryFdOrPayload(parcel, nullptr); status != STATUS_OK) {
105         ALOGE("failed to serialize: %d", status);
106         return status;
107     }
108     int32_t payloadSize = AParcel_getDataPosition(parcel) - startPosition;
109     bool noSharedMemory = (payloadSize <= MAX_DIRECT_PAYLOAD_SIZE);
110     if (noSharedMemory) {
111         // Do nothing.
112         mNeedSharedMemory = false;
113         return STATUS_OK;
114     }
115     binder_status_t status;
116     std::unique_ptr<SharedMemory> sharedMemory =
117             serializeParcelToSharedMemory(*parcel, startPosition, payloadSize, &status);
118     if (status != STATUS_OK) {
119         ALOGE("failed to serialize parcel to shared memory: %d", status);
120         return status;
121     }
122     mNeedSharedMemory = true;
123     mSharedMemory = std::move(sharedMemory);
124     return STATUS_OK;
125 }
126 
writeToParcel(AParcel * dest) const127 binder_status_t LargeParcelableBase::writeToParcel(AParcel* dest) const {
128     // Make this compatible with stable AIDL
129     // payloadSize + Nullable Parcelable + Nullable ParcelFileDescriptor
130     int startPosition = AParcel_getDataPosition(dest);
131     if (!mNeedSharedMemory.has_value()) {
132         if (binder_status_t status = prepareSharedMemory(dest); status != STATUS_OK) {
133             ALOGE("failed to serialize payload to parcel: %d", status);
134             return status;
135         }
136     }
137     bool needSharedMemory = mNeedSharedMemory.value();
138     if (needSharedMemory) {
139         const SharedMemory* sharedMemory = mSharedMemory.get();
140         AParcel_setDataPosition(dest, startPosition);
141         if (binder_status_t status = serializeMemoryFdOrPayload(dest, sharedMemory);
142             status != STATUS_OK) {
143             ALOGE("failed to serialize shared memory fd to parcel: %d", status);
144             return status;
145         }
146     }
147 
148     int32_t totalPayloadSize = AParcel_getDataPosition(dest) - startPosition;
149     if (DBG_PAYLOAD) {
150         ALOGD("Write, start:%d totalPayloadSize:%d hasSharedMemory:%d", startPosition,
151               totalPayloadSize, needSharedMemory);
152     }
153     return OK;
154 }
155 
deserializeSharedMemoryAndClose(unique_fd memoryFd)156 binder_status_t LargeParcelableBase::deserializeSharedMemoryAndClose(unique_fd memoryFd) {
157     ScopedAParcel parcel(AParcel_create());
158     // This would close memoryFd after destruction.
159     SharedMemory sharedMemory(std::move(memoryFd));
160     if (!sharedMemory.isValid()) {
161         ALOGE("invalid shared memory fd, status: %d", sharedMemory.getErr());
162         return STATUS_FDS_NOT_ALLOWED;
163     }
164     if (binder_status_t status = copyFromSharedMemory(sharedMemory, parcel.get());
165         status != STATUS_OK) {
166         return status;
167     }
168     int32_t payloadSize;
169     if (binder_status_t status = AParcel_readInt32(parcel.get(), &payloadSize);
170         status != STATUS_OK) {
171         ALOGE("failed to read Int32: %d", status);
172         if (DBG_PAYLOAD) {
173             ALOGD("parse shared memory file, payload size: %d", payloadSize);
174         }
175         return status;
176     }
177     if (binder_status_t status = deserialize(*(parcel.get())); status != STATUS_OK) {
178         return status;
179     }
180     // There is an additional 0 for null file descriptor in the parcel we would ignore.
181     return STATUS_OK;
182 }
183 
copyFromSharedMemory(const SharedMemory & sharedMemory,AParcel * parcel)184 binder_status_t LargeParcelableBase::copyFromSharedMemory(const SharedMemory& sharedMemory,
185                                                           AParcel* parcel) {
186     std::unique_ptr<MappedFile> mappedFile = sharedMemory.mapReadOnly();
187     size_t mappedFileSize = mappedFile->getSize();
188     if (!mappedFile->isValid()) {
189         ALOGE("failed to map file for size: %zu, error: %d", sharedMemory.getSize(),
190               mappedFile->getErr());
191         return STATUS_FDS_NOT_ALLOWED;
192     }
193     if (binder_status_t status =
194                 AParcel_unmarshal(parcel, static_cast<const uint8_t*>(mappedFile->getAddr()),
195                                   mappedFileSize);
196         status != STATUS_OK) {
197         return status;
198     }
199     AParcel_setDataPosition(parcel, 0);
200     if (DBG_PAYLOAD) {
201         size_t dumpSize = std::min(DBG_DUMP_LENGTH, mappedFileSize);
202         bool truncated = (dumpSize < mappedFileSize);
203         std::stringbuf buffer;
204         std::ostream bd(&buffer);
205         if (truncated) {
206             bd << "unmarshalled(truncated):";
207         } else {
208             bd << "unmarshalled:";
209         }
210         bd << std::hex;
211         size_t parcelStartPosition = AParcel_getDataPosition(parcel);
212         std::unique_ptr<uint8_t[]> fromParcel(new uint8_t[mappedFileSize]);
213         if (binder_status_t status = AParcel_marshal(parcel, fromParcel.get(), 0, dumpSize);
214             status != STATUS_OK) {
215             ALOGE("failed to marshal parcel: %d", status);
216             return status;
217         }
218         for (size_t i = 0; i < dumpSize; i++) {
219             bd << static_cast<int>(fromParcel[i]);
220             if (i != dumpSize - 1) {
221                 bd << ",";
222             }
223         }
224         bd << "=startPosition:" << parcelStartPosition;
225         bd.flush();
226         ALOGD("%s", buffer.str().c_str());
227         AParcel_setDataPosition(parcel, parcelStartPosition);
228     }
229     return STATUS_OK;
230 }
231 
writeSharedMemoryCompatibleToParcel(const SharedMemory * sharedMemory,AParcel * dest)232 binder_status_t LargeParcelableBase::writeSharedMemoryCompatibleToParcel(
233         const SharedMemory* sharedMemory, AParcel* dest) {
234     ScopedFileDescriptor descriptor;
235     if (sharedMemory == nullptr) {
236         return ::ndk::AParcel_writeNullableParcelFileDescriptor(dest, descriptor);
237     }
238     // non-null case
239     unique_fd fd = sharedMemory->getDupFd();
240     descriptor.set(fd.release());
241     return ::ndk::AParcel_writeNullableParcelFileDescriptor(dest, descriptor);
242 }
243 
serializeParcelToSharedMemory(const AParcel & p,int32_t start,int32_t size,binder_status_t * outStatus)244 std::unique_ptr<SharedMemory> LargeParcelableBase::serializeParcelToSharedMemory(
245         const AParcel& p, int32_t start, int32_t size, binder_status_t* outStatus) {
246     std::unique_ptr<SharedMemory> memory(new SharedMemory(size));
247     if (!memory->isValid()) {
248         ALOGE("failed to create memfile for size: %d, status: %d", size, memory->getErr());
249         *outStatus = STATUS_UNKNOWN_ERROR;
250         return std::unique_ptr<SharedMemory>(nullptr);
251     }
252     // This would be unmapped after function returns.
253     std::unique_ptr<MappedFile> buffer = memory->mapReadWrite();
254     if (!buffer->isValid()) {
255         ALOGE("failed to map shared memory as read write for size: %d, status: %d", size,
256               buffer->getErr());
257         *outStatus = STATUS_UNKNOWN_ERROR;
258         return std::unique_ptr<SharedMemory>(nullptr);
259     }
260     if (binder_status_t status =
261                 AParcel_marshal(&p, static_cast<uint8_t*>(buffer->getWriteAddr()), start, size);
262         status != STATUS_OK) {
263         ALOGE("failed to marshal parcel: %d", status);
264         *outStatus = status;
265         return std::unique_ptr<SharedMemory>(nullptr);
266     }
267     buffer->sync();
268 
269     if (status_t astatus = memory->lock() != OK) {
270         ALOGE("Failed to set read-only protection on shared memory: %d", astatus);
271         *outStatus = STATUS_UNKNOWN_ERROR;
272         return std::unique_ptr<SharedMemory>(nullptr);
273     }
274 
275     if (DBG_PAYLOAD) {
276         size_t dumpSize = std::min(DBG_DUMP_LENGTH, static_cast<size_t>(size));
277         std::stringbuf log;
278         std::ostream bd(&log);
279         const uint8_t* addr = static_cast<uint8_t*>(buffer->getWriteAddr());
280         bd << "unmarshalled:" << std::hex;
281         for (size_t i = 0; i < dumpSize; i++) {
282             bd << static_cast<int>(addr[i]);
283             if (i != dumpSize - 1) {
284                 bd << ",";
285             }
286         }
287         bd.flush();
288         ALOGD("%s", log.str().c_str());
289     }
290     *outStatus = STATUS_OK;
291     return memory;
292 }
293 
updatePayloadSize(AParcel * dest,int32_t startPosition)294 int32_t LargeParcelableBase::updatePayloadSize(AParcel* dest, int32_t startPosition) {
295     int32_t lastPosition = AParcel_getDataPosition(dest);
296     int32_t totalPayloadSize = lastPosition - startPosition;
297     AParcel_setDataPosition(dest, startPosition);
298     AParcel_writeInt32(dest, totalPayloadSize);
299     AParcel_setDataPosition(dest, lastPosition);
300     return totalPayloadSize;
301 }
302 
hasDeserializedParcelable() const303 bool LargeParcelableBase::hasDeserializedParcelable() const {
304     return mHasDeserializedParcelable;
305 }
306 
getParcelFromMemoryFile(const ScopedFileDescriptor & fd,AParcel * parcel)307 binder_status_t LargeParcelableBase::getParcelFromMemoryFile(const ScopedFileDescriptor& fd,
308                                                              AParcel* parcel) {
309     borrowed_fd memoryFd = scopedFdToBorrowedFd(fd);
310     SharedMemory sharedMemory(memoryFd);
311     if (!sharedMemory.isValid()) {
312         ALOGE("invalid shared memory fd, status: %d", sharedMemory.getErr());
313         return STATUS_FDS_NOT_ALLOWED;
314     }
315     if (binder_status_t status = copyFromSharedMemory(sharedMemory, parcel); status != STATUS_OK) {
316         ALOGE("failed to copy from shared memory: %d", status);
317         return status;
318     }
319     return STATUS_OK;
320 }
321 
parcelToMemoryFile(const AParcel & parcel,ScopedFileDescriptor * sharedMemoryFd)322 binder_status_t LargeParcelableBase::parcelToMemoryFile(const AParcel& parcel,
323                                                         ScopedFileDescriptor* sharedMemoryFd) {
324     int32_t payloadSize = AParcel_getDataPosition(&parcel);
325     binder_status_t status = STATUS_OK;
326     std::unique_ptr<SharedMemory> sharedMemory =
327             serializeParcelToSharedMemory(parcel, /*start=*/0, payloadSize, &status);
328     if (status != STATUS_OK) {
329         ALOGE("failed to serialize parcel to shared memory: %d", status);
330         return status;
331     }
332 
333     unique_fd fd(sharedMemory->getDupFd());
334     sharedMemoryFd->set(fd.release());
335     return STATUS_OK;
336 }
337 
serializeMemoryFdOrPayload(AParcel * dest,const SharedMemory * sharedMemory) const338 binder_status_t LargeParcelableBase::serializeMemoryFdOrPayload(
339         AParcel* dest, const SharedMemory* sharedMemory) const {
340     // This is compatible with stable AIDL serialization:
341     // payload size + payload + nullable fd
342     // The shared Memory file might contain a serialized parcel created from this function.
343     int32_t startPosition = AParcel_getDataPosition(dest);
344     AParcel_writeInt32(dest, 0);
345     if (sharedMemory == nullptr) {
346         if (binder_status_t status = serialize(dest); status != STATUS_OK) {
347             ALOGE("failed to serialize: %d", status);
348             return status;
349         }
350     } else {
351         serializeNullPayload(dest);
352     }
353 
354     if (DBG_PAYLOAD) {
355         int sharedMemoryPosition = AParcel_getDataPosition(dest) - startPosition;
356         ALOGD("Serialize shared memory fd: sharedMemoryPosition:%d hasSharedMemory:%d",
357               sharedMemoryPosition, sharedMemory != nullptr);
358     }
359     if (binder_status_t status = writeSharedMemoryCompatibleToParcel(sharedMemory, dest);
360         status != STATUS_OK) {
361         ALOGE("failed to write file descriptor to parcel: %d", status);
362         return status;
363     }
364     updatePayloadSize(dest, startPosition);
365     return STATUS_OK;
366 }
367 
368 }  // namespace car_binder_lib
369 }  // namespace automotive
370 }  // namespace android
371