Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2013 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 #include <jni.h>
     18 #include <linux/futex.h>
     19 #include <sys/types.h>
     20 #include <sys/syscall.h>
     21 #include <unistd.h>
     22 #include <sys/prctl.h>
     23 #include <sys/ptrace.h>
     24 #include <sys/wait.h>
     25 #include <signal.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 #include <sys/mman.h>
     29 #include <sys/stat.h>
     30 #include <sys/utsname.h>
     31 #include <fcntl.h>
     32 #include <cutils/log.h>
     33 #include <linux/perf_event.h>
     34 #include <errno.h>
     35 #include <inttypes.h>
     36 #include <linux/sysctl.h>
     37 #include <arpa/inet.h>
     38 
     39 /*
     40  * Returns true iff this device is vulnerable to CVE-2013-2094.
     41  * A patch for CVE-2013-2094 can be found at
     42  * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8176cced706b5e5d15887584150764894e94e02f
     43  */
     44 static jboolean android_security_cts_NativeCodeTest_doPerfEventTest(JNIEnv* env, jobject thiz)
     45 {
     46     uint64_t attr[10] = { 0x4800000001, (uint32_t) -1, 0, 0, 0, 0x300 };
     47 
     48     int fd = syscall(__NR_perf_event_open, attr, 0, -1, -1, 0);
     49     jboolean result = (fd != -1);
     50 
     51     if (fd != -1) {
     52         close(fd);
     53     }
     54 
     55     return result;
     56 }
     57 
     58 /*
     59  * Detects if the following patch is present.
     60  * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c95eb3184ea1a3a2551df57190c81da695e2144b
     61  *
     62  * Returns true if the patch is applied, or crashes the system otherwise.
     63  *
     64  * While you're at it, you want to apply the following patch too.
     65  * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b88a2595b6d8aedbd275c07dfa784657b4f757eb
     66  * This test doesn't cover the above patch. TODO write a new test.
     67  *
     68  * Credit: https://github.com/deater/perf_event_tests/blob/master/exploits/arm_perf_exploit.c
     69  */
     70 static jboolean android_security_cts_NativeCodeTest_doPerfEventTest2(JNIEnv* env, jobject thiz)
     71 {
     72     struct perf_event_attr pe[2];
     73     int fd[2];
     74     memset(pe, 0, sizeof(pe));
     75     pe[0].type = 2;
     76     pe[0].config = 72;
     77     pe[0].size = 80;
     78     pe[1].type = PERF_TYPE_RAW;
     79     pe[1].size = 80;
     80     fd[0]=syscall(__NR_perf_event_open, &pe[0], 0, 0, -1, 0);
     81     fd[1]=syscall(__NR_perf_event_open, &pe[1], 0, 0, fd[0], 0);
     82     close(fd[0]);
     83     close(fd[1]);
     84     return true;
     85 }
     86 
     87 /*
     88  * Prior to https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/arch/arm/include/asm/uaccess.h?id=8404663f81d212918ff85f493649a7991209fa04
     89  * there was a flaw in the kernel's handling of get_user and put_user
     90  * requests. Normally, get_user and put_user are supposed to guarantee
     91  * that reads/writes outside the process's address space are not
     92  * allowed.
     93  *
     94  * In this test, we use sysctl to force a read from an address outside
     95  * of our address space (but in the kernel's address space). Without the
     96  * patch applied, this read succeeds, because sysctl uses the
     97  * vulnerable get_user call.
     98  *
     99  * This function returns true if the patch above is applied, or false
    100  * otherwise.
    101  *
    102  * Credit: https://twitter.com/grsecurity/status/401443359912239105
    103  */
    104 static jboolean android_security_cts_NativeCodeTest_doVrootTest(JNIEnv*, jobject)
    105 {
    106 #ifdef __arm__
    107     ALOGE("Starting doVrootTest");
    108 
    109     struct __sysctl_args args;
    110     char osname[100];
    111     int name[] = { CTL_KERN, KERN_OSTYPE };
    112 
    113     memset(&args, 0, sizeof(struct __sysctl_args));
    114     args.name = name;
    115     args.nlen = sizeof(name)/sizeof(name[0]);
    116     args.oldval = osname;
    117     args.oldlenp = (size_t *) 0xc0000000; // PAGE_OFFSET
    118 
    119     int result = syscall(__NR__sysctl, &args);
    120     return ((result == -1) && (errno == EFAULT));
    121 #else
    122     return true;
    123 #endif
    124 }
    125 
    126 static void* mmap_syscall(void* addr, size_t len, int prot, int flags, int fd, off_t offset)
    127 {
    128 #ifdef __LP64__
    129     return mmap(addr, len, prot, flags, fd, offset);
    130 #else
    131     return (void*) syscall(__NR_mmap2, addr, len, prot, flags, fd, offset);
    132 #endif
    133 }
    134 
    135 #define KBASE_REG_COOKIE_TB         2
    136 #define KBASE_REG_COOKIE_MTP        3
    137 
    138 /*
    139  * Returns true if the device is immune to CVE-2014-1710,
    140  * false if the device is vulnerable.
    141  */
    142 static jboolean android_security_cts_NativeCodeTest_doCVE20141710Test(JNIEnv*, jobject)
    143 {
    144     jboolean result = false;
    145     int fd = open("/dev/mali0", O_RDWR);
    146     if (fd < 0) {
    147         return true; /* not vulnerable */
    148     }
    149 
    150     void* a = mmap_syscall(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, KBASE_REG_COOKIE_MTP);
    151     void* b = mmap_syscall(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, KBASE_REG_COOKIE_TB);
    152 
    153     if (a == MAP_FAILED) {
    154         result = true; /* assume not vulnerable */
    155         goto done;
    156     }
    157 
    158     if (b == MAP_FAILED) {
    159         result = true; /* assume not vulnerable */
    160         goto done;
    161     }
    162 
    163     /* mprotect should return an error if not vulnerable */
    164     result = (mprotect(b, 0x1000, PROT_READ | PROT_WRITE) == -1);
    165 
    166  done:
    167     if (a != MAP_FAILED) {
    168         munmap(a, 0x1000);
    169     }
    170     if (b != MAP_FAILED) {
    171         munmap(b, 0x1000);
    172     }
    173     close(fd);
    174     return result;
    175 }
    176 
    177 static inline int futex_syscall(volatile int* uaddr, int op, int val, const struct timespec* ts,
    178                                 volatile int* uaddr2, int val3) {
    179     return syscall(__NR_futex, uaddr, op, val, ts, uaddr2, val3);
    180 }
    181 
    182 /*
    183  * Test for vulnerability to CVE-2014-3153, a bug in the futex() syscall that can
    184  * lead to privilege escalation and was used by the towelroot exploit. Returns true
    185  * if device is patched, false if still vulnerable.
    186  */
    187 static jboolean android_security_cts_NativeCodeTest_doFutexTest(JNIEnv*, jobject)
    188 {
    189     jboolean result = false;
    190 
    191     int futex = 1;
    192     int ret;
    193 
    194     /* The patch will reject FUTEX_CMP_REQUEUE_PI calls where addr == addr2, so
    195      * that's what we're checking for - they're both &futex. Patched systems will
    196      * return -1 and set errno to 22 (EINVAL), vulnerable systems will return 0.
    197      */
    198     ret = futex_syscall(&futex, FUTEX_CMP_REQUEUE_PI, 1, NULL, &futex, 0);
    199     return (ret == -1 && errno == EINVAL);
    200 }
    201 
    202 static jboolean android_security_cts_NativeCodeTest_doNvmapIocFromIdTest(JNIEnv*, jobject)
    203 {
    204     /*
    205      * IOCTL code specified from the original notification.
    206      * Also available in:
    207      *     .../kernel/tegra/drivers/video/tegra/nvmap/nvmap_ioctl.h
    208      * #define NVMAP_IOC_MAGIC 'N'
    209      * #define NVMAP_IOC_FROM_ID _IOWR(NVMAP_IOC_MAGIC, 2, struct nvmap_create_handle)
    210      */
    211     const int NVMAP_IOC_FROM_ID = 0xc0084e02;
    212     int       nvmap = open("/dev/nvmap", O_RDWR | O_CLOEXEC, 0);
    213     bool      vulnerable = false;
    214 
    215     if (nvmap >= 0) {
    216         if (0 == ioctl(nvmap, NVMAP_IOC_FROM_ID)) {
    217             /* IOCTL succeeded */
    218             vulnerable = true;
    219         }
    220         else if (errno != ENOTTY) {
    221             /* IOCTL failed, but provided the wrong error number */
    222             vulnerable = true;
    223         }
    224 
    225         close(nvmap);
    226     }
    227 
    228     return !vulnerable;
    229 }
    230 
    231 static jboolean android_security_cts_NativeCodeTest_doPingPongRootTest(JNIEnv*, jobject)
    232 {
    233     int icmp_sock;
    234     struct sockaddr sock_addr;
    235 
    236     memset(&sock_addr, 0, sizeof(sock_addr));
    237     icmp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
    238     sock_addr.sa_family = AF_INET;
    239 
    240     /* first connect */
    241     connect(icmp_sock, &sock_addr, sizeof(sock_addr));
    242 
    243     /* disconnect */
    244     sock_addr.sa_family = AF_UNSPEC;
    245     connect(icmp_sock, &sock_addr, sizeof(sock_addr));
    246 
    247     /* second disconnect -> crash */
    248     sock_addr.sa_family = AF_UNSPEC;
    249     connect(icmp_sock, &sock_addr, sizeof(sock_addr));
    250 
    251     return true;
    252 }
    253 
    254 static JNINativeMethod gMethods[] = {
    255     {  "doPerfEventTest", "()Z",
    256             (void *) android_security_cts_NativeCodeTest_doPerfEventTest },
    257     {  "doPerfEventTest2", "()Z",
    258             (void *) android_security_cts_NativeCodeTest_doPerfEventTest2 },
    259     {  "doVrootTest", "()Z",
    260             (void *) android_security_cts_NativeCodeTest_doVrootTest },
    261     {  "doCVE20141710Test", "()Z",
    262             (void *) android_security_cts_NativeCodeTest_doCVE20141710Test },
    263     {  "doFutexTest", "()Z",
    264             (void *) android_security_cts_NativeCodeTest_doFutexTest },
    265     {  "doNvmapIocFromIdTest", "()Z",
    266             (void *) android_security_cts_NativeCodeTest_doNvmapIocFromIdTest },
    267     {  "doPingPongRootTest", "()Z",
    268             (void *) android_security_cts_NativeCodeTest_doPingPongRootTest },
    269 };
    270 
    271 int register_android_security_cts_NativeCodeTest(JNIEnv* env)
    272 {
    273     jclass clazz = env->FindClass("android/security/cts/NativeCodeTest");
    274     return env->RegisterNatives(clazz, gMethods,
    275             sizeof(gMethods) / sizeof(JNINativeMethod));
    276 }
    277 
    278