Skip to content

Commit

Permalink
Detect volume down key combo for safe mode
Browse files Browse the repository at this point in the history
It is possible that a module is breaking the device so bad that zygote
cannot even be started. In this case, system_server cannot start and
detect the safe mode key combo, set the persist property, and reboot.

Also on old Android versions, the system directly goes to safe mode
after detecting a key combo without rebooting, defeating the purpose of
Magisk's safe mode protection if we only check for the persist property.

Directly adding key combo check natively in magiskd allows us to enter
Magisk safe mode before the system is even aware of it.
  • Loading branch information
topjohnwu committed May 19, 2020
1 parent 3c04dab commit e02e46d
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 48 deletions.
57 changes: 50 additions & 7 deletions native/jni/core/bootstages.cpp
@@ -1,11 +1,7 @@
#include <sys/mount.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <sys/sysmacros.h>
#include <linux/input.h>
#include <libgen.h>
#include <vector>
#include <string>
Expand Down Expand Up @@ -220,6 +216,53 @@ static void collect_logs(bool reset) {
});
}

#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8)))

static bool check_key_combo() {
uint8_t bitmask[(KEY_MAX + 1) / 8];
vector<int> events;
constexpr char name[] = "/dev/.ev";

// First collect candidate events that accepts volume down
for (int minor = 64; minor < 96; ++minor) {
if (xmknod(name, S_IFCHR | 0444, makedev(13, minor)))
continue;
int fd = open(name, O_RDONLY | O_CLOEXEC);
unlink(name);
if (fd < 0)
continue;
memset(bitmask, 0, sizeof(bitmask));
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
if (test_bit(KEY_VOLUMEDOWN, bitmask))
events.push_back(fd);
else
close(fd);
}
if (events.empty())
return false;

run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); });

// Check if volume down key is held continuously for more than 3 seconds
for (int i = 0; i < 300; ++i) {
bool pressed = false;
for (const int &fd : events) {
memset(bitmask, 0, sizeof(bitmask));
ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask);
if (test_bit(KEY_VOLUMEDOWN, bitmask)) {
pressed = true;
break;
}
}
if (!pressed)
return false;
// Check every 10ms
usleep(10000);
}
LOGD("KEY_VOLUMEDOWN detected: enter safe mode\n");
return true;
}

/****************
* Entry points *
****************/
Expand Down Expand Up @@ -260,7 +303,7 @@ void post_fs_data(int client) {
goto unblock_init;
}

if (getprop("persist.sys.safemode", true) == "1") {
if (getprop("persist.sys.safemode", true) == "1" || check_key_combo()) {
safe_mode = true;
// Disable all modules and magiskhide so next boot will be clean
foreach_modules("disable");
Expand Down
14 changes: 5 additions & 9 deletions native/jni/init/getinfo.cpp
Expand Up @@ -48,10 +48,8 @@ static bool check_key_combo() {
constexpr const char *name = "/event";

for (int minor = 64; minor < 96; ++minor) {
if (mknod(name, S_IFCHR | 0444, makedev(13, minor))) {
PLOGE("mknod");
if (xmknod(name, S_IFCHR | 0444, makedev(13, minor)))
continue;
}
int fd = open(name, O_RDONLY | O_CLOEXEC);
unlink(name);
if (fd < 0)
Expand All @@ -60,17 +58,15 @@ static bool check_key_combo() {
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
if (test_bit(KEY_VOLUMEUP, bitmask))
events.push_back(fd);
else
close(fd);
}

if (events.empty())
return false;

run_finally fin([&]() -> void {
for (const int &fd : events)
close(fd);
});
run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); });

// Return true if volume key up is hold for more than 3 seconds
// Return true if volume up key is held for more than 3 seconds
int count = 0;
for (int i = 0; i < 500; ++i) {
for (const int &fd : events) {
Expand Down

0 comments on commit e02e46d

Please sign in to comment.