From e02e46d0fc147a6a3686640351dd514a9a63800f Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 19 May 2020 04:53:16 -0700 Subject: [PATCH] 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. --- native/jni/core/bootstages.cpp | 57 +++++++++++++++++++++++---- native/jni/init/getinfo.cpp | 14 +++---- native/jni/utils/xwrap.cpp | 72 +++++++++++++++++++--------------- native/jni/utils/xwrap.hpp | 1 + 4 files changed, 96 insertions(+), 48 deletions(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 3c44dea0a..e2f8d52f6 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -1,11 +1,7 @@ #include #include -#include -#include -#include -#include -#include -#include +#include +#include #include #include #include @@ -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 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"); diff --git a/native/jni/init/getinfo.cpp b/native/jni/init/getinfo.cpp index 5d707074a..b4c31c27a 100644 --- a/native/jni/init/getinfo.cpp +++ b/native/jni/init/getinfo.cpp @@ -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) { diff --git a/native/jni/utils/xwrap.cpp b/native/jni/utils/xwrap.cpp index 3a5d67b2b..70d1c0a78 100644 --- a/native/jni/utils/xwrap.cpp +++ b/native/jni/utils/xwrap.cpp @@ -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; +} diff --git a/native/jni/utils/xwrap.hpp b/native/jni/utils/xwrap.hpp index 266f4ad4f..d982f0985 100644 --- a/native/jni/utils/xwrap.hpp +++ b/native/jni/utils/xwrap.hpp @@ -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);