Merge "libpropertyinfoparser: only use system_shared_libs for bionic variant"
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 0288d85..6f874a6 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -779,7 +779,7 @@
nullptr /* out_public_key_data */, &verify_result);
ASSERT_EQ(0, close(hash_modified_fd.release()));
EXPECT_NE(nullptr, vbmeta);
- EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
// Modifies the auxiliary data block.
@@ -795,7 +795,7 @@
nullptr /* out_public_key_data */, &verify_result);
ASSERT_EQ(0, close(aux_modified_fd.release()));
EXPECT_NE(nullptr, vbmeta);
- EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
// Resets previous modification by setting offset to -1, and checks the verification can pass.
@@ -807,7 +807,7 @@
nullptr /* out_public_key_data */, &verify_result);
ASSERT_EQ(0, close(ok_fd.release()));
EXPECT_NE(nullptr, vbmeta);
- EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
}
diff --git a/fs_mgr/liblp/OWNERS b/fs_mgr/liblp/OWNERS
new file mode 100644
index 0000000..6a95eb2
--- /dev/null
+++ b/fs_mgr/liblp/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 391836
+dvander@google.com
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 52324ba..3e8d9c1 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1452,7 +1452,7 @@
std::vector<std::string>* snapuserd_argv) {
LOG(INFO) << "Performing transition for snapuserd.";
- // Don't use EnsuerSnapuserdConnected() because this is called from init,
+ // Don't use EnsureSnapuserdConnected() because this is called from init,
// and attempting to do so will deadlock.
if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) {
snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
@@ -1509,8 +1509,15 @@
continue;
}
+ std::string source_device_name;
+ if (snapshot_status.old_partition_size() > 0) {
+ source_device_name = GetSourceDeviceName(snapshot);
+ } else {
+ source_device_name = GetBaseDeviceName(snapshot);
+ }
+
std::string source_device;
- if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) {
+ if (!dm.GetDmDevicePathByName(source_device_name, &source_device)) {
LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
continue;
}
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 057e5b19a..43c7fe2 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2118,14 +2118,24 @@
// After reboot, init does first stage mount.
auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
+
+ ASSERT_TRUE(init->EnsureSnapuserdConnected());
+ init->set_use_first_stage_snapuserd(true);
+
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
// Check that the target partitions have the same content.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
+ std::vector<std::string> partitions = {"sys_b", "vnd_b", "prd_b", "dlkm_b"};
+ for (const auto& name : partitions) {
ASSERT_TRUE(IsPartitionUnchanged(name));
}
+ ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
+ for (const auto& name : partitions) {
+ ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(name + "-user-cow-init"));
+ }
+
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
diff --git a/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
index f4aef44..bff0a50 100644
--- a/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
@@ -108,6 +108,7 @@
void MergeInterruptFixed(int duration);
void MergeInterruptRandomly(int max_duration);
void ReadDmUserBlockWithoutDaemon();
+ void ReadLastBlock();
std::string snapshot_dev() const { return snapshot_dev_->path(); }
@@ -256,6 +257,73 @@
}
}
+void CowSnapuserdTest::ReadLastBlock() {
+ unique_fd rnd_fd;
+ total_base_size_ = BLOCK_SZ * 2;
+
+ base_fd_ = CreateTempFile("base_device", total_base_size_);
+ ASSERT_GE(base_fd_, 0);
+
+ rnd_fd.reset(open("/dev/random", O_RDONLY));
+ ASSERT_TRUE(rnd_fd > 0);
+
+ std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+ for (size_t j = 0; j < ((total_base_size_) / BLOCK_SZ); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), BLOCK_SZ, 0), true);
+ ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), BLOCK_SZ), true);
+ }
+
+ ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
+
+ base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
+ ASSERT_TRUE(base_loop_->valid());
+
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(total_base_size_);
+ loff_t offset = 0;
+
+ // Fill random data
+ for (size_t j = 0; j < (total_base_size_ / BLOCK_SZ); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, BLOCK_SZ, 0),
+ true);
+
+ offset += BLOCK_SZ;
+ }
+
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+ ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
+ ASSERT_TRUE(writer.AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
+
+ ASSERT_TRUE(writer.Finalize());
+
+ SetDeviceControlName();
+
+ StartSnapuserdDaemon();
+ InitCowDevice();
+
+ CreateDmUserDevice();
+ InitDaemon();
+
+ CreateSnapshotDevice();
+
+ unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY));
+ ASSERT_TRUE(snapshot_fd > 0);
+
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+ offset = 7680;
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), 512, offset), true);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)random_buffer_1_.get() + offset, 512), 0);
+}
+
void CowSnapuserdTest::CreateBaseDevice() {
unique_fd rnd_fd;
@@ -1143,6 +1211,12 @@
harness.Shutdown();
}
+TEST(Snapuserd_Test, Snapshot_END_IO_TEST) {
+ CowSnapuserdTest harness;
+ harness.ReadLastBlock();
+ harness.Shutdown();
+}
+
TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) {
CowSnapuserdTest harness;
ASSERT_TRUE(harness.SetupCopyOverlap_1());
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
index cdf9fe7..5d184ad 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
@@ -350,16 +350,36 @@
it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
Snapuserd::compare);
- if (!(it != chunk_vec.end())) {
- SNAP_LOG(ERROR) << "ReadData: Sector " << sector << " not found in chunk_vec";
- return -1;
+ bool read_end_of_device = false;
+ if (it == chunk_vec.end()) {
+ // |-------|-------|-------|
+ // 0 1 2 3
+ //
+ // Block 0 - op 1
+ // Block 1 - op 2
+ // Block 2 - op 3
+ //
+ // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
+ //
+ // Each block is 4k bytes. Thus, the last block will span 8 sectors
+ // ranging till block 3 (However, block 3 won't be in chunk_vec as
+ // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
+ // spanning between block 2 and block 3, we need to step back
+ // and get hold of the last element.
+ //
+ // Additionally, dm-snapshot makes sure that I/O request beyond block 3
+ // will not be routed to the daemon. Hence, it is safe to assume that
+ // if a sector is not available in the chunk_vec, the I/O falls in the
+ // end of region.
+ it = std::prev(chunk_vec.end());
+ read_end_of_device = true;
}
// We didn't find the required sector; hence find the previous sector
// as lower_bound will gives us the value greater than
// the requested sector
if (it->first != sector) {
- if (it != chunk_vec.begin()) {
+ if (it != chunk_vec.begin() && !read_end_of_device) {
--it;
}
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 50eee19..9a47f6b 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -46,14 +46,6 @@
HealthdDraw::HealthdDraw(animation* anim)
: kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
- int ret = gr_init();
-
- if (ret < 0) {
- LOGE("gr_init failed\n");
- graphics_available = false;
- return;
- }
-
graphics_available = true;
sys_font = gr_sys_font();
if (sys_font == nullptr) {
@@ -235,3 +227,11 @@
LOGW("Charging, level unknown\n");
}
}
+
+std::unique_ptr<HealthdDraw> HealthdDraw::Create(animation *anim) {
+ if (gr_init() < 0) {
+ LOGE("gr_init failed\n");
+ return nullptr;
+ }
+ return std::unique_ptr<HealthdDraw>(new HealthdDraw(anim));
+}
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 7c847bd..0b48ce8 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -26,8 +26,6 @@
class HealthdDraw {
public:
- // Configures font using given animation.
- HealthdDraw(animation* anim);
virtual ~HealthdDraw();
// Redraws screen.
@@ -36,6 +34,8 @@
// Blanks screen if true, unblanks if false.
virtual void blank_screen(bool blank);
+ static std::unique_ptr<HealthdDraw> Create(animation *anim);
+
protected:
virtual void clear_screen();
@@ -76,6 +76,10 @@
// true if minui init'ed OK, false if minui init failed
bool graphics_available;
+
+ private:
+ // Configures font using given animation.
+ HealthdDraw(animation* anim);
};
#endif // HEALTHD_DRAW_H
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index e95efc0..3ea90b0 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -218,9 +218,7 @@
char* ptr;
size_t len;
- LOGW("\n");
LOGW("*************** LAST KMSG ***************\n");
- LOGW("\n");
const char* kmsg[] = {
// clang-format off
"/sys/fs/pstore/console-ramoops-0",
@@ -263,9 +261,7 @@
}
out:
- LOGW("\n");
LOGW("************* END LAST KMSG *************\n");
- LOGW("\n");
}
static int request_suspend(bool enable) {
@@ -325,7 +321,8 @@
}
}
- healthd_draw_.reset(new HealthdDraw(&batt_anim_));
+ healthd_draw_ = HealthdDraw::Create(&batt_anim_);
+ if (healthd_draw_ == nullptr) return;
if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
healthd_draw_->blank_screen(true);
diff --git a/init/README.md b/init/README.md
index f447ab2..58a8d6b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -693,7 +693,7 @@
fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
under /odm/etc, /vendor/etc, or / at runtime, in that order.
-`verity_update_state <mount-point>`
+`verity_update_state`
> Internal implementation detail used to update dm-verity state and
set the partition._mount-point_.verified properties used by adb remount
because fs\_mgr can't set them directly itself.
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 14e3e35..292425a 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -20,6 +20,16 @@
namespace android {
+namespace {
+
+constexpr uint64_t WAKE_EVENT_FD_SEQ = 1;
+
+epoll_event createEpollEvent(uint32_t events, uint64_t seq) {
+ return {.events = events, .data = {.u64 = seq}};
+}
+
+} // namespace
+
// --- WeakMessageHandler ---
WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :
@@ -64,7 +74,7 @@
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
- mNextRequestSeq(0),
+ mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
@@ -137,22 +147,17 @@
mEpollFd.reset();
}
- // Allocate the new epoll instance and register the wake pipe.
+ // Allocate the new epoll instance and register the WakeEventFd.
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
- struct epoll_event eventItem;
- memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem.events = EPOLLIN;
- eventItem.data.fd = mWakeEventFd.get();
- int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
+ epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
+ int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
strerror(errno));
- for (size_t i = 0; i < mRequests.size(); i++) {
- const Request& request = mRequests.valueAt(i);
- struct epoll_event eventItem;
- request.initEventItem(&eventItem);
+ for (const auto& [seq, request] : mRequests) {
+ epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
if (epollResult < 0) {
@@ -276,26 +281,28 @@
#endif
for (int i = 0; i < eventCount; i++) {
- int fd = eventItems[i].data.fd;
+ const SequenceNumber seq = eventItems[i].data.u64;
uint32_t epollEvents = eventItems[i].events;
- if (fd == mWakeEventFd.get()) {
+ if (seq == WAKE_EVENT_FD_SEQ) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
- ssize_t requestIndex = mRequests.indexOfKey(fd);
- if (requestIndex >= 0) {
+ const auto& request_it = mRequests.find(seq);
+ if (request_it != mRequests.end()) {
+ const auto& request = request_it->second;
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
- pushResponse(events, mRequests.valueAt(requestIndex));
+ mResponses.push({.seq = seq, .events = events, .request = request});
} else {
- ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
- "no longer registered.", epollEvents, fd);
+ ALOGW("Ignoring unexpected epoll events 0x%x for sequence number %" PRIu64
+ " that is no longer registered.",
+ epollEvents, seq);
}
}
}
@@ -354,7 +361,8 @@
// we need to be a little careful when removing the file descriptor afterwards.
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
- removeFd(fd, response.request.seq);
+ AutoMutex _l(mLock);
+ removeSequenceNumberLocked(response.seq);
}
// Clear the callback reference in the response structure promptly because we
@@ -416,13 +424,6 @@
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}
-void Looper::pushResponse(int events, const Request& request) {
- Response response;
- response.events = events;
- response.request = request;
- mResponses.push(response);
-}
-
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : nullptr, data);
}
@@ -449,27 +450,27 @@
{ // acquire lock
AutoMutex _l(mLock);
+ // There is a sequence number reserved for the WakeEventFd.
+ if (mNextRequestSeq == WAKE_EVENT_FD_SEQ) mNextRequestSeq++;
+ const SequenceNumber seq = mNextRequestSeq++;
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
- request.seq = mNextRequestSeq++;
request.callback = callback;
request.data = data;
- if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1
- struct epoll_event eventItem;
- request.initEventItem(&eventItem);
-
- ssize_t requestIndex = mRequests.indexOfKey(fd);
- if (requestIndex < 0) {
+ epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
+ auto seq_it = mSequenceNumberByFd.find(fd);
+ if (seq_it == mSequenceNumberByFd.end()) {
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
return -1;
}
- mRequests.add(fd, request);
+ mRequests.emplace(seq, request);
+ mSequenceNumberByFd.emplace(fd, seq);
} else {
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
if (epollResult < 0) {
@@ -486,7 +487,7 @@
// set from scratch because it may contain an old file handle that we are
// now unable to remove since its file descriptor is no longer valid.
// No such problem would have occurred if we were using the poll system
- // call instead, but that approach carries others disadvantages.
+ // call instead, but that approach carries other disadvantages.
#if DEBUG_CALLBACKS
ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
"being recycled, falling back on EPOLL_CTL_ADD: %s",
@@ -504,71 +505,69 @@
return -1;
}
}
- mRequests.replaceValueAt(requestIndex, request);
+ const SequenceNumber oldSeq = seq_it->second;
+ mRequests.erase(oldSeq);
+ mRequests.emplace(seq, request);
+ seq_it->second = seq;
}
} // release lock
return 1;
}
int Looper::removeFd(int fd) {
- return removeFd(fd, -1);
+ AutoMutex _l(mLock);
+ const auto& it = mSequenceNumberByFd.find(fd);
+ if (it == mSequenceNumberByFd.end()) {
+ return 0;
+ }
+ return removeSequenceNumberLocked(it->second);
}
-int Looper::removeFd(int fd, int seq) {
+int Looper::removeSequenceNumberLocked(SequenceNumber seq) {
#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - fd=%d, seq=%d", this, fd, seq);
+ ALOGD("%p ~ removeFd - fd=%d, seq=%u", this, fd, seq);
#endif
- { // acquire lock
- AutoMutex _l(mLock);
- ssize_t requestIndex = mRequests.indexOfKey(fd);
- if (requestIndex < 0) {
- return 0;
- }
+ const auto& request_it = mRequests.find(seq);
+ if (request_it == mRequests.end()) {
+ return 0;
+ }
+ const int fd = request_it->second.fd;
- // Check the sequence number if one was given.
- if (seq != -1 && mRequests.valueAt(requestIndex).seq != seq) {
+ // Always remove the FD from the request map even if an error occurs while
+ // updating the epoll set so that we avoid accidentally leaking callbacks.
+ mRequests.erase(request_it);
+ mSequenceNumberByFd.erase(fd);
+
+ int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
+ if (epollResult < 0) {
+ if (errno == EBADF || errno == ENOENT) {
+ // Tolerate EBADF or ENOENT because it means that the file descriptor was closed
+ // before its callback was unregistered. This error may occur naturally when a
+ // callback has the side-effect of closing the file descriptor before returning and
+ // unregistering itself.
+ //
+ // Unfortunately due to kernel limitations we need to rebuild the epoll
+ // set from scratch because it may contain an old file handle that we are
+ // now unable to remove since its file descriptor is no longer valid.
+ // No such problem would have occurred if we were using the poll system
+ // call instead, but that approach carries other disadvantages.
#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - sequence number mismatch, oldSeq=%d",
- this, mRequests.valueAt(requestIndex).seq);
+ ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
+ "being closed: %s",
+ this, strerror(errno));
#endif
- return 0;
+ scheduleEpollRebuildLocked();
+ } else {
+ // Some other error occurred. This is really weird because it means
+ // our list of callbacks got out of sync with the epoll set somehow.
+ // We defensively rebuild the epoll set to avoid getting spurious
+ // notifications with nowhere to go.
+ ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
+ scheduleEpollRebuildLocked();
+ return -1;
}
-
- // Always remove the FD from the request map even if an error occurs while
- // updating the epoll set so that we avoid accidentally leaking callbacks.
- mRequests.removeItemsAt(requestIndex);
-
- int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
- if (epollResult < 0) {
- if (seq != -1 && (errno == EBADF || errno == ENOENT)) {
- // Tolerate EBADF or ENOENT when the sequence number is known because it
- // means that the file descriptor was closed before its callback was
- // unregistered. This error may occur naturally when a callback has the
- // side-effect of closing the file descriptor before returning and
- // unregistering itself.
- //
- // Unfortunately due to kernel limitations we need to rebuild the epoll
- // set from scratch because it may contain an old file handle that we are
- // now unable to remove since its file descriptor is no longer valid.
- // No such problem would have occurred if we were using the poll system
- // call instead, but that approach carries others disadvantages.
-#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
- "being closed: %s", this, strerror(errno));
-#endif
- scheduleEpollRebuildLocked();
- } else {
- // Some other error occurred. This is really weird because it means
- // our list of callbacks got out of sync with the epoll set somehow.
- // We defensively rebuild the epoll set to avoid getting spurious
- // notifications with nowhere to go.
- ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
- scheduleEpollRebuildLocked();
- return -1;
- }
- }
- } // release lock
+ }
return 1;
}
@@ -656,14 +655,11 @@
return mPolling;
}
-void Looper::Request::initEventItem(struct epoll_event* eventItem) const {
- int epollEvents = 0;
+uint32_t Looper::Request::getEpollEvents() const {
+ uint32_t epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
-
- memset(eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem->events = epollEvents;
- eventItem->data.fd = fd;
+ return epollEvents;
}
MessageHandler::~MessageHandler() { }
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 34f424b..c859f9c 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -8,6 +8,9 @@
#include <utils/Looper.h>
#include <utils/StopWatch.h>
#include <utils/Timers.h>
+#include <thread>
+#include <unordered_map>
+#include <utility>
#include "Looper_test_pipe.h"
#include <utils/threads.h>
@@ -710,4 +713,123 @@
<< "no more messages to handle";
}
+class LooperEventCallback : public LooperCallback {
+ public:
+ using Callback = std::function<int(int fd, int events)>;
+ explicit LooperEventCallback(Callback callback) : mCallback(std::move(callback)) {}
+ int handleEvent(int fd, int events, void* /*data*/) override { return mCallback(fd, events); }
+
+ private:
+ Callback mCallback;
+};
+
+// A utility class that allows for pipes to be added and removed from the looper, and polls the
+// looper from a different thread.
+class ThreadedLooperUtil {
+ public:
+ explicit ThreadedLooperUtil(const sp<Looper>& looper) : mLooper(looper), mRunning(true) {
+ mThread = std::thread([this]() {
+ while (mRunning) {
+ static constexpr std::chrono::milliseconds POLL_TIMEOUT(500);
+ mLooper->pollOnce(POLL_TIMEOUT.count());
+ }
+ });
+ }
+
+ ~ThreadedLooperUtil() {
+ mRunning = false;
+ mThread.join();
+ }
+
+ // Create a new pipe, and return the write end of the pipe and the id used to track the pipe.
+ // The read end of the pipe is added to the looper.
+ std::pair<int /*id*/, base::unique_fd> createPipe() {
+ int pipeFd[2];
+ if (pipe(pipeFd)) {
+ ADD_FAILURE() << "pipe() failed.";
+ return {};
+ }
+ const int readFd = pipeFd[0];
+ const int writeFd = pipeFd[1];
+
+ int id;
+ { // acquire lock
+ std::scoped_lock l(mLock);
+
+ id = mNextId++;
+ mFds.emplace(id, readFd);
+
+ auto removeCallback = [this, id, readFd](int fd, int events) {
+ EXPECT_EQ(readFd, fd) << "Received callback for incorrect fd.";
+ if ((events & Looper::EVENT_HANGUP) == 0) {
+ return 1; // Not a hangup, keep the callback.
+ }
+ removePipe(id);
+ return 0; // Remove the callback.
+ };
+
+ mLooper->addFd(readFd, 0, Looper::EVENT_INPUT,
+ new LooperEventCallback(std::move(removeCallback)), nullptr);
+ } // release lock
+
+ return {id, base::unique_fd(writeFd)};
+ }
+
+ // Remove the pipe with the given id.
+ void removePipe(int id) {
+ std::scoped_lock l(mLock);
+ if (mFds.find(id) == mFds.end()) {
+ return;
+ }
+ mLooper->removeFd(mFds[id].get());
+ mFds.erase(id);
+ }
+
+ // Check if the pipe with the given id exists and has not been removed.
+ bool hasPipe(int id) {
+ std::scoped_lock l(mLock);
+ return mFds.find(id) != mFds.end();
+ }
+
+ private:
+ sp<Looper> mLooper;
+ std::atomic<bool> mRunning;
+ std::thread mThread;
+
+ std::mutex mLock;
+ std::unordered_map<int, base::unique_fd> mFds GUARDED_BY(mLock);
+ int mNextId GUARDED_BY(mLock) = 0;
+};
+
+TEST_F(LooperTest, MultiThreaded_NoUnexpectedFdRemoval) {
+ ThreadedLooperUtil util(mLooper);
+
+ // Iterate repeatedly to try to recreate a flaky instance.
+ for (int i = 0; i < 1000; i++) {
+ auto [firstPipeId, firstPipeFd] = util.createPipe();
+ const int firstFdNumber = firstPipeFd.get();
+
+ // Close the first pipe's fd, causing a fd hangup.
+ firstPipeFd.reset();
+
+ // Request to remove the pipe from this test thread. This causes a race for pipe removal
+ // between the hangup in the looper's thread and this remove request from the test thread.
+ util.removePipe(firstPipeId);
+
+ // Create the second pipe. Since the fds for the first pipe are closed, this pipe should
+ // have the same fd numbers as the first pipe because the lowest unused fd number is used.
+ const auto [secondPipeId, fd] = util.createPipe();
+ EXPECT_EQ(firstFdNumber, fd.get())
+ << "The first and second fds must match for the purposes of this test.";
+
+ // Wait for unexpected hangup to occur.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+ ASSERT_TRUE(util.hasPipe(secondPipeId)) << "The second pipe was removed unexpectedly.";
+
+ util.removePipe(secondPipeId);
+ }
+ SUCCEED() << "No unexpectedly removed fds.";
+}
+
} // namespace android
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index 466fbb7..b387d68 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -17,15 +17,16 @@
#ifndef UTILS_LOOPER_H
#define UTILS_LOOPER_H
-#include <utils/threads.h>
#include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
#include <utils/Timers.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
#include <sys/epoll.h>
#include <android-base/unique_fd.h>
+#include <unordered_map>
#include <utility>
namespace android {
@@ -421,18 +422,20 @@
static sp<Looper> getForThread();
private:
- struct Request {
- int fd;
- int ident;
- int events;
- int seq;
- sp<LooperCallback> callback;
- void* data;
+ using SequenceNumber = uint64_t;
- void initEventItem(struct epoll_event* eventItem) const;
- };
+ struct Request {
+ int fd;
+ int ident;
+ int events;
+ sp<LooperCallback> callback;
+ void* data;
+
+ uint32_t getEpollEvents() const;
+ };
struct Response {
+ SequenceNumber seq;
int events;
Request request;
};
@@ -463,9 +466,14 @@
android::base::unique_fd mEpollFd; // guarded by mLock but only modified on the looper thread
bool mEpollRebuildRequired; // guarded by mLock
- // Locked list of file descriptor monitoring requests.
- KeyedVector<int, Request> mRequests; // guarded by mLock
- int mNextRequestSeq;
+ // Locked maps of fds and sequence numbers monitoring requests.
+ // Both maps must be kept in sync at all times.
+ std::unordered_map<SequenceNumber, Request> mRequests; // guarded by mLock
+ std::unordered_map<int /*fd*/, SequenceNumber> mSequenceNumberByFd; // guarded by mLock
+
+ // The sequence number to use for the next fd that is added to the looper.
+ // The sequence number 0 is reserved for the WakeEventFd.
+ SequenceNumber mNextRequestSeq; // guarded by mLock
// This state is only used privately by pollOnce and does not require a lock since
// it runs on a single thread.
@@ -474,9 +482,8 @@
nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
int pollInner(int timeoutMillis);
- int removeFd(int fd, int seq);
+ int removeSequenceNumberLocked(SequenceNumber seq); // requires mLock
void awoken();
- void pushResponse(int events, const Request& request);
void rebuildEpollLocked();
void scheduleEpollRebuildLocked();
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9371617..08ef6e3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -776,11 +776,13 @@
# Create directories to push tests to for each linker namespace.
# Create the subdirectories in case the first test is run as root
# so it doesn't end up owned by root.
- mkdir /data/local/tests 0700 shell shell
- mkdir /data/local/tests/product 0700 shell shell
- mkdir /data/local/tests/system 0700 shell shell
- mkdir /data/local/tests/unrestricted 0700 shell shell
- mkdir /data/local/tests/vendor 0700 shell shell
+ # Set directories to be executable by any process so that debuggerd,
+ # aka crash_dump, can read any executables/shared libraries.
+ mkdir /data/local/tests 0701 shell shell
+ mkdir /data/local/tests/product 0701 shell shell
+ mkdir /data/local/tests/system 0701 shell shell
+ mkdir /data/local/tests/unrestricted 0701 shell shell
+ mkdir /data/local/tests/vendor 0701 shell shell
# create dalvik-cache, so as to enforce our permissions
mkdir /data/dalvik-cache 0771 root root encryption=Require
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index b59fb67..a40105f 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -16,7 +16,10 @@
#include <errno.h>
#include <fcntl.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
#include <scsi/sg.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -104,22 +107,18 @@
static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock";
-#ifdef RPMB_DEBUG
-
-static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
+static void print_buf(FILE* handle, const char* prefix, const uint8_t* buf, size_t size) {
size_t i;
- printf("%s @%p [%zu]", prefix, buf, size);
+ fprintf(handle, "%s @%p [%zu]", prefix, buf, size);
for (i = 0; i < size; i++) {
- if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
- printf(" %02x", buf[i]);
+ if (i && i % 32 == 0) fprintf(handle, "\n%*s", (int)strlen(prefix), "");
+ fprintf(handle, " %02x", buf[i]);
}
- printf("\n");
- fflush(stdout);
+ fprintf(handle, "\n");
+ fflush(handle);
}
-#endif
-
static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned char cmd_len,
unsigned char mx_sb_len, unsigned int dxfer_len, void* dxferp,
unsigned char* cmdp, void* sbp) {
@@ -135,6 +134,111 @@
io_hdrp->timeout = TIMEOUT;
}
+/* Returns false if the sense data was valid and no errors were present */
+static bool check_scsi_sense(const uint8_t* sense_buf, size_t len) {
+ uint8_t response_code = 0;
+ uint8_t sense_key = 0;
+ uint8_t additional_sense_code = 0;
+ uint8_t additional_sense_code_qualifier = 0;
+ uint8_t additional_length = 0;
+
+ if (!sense_buf || len == 0) {
+ ALOGE("Invalid SCSI sense buffer, length: %zu\n", len);
+ return false;
+ }
+
+ response_code = 0x7f & sense_buf[0];
+
+ if (response_code < 0x70 || response_code > 0x73) {
+ ALOGE("Invalid SCSI sense response code: %hhu\n", response_code);
+ return false;
+ }
+
+ if (response_code >= 0x72) {
+ /* descriptor format, SPC-6 4.4.2 */
+ if (len > 1) {
+ sense_key = 0xf & sense_buf[1];
+ }
+ if (len > 2) {
+ additional_sense_code = sense_buf[2];
+ }
+ if (len > 3) {
+ additional_sense_code_qualifier = sense_buf[3];
+ }
+ if (len > 7) {
+ additional_length = sense_buf[7];
+ }
+ } else {
+ /* fixed format, SPC-6 4.4.3 */
+ if (len > 2) {
+ sense_key = 0xf & sense_buf[2];
+ }
+ if (len > 7) {
+ additional_length = sense_buf[7];
+ }
+ if (len > 12) {
+ additional_sense_code = sense_buf[12];
+ }
+ if (len > 13) {
+ additional_sense_code_qualifier = sense_buf[13];
+ }
+ }
+
+ switch (sense_key) {
+ case NO_SENSE:
+ case 0x0f: /* COMPLETED, not present in kernel headers */
+ ALOGD("SCSI success with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+ additional_sense_code, additional_sense_code_qualifier);
+ return true;
+ }
+
+ ALOGE("Unexpected SCSI sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+ additional_sense_code, additional_sense_code_qualifier);
+ print_buf(stderr, "sense buffer: ", sense_buf, len);
+ return false;
+}
+
+static void check_sg_io_hdr(const sg_io_hdr_t* io_hdrp) {
+ if (io_hdrp->status == 0 && io_hdrp->host_status == 0 && io_hdrp->driver_status == 0) {
+ return;
+ }
+
+ if (io_hdrp->status & 0x01) {
+ ALOGE("SG_IO received unknown status, LSB is set: %hhu", io_hdrp->status);
+ }
+
+ if (io_hdrp->masked_status != GOOD && io_hdrp->sb_len_wr > 0) {
+ bool sense_error = check_scsi_sense(io_hdrp->sbp, io_hdrp->sb_len_wr);
+ if (sense_error) {
+ ALOGE("Unexpected SCSI sense. masked_status: %hhu, host_status: %hu, driver_status: "
+ "%hu\n",
+ io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
+ return;
+ }
+ }
+
+ switch (io_hdrp->masked_status) {
+ case GOOD:
+ break;
+ case CHECK_CONDITION:
+ /* handled by check_sg_sense above */
+ break;
+ default:
+ ALOGE("SG_IO failed with masked_status: %hhu, host_status: %hu, driver_status: %hu\n",
+ io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
+ return;
+ }
+
+ if (io_hdrp->host_status != 0) {
+ ALOGE("SG_IO failed with host_status: %hu, driver_status: %hu\n", io_hdrp->host_status,
+ io_hdrp->driver_status);
+ }
+
+ if (io_hdrp->resid != 0) {
+ ALOGE("SG_IO resid was non-zero: %d\n", io_hdrp->resid);
+ }
+}
+
static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
struct {
struct mmc_ioc_multi_cmd multi;
@@ -153,7 +257,7 @@
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
- print_buf("request: ", write_buf, req->reliable_write_size);
+ print_buf(stdout, "request: ", write_buf, req->reliable_write_size);
#endif
write_buf += req->reliable_write_size;
mmc.multi.num_of_cmds++;
@@ -169,7 +273,7 @@
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
- print_buf("request: ", write_buf, req->write_size);
+ print_buf(stdout, "request: ", write_buf, req->write_size);
#endif
write_buf += req->write_size;
mmc.multi.num_of_cmds++;
@@ -225,6 +329,7 @@
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
goto err_op;
}
+ check_sg_io_hdr(&io_hdr);
write_buf += req->reliable_write_size;
}
@@ -239,6 +344,7 @@
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
goto err_op;
}
+ check_sg_io_hdr(&io_hdr);
write_buf += req->write_size;
}
@@ -252,6 +358,7 @@
if (rc < 0) {
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
}
+ check_sg_io_hdr(&io_hdr);
}
err_op:
@@ -353,7 +460,7 @@
goto err_response;
}
#ifdef RPMB_DEBUG
- if (req->read_size) print_buf("response: ", read_buf, req->read_size);
+ if (req->read_size) print_buf(stdout, "response: ", read_buf, req->read_size);
#endif
if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 6cd381f..21ea7ae 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -23,7 +23,7 @@
# HAL loading of gatekeeper.trusty.
PRODUCT_PACKAGES += \
- android.hardware.keymaster@4.0-service.trusty \
+ android.hardware.security.keymint-service.trusty \
android.hardware.gatekeeper@1.0-service.trusty \
trusty_apploader