Detect volume down key combo for safe mode

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.
This commit is contained in:
topjohnwu 2020-05-19 04:53:16 -07:00
parent 3c04dab472
commit e02e46d0fc
4 changed files with 96 additions and 48 deletions

View File

@ -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>
@ -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 *
****************/
@ -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");

View File

@ -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)
@ -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) {

View File

@ -91,7 +91,7 @@ ssize_t xxread(int fd, void *buf, size_t count) {
int xpipe2(int pipefd[2], int flags) {
int ret = pipe2(pipefd, flags);
if (ret == -1) {
if (ret < 0) {
PLOGE("pipe2");
}
return ret;
@ -99,7 +99,7 @@ int xpipe2(int pipefd[2], int flags) {
int xsetns(int fd, int nstype) {
int ret = setns(fd, nstype);
if (ret == -1) {
if (ret < 0) {
PLOGE("setns");
}
return ret;
@ -107,7 +107,7 @@ int xsetns(int fd, int nstype) {
int xunshare(int flags) {
int ret = unshare(flags);
if (ret == -1) {
if (ret < 0) {
PLOGE("unshare");
}
return ret;
@ -147,7 +147,7 @@ struct dirent *xreaddir(DIR *dirp) {
pid_t xsetsid() {
pid_t pid = setsid();
if (pid == -1) {
if (pid < 0) {
PLOGE("setsid");
}
return pid;
@ -155,7 +155,7 @@ pid_t xsetsid() {
int xsocket(int domain, int type, int protocol) {
int fd = socket(domain, type, protocol);
if (fd == -1) {
if (fd < 0) {
PLOGE("socket");
}
return fd;
@ -163,7 +163,7 @@ int xsocket(int domain, int type, int protocol) {
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
int ret = bind(sockfd, addr, addrlen);
if (ret == -1) {
if (ret < 0) {
PLOGE("bind");
}
return ret;
@ -171,7 +171,7 @@ int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
int xlisten(int sockfd, int backlog) {
int ret = listen(sockfd, backlog);
if (ret == -1) {
if (ret < 0) {
PLOGE("listen");
}
return ret;
@ -179,7 +179,7 @@ int xlisten(int sockfd, int backlog) {
static int accept4_compat(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
int fd = accept(sockfd, addr, addrlen);
if (fd == -1) {
if (fd < 0) {
PLOGE("accept");
} else {
if (flags & SOCK_CLOEXEC)
@ -194,7 +194,7 @@ static int accept4_compat(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
int fd = accept4(sockfd, addr, addrlen, flags);
if (fd == -1) {
if (fd < 0) {
if (errno == ENOSYS)
return accept4_compat(sockfd, addr, addrlen, flags);
PLOGE("accept4");
@ -228,7 +228,7 @@ void *xrealloc(void *ptr, size_t size) {
ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags) {
int sent = sendmsg(sockfd, msg, flags);
if (sent == -1) {
if (sent < 0) {
PLOGE("sendmsg");
}
return sent;
@ -236,7 +236,7 @@ ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags) {
ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {
int rec = recvmsg(sockfd, msg, flags);
if (rec == -1) {
if (rec < 0) {
PLOGE("recvmsg");
}
return rec;
@ -253,7 +253,7 @@ int xpthread_create(pthread_t *thread, const pthread_attr_t *attr,
int xstat(const char *pathname, struct stat *buf) {
int ret = stat(pathname, buf);
if (ret == -1) {
if (ret < 0) {
PLOGE("stat %s", pathname);
}
return ret;
@ -261,7 +261,7 @@ int xstat(const char *pathname, struct stat *buf) {
int xlstat(const char *pathname, struct stat *buf) {
int ret = lstat(pathname, buf);
if (ret == -1) {
if (ret < 0) {
PLOGE("lstat %s", pathname);
}
return ret;
@ -269,7 +269,7 @@ int xlstat(const char *pathname, struct stat *buf) {
int xfstat(int fd, struct stat *buf) {
int ret = fstat(fd, buf);
if (ret == -1) {
if (ret < 0) {
PLOGE("fstat %d", fd);
}
return ret;
@ -277,7 +277,7 @@ int xfstat(int fd, struct stat *buf) {
int xdup(int fd) {
int ret = dup(fd);
if (ret == -1) {
if (ret < 0) {
PLOGE("dup");
}
return ret;
@ -285,7 +285,7 @@ int xdup(int fd) {
int xdup2(int oldfd, int newfd) {
int ret = dup2(oldfd, newfd);
if (ret == -1) {
if (ret < 0) {
PLOGE("dup2");
}
return ret;
@ -293,7 +293,7 @@ int xdup2(int oldfd, int newfd) {
int xdup3(int oldfd, int newfd, int flags) {
int ret = dup3(oldfd, newfd, flags);
if (ret == -1) {
if (ret < 0) {
PLOGE("dup3");
}
return ret;
@ -301,7 +301,7 @@ int xdup3(int oldfd, int newfd, int flags) {
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) {
ssize_t ret = readlink(pathname, buf, bufsiz);
if (ret == -1) {
if (ret < 0) {
PLOGE("readlink %s", pathname);
} else {
buf[ret] = '\0';
@ -315,7 +315,7 @@ ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) {
#if defined(__i386__) || defined(__x86_64__)
memset(buf, 0, bufsiz);
ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz);
if (ret == -1) {
if (ret < 0) {
PLOGE("readlinkat %s", pathname);
}
return ret;
@ -332,7 +332,7 @@ ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) {
int xsymlink(const char *target, const char *linkpath) {
int ret = symlink(target, linkpath);
if (ret == -1) {
if (ret < 0) {
PLOGE("symlink %s->%s", target, linkpath);
}
return ret;
@ -340,7 +340,7 @@ int xsymlink(const char *target, const char *linkpath) {
int xsymlinkat(const char *target, int newdirfd, const char *linkpath) {
int ret = symlinkat(target, newdirfd, linkpath);
if (ret == -1) {
if (ret < 0) {
PLOGE("symlinkat %s->%s", target, linkpath);
}
return ret;
@ -348,7 +348,7 @@ int xsymlinkat(const char *target, int newdirfd, const char *linkpath) {
int xlinkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) {
int ret = linkat(olddirfd, oldpath, newdirfd, newpath, flags);
if (ret == -1) {
if (ret < 0) {
PLOGE("linkat %s->%s", oldpath, newpath);
}
return ret;
@ -358,7 +358,7 @@ int xmount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data) {
int ret = mount(source, target, filesystemtype, mountflags, data);
if (ret == -1) {
if (ret < 0) {
PLOGE("mount %s->%s", source, target);
}
return ret;
@ -366,7 +366,7 @@ int xmount(const char *source, const char *target,
int xumount(const char *target) {
int ret = umount(target);
if (ret == -1) {
if (ret < 0) {
PLOGE("umount %s", target);
}
return ret;
@ -374,7 +374,7 @@ int xumount(const char *target) {
int xumount2(const char *target, int flags) {
int ret = umount2(target, flags);
if (ret == -1) {
if (ret < 0) {
PLOGE("umount2 %s", target);
}
return ret;
@ -382,7 +382,7 @@ int xumount2(const char *target, int flags) {
int xrename(const char *oldpath, const char *newpath) {
int ret = rename(oldpath, newpath);
if (ret == -1) {
if (ret < 0) {
PLOGE("rename %s->%s", oldpath, newpath);
}
return ret;
@ -390,7 +390,7 @@ int xrename(const char *oldpath, const char *newpath) {
int xmkdir(const char *pathname, mode_t mode) {
int ret = mkdir(pathname, mode);
if (ret == -1 && errno != EEXIST) {
if (ret < 0 && errno != EEXIST) {
PLOGE("mkdir %s %u", pathname, mode);
}
return ret;
@ -398,7 +398,7 @@ int xmkdir(const char *pathname, mode_t mode) {
int xmkdirs(const char *pathname, mode_t mode) {
int ret = mkdirs(pathname, mode);
if (ret == -1) {
if (ret < 0) {
PLOGE("mkdirs %s", pathname);
}
return ret;
@ -406,7 +406,7 @@ int xmkdirs(const char *pathname, mode_t mode) {
int xmkdirat(int dirfd, const char *pathname, mode_t mode) {
int ret = mkdirat(dirfd, pathname, mode);
if (ret == -1 && errno != EEXIST) {
if (ret < 0 && errno != EEXIST) {
PLOGE("mkdirat %s %u", pathname, mode);
}
return ret;
@ -431,7 +431,7 @@ ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count) {
pid_t xfork() {
int ret = fork();
if (ret == -1) {
if (ret < 0) {
PLOGE("fork");
}
return ret;
@ -439,7 +439,7 @@ pid_t xfork() {
int xpoll(struct pollfd *fds, nfds_t nfds, int timeout) {
int ret = poll(fds, nfds, timeout);
if (ret == -1) {
if (ret < 0) {
PLOGE("poll");
}
return ret;
@ -447,7 +447,7 @@ int xpoll(struct pollfd *fds, nfds_t nfds, int timeout) {
int xinotify_init1(int flags) {
int ret = inotify_init1(flags);
if (ret == -1) {
if (ret < 0) {
PLOGE("inotify_init1");
}
return ret;
@ -463,3 +463,11 @@ char *xrealpath(const char *path, char *resolved_path) {
}
return ret;
}
int xmknod(const char *pathname, mode_t mode, dev_t dev) {
int ret = mknod(pathname, mode, dev);
if (ret < 0) {
PLOGE("mknod");
}
return ret;
}

View File

@ -59,4 +59,5 @@ pid_t xfork();
int xpoll(struct pollfd *fds, nfds_t nfds, int timeout);
int xinotify_init1(int flags);
char *xrealpath(const char *path, char *resolved_path);
int xmknod(const char *pathname, mode_t mode, dev_t dev);