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