1 /* 2 * Copyright (C) 2014 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 package android.os.cts; 18 19 import android.app.Service; 20 import android.content.Context; 21 import android.content.ComponentName; 22 import android.content.Intent; 23 import android.content.ServiceConnection; 24 import android.os.Environment; 25 import android.os.IBinder; 26 import android.os.ParcelFileDescriptor; 27 import android.os.RemoteException; 28 import android.os.MemoryFile; 29 import android.os.SystemClock; 30 import android.os.Build; 31 import android.util.Log; 32 import android.test.AndroidTestCase; 33 34 import com.google.common.util.concurrent.AbstractFuture; 35 36 import java.io.File; 37 import java.io.FileInputStream; 38 import java.io.FileOutputStream; 39 import java.io.FileNotFoundException; 40 import java.io.IOException; 41 import java.util.concurrent.ExecutionException; 42 import java.util.concurrent.TimeUnit; 43 import java.util.concurrent.TimeoutException; 44 import java.util.Date; 45 46 public class SeccompTest extends AndroidTestCase { 47 final static String TAG = "SeccompTest"; 48 49 static { 50 System.loadLibrary("ctsos_jni"); 51 } 52 53 // As this test validates a kernel system call interface, if the CTS tests 54 // were built for ARM but are running on an x86 CPU, the system call numbers 55 // will not be correct, so skip those tests. isRunningUnderEmulatedAbi()56 private boolean isRunningUnderEmulatedAbi() { 57 final String primaryAbi = Build.SUPPORTED_ABIS[0]; 58 return (CpuFeatures.isArmCpu() || CpuFeatures.isArm64Cpu()) && 59 !(primaryAbi.equals("armeabi-v7a") || primaryAbi.equals("arm64-v8a")); 60 } 61 testSeccomp()62 public void testSeccomp() { 63 if (OSFeatures.needsSeccompSupport()) { 64 assertTrue("Please enable seccomp support " 65 + "in your kernel (CONFIG_SECCOMP_FILTER=y)", 66 OSFeatures.hasSeccompSupport()); 67 } 68 } 69 testKernelBasicTests()70 public void testKernelBasicTests() { 71 if (!OSFeatures.needsSeccompSupport()) 72 return; 73 74 if (isRunningUnderEmulatedAbi()) { 75 Log.d(TAG, "Skipping test running under an emulated ABI"); 76 return; 77 } 78 79 final String[] tests = { 80 "global.mode_strict_support", 81 "global.mode_strict_cannot_call_prctl", 82 "global.no_new_privs_support", 83 "global.mode_filter_support", 84 /* "global.mode_filter_without_nnp", // all Android processes already have nnp */ 85 "global.filter_size_limits", 86 "global.filter_chain_limits", 87 "global.mode_filter_cannot_move_to_strict", 88 "global.mode_filter_get_seccomp", 89 "global.ALLOW_all", 90 "global.empty_prog", 91 "global.unknown_ret_is_kill_inside", 92 "global.unknown_ret_is_kill_above_allow", 93 "global.KILL_all", 94 "global.KILL_one", 95 "global.KILL_one_arg_one", 96 "global.KILL_one_arg_six", 97 "global.arg_out_of_range", 98 "global.ERRNO_one", 99 "global.ERRNO_one_ok", 100 }; 101 runKernelUnitTestSuite(tests); 102 } 103 testKernelTrapTests()104 public void testKernelTrapTests() { 105 if (!OSFeatures.needsSeccompSupport()) 106 return; 107 108 final String[] tests = { 109 "TRAP.dfl", 110 "TRAP.ign", 111 "TRAP.handler", 112 }; 113 runKernelUnitTestSuite(tests); 114 } 115 testKernelPrecedenceTests()116 public void testKernelPrecedenceTests() { 117 if (!OSFeatures.needsSeccompSupport()) 118 return; 119 120 final String[] tests = { 121 "precedence.allow_ok", 122 "precedence.kill_is_highest", 123 "precedence.kill_is_highest_in_any_order", 124 "precedence.trap_is_second", 125 "precedence.trap_is_second_in_any_order", 126 "precedence.errno_is_third", 127 "precedence.errno_is_third_in_any_order", 128 "precedence.trace_is_fourth", 129 "precedence.trace_is_fourth_in_any_order", 130 }; 131 runKernelUnitTestSuite(tests); 132 } 133 134 /* // The SECCOMP_RET_TRACE does not work under Android Arm32. 135 public void testKernelTraceTests() { 136 if (!OSFeatures.needsSeccompSupport()) 137 return; 138 139 final String[] tests = { 140 "TRACE_poke.read_has_side_effects", 141 "TRACE_poke.getpid_runs_normally", 142 "TRACE_syscall.syscall_allowed", 143 "TRACE_syscall.syscall_redirected", 144 "TRACE_syscall.syscall_dropped", 145 }; 146 runKernelUnitTestSuite(tests); 147 } 148 */ 149 testKernelTSYNCTests()150 public void testKernelTSYNCTests() { 151 if (!OSFeatures.needsSeccompSupport()) 152 return; 153 154 if (isRunningUnderEmulatedAbi()) { 155 Log.d(TAG, "Skipping test running under an emulated ABI"); 156 return; 157 } 158 159 final String[] tests = { 160 "global.seccomp_syscall", 161 "global.seccomp_syscall_mode_lock", 162 "global.TSYNC_first", 163 "TSYNC.siblings_fail_prctl", 164 "TSYNC.two_siblings_with_ancestor", 165 /* "TSYNC.two_sibling_want_nnp", // all Android processes already have nnp */ 166 "TSYNC.two_siblings_with_no_filter", 167 "TSYNC.two_siblings_with_one_divergence", 168 "TSYNC.two_siblings_not_under_filter", 169 /* "global.syscall_restart", // ptrace attach fails */ 170 }; 171 runKernelUnitTestSuite(tests); 172 } 173 174 /** 175 * Runs a kernel unit test suite (an array of kernel test names). 176 */ runKernelUnitTestSuite(final String[] tests)177 private void runKernelUnitTestSuite(final String[] tests) { 178 for (final String test : tests) { 179 // TODO: Replace the URL with the documentation when it's finished. 180 assertTrue(test + " failed. This test requires kernel functionality to pass. " 181 + "Please go to http://XXXXX for instructions on how to enable or " 182 + "backport the required functionality.", 183 runKernelUnitTest(test)); 184 } 185 } 186 187 /** 188 * Integration test for seccomp-bpf policy applied to an isolatedProcess=true 189 * service. This will perform various operations in an isolated process under a 190 * fairly restrictive seccomp policy. 191 */ testIsolatedServicePolicy()192 public void testIsolatedServicePolicy() throws InterruptedException, ExecutionException, 193 RemoteException { 194 if (!OSFeatures.needsSeccompSupport()) 195 return; 196 197 if (isRunningUnderEmulatedAbi()) { 198 Log.d(TAG, "Skipping test running under an emulated ABI"); 199 return; 200 } 201 202 final IsolatedServiceConnection peer = new IsolatedServiceConnection(); 203 final Intent intent = new Intent(getContext(), IsolatedService.class); 204 assertTrue(getContext().bindService(intent, peer, Context.BIND_AUTO_CREATE)); 205 206 final ISeccompIsolatedService service = peer.get(); 207 208 // installFilter() must be called first, to set the seccomp policy. 209 assertTrue(service.installFilter()); 210 assertTrue(service.createThread()); 211 assertTrue(service.getSystemInfo()); 212 doFileWriteTest(service); 213 assertTrue(service.openAshmem()); 214 assertTrue(service.openDevFile()); 215 216 getContext().unbindService(peer); 217 } 218 219 /** 220 * Integration test for seccomp-bpf policy with isolatedProcess, where the 221 * process then violates the policy and gets killed by the kernel. 222 */ testViolateIsolatedServicePolicy()223 public void testViolateIsolatedServicePolicy() throws InterruptedException, 224 ExecutionException, RemoteException { 225 if (!OSFeatures.needsSeccompSupport()) 226 return; 227 228 if (isRunningUnderEmulatedAbi()) { 229 Log.d(TAG, "Skipping test running under an emulated ABI"); 230 return; 231 } 232 233 final IsolatedServiceConnection peer = new IsolatedServiceConnection(); 234 final Intent intent = new Intent(getContext(), IsolatedService.class); 235 assertTrue(getContext().bindService(intent, peer, Context.BIND_AUTO_CREATE)); 236 237 final ISeccompIsolatedService service = peer.get(); 238 239 assertTrue(service.installFilter()); 240 boolean gotRemoteException = false; 241 try { 242 service.violatePolicy(); 243 } catch (RemoteException e) { 244 gotRemoteException = true; 245 } 246 assertTrue(gotRemoteException); 247 248 getContext().unbindService(peer); 249 } 250 doFileWriteTest(ISeccompIsolatedService service)251 private void doFileWriteTest(ISeccompIsolatedService service) throws RemoteException { 252 final String fileName = "seccomp_test"; 253 ParcelFileDescriptor fd = null; 254 try { 255 FileOutputStream fOut = getContext().openFileOutput(fileName, 0); 256 fd = ParcelFileDescriptor.dup(fOut.getFD()); 257 fOut.close(); 258 } catch (FileNotFoundException e) { 259 fail(e.getMessage()); 260 return; 261 } catch (IOException e) { 262 fail(e.getMessage()); 263 return; 264 } 265 266 assertTrue(service.writeToFile(fd)); 267 268 try { 269 FileInputStream fIn = getContext().openFileInput(fileName); 270 assertEquals('!', fIn.read()); 271 fIn.close(); 272 } catch (FileNotFoundException e) { 273 fail(e.getMessage()); 274 } catch (IOException e) { 275 fail(e.getMessage()); 276 } 277 } 278 279 class IsolatedServiceConnection extends AbstractFuture<ISeccompIsolatedService> 280 implements ServiceConnection { 281 @Override onServiceConnected(ComponentName name, IBinder service)282 public void onServiceConnected(ComponentName name, IBinder service) { 283 set(ISeccompIsolatedService.Stub.asInterface(service)); 284 } 285 286 @Override onServiceDisconnected(ComponentName name)287 public void onServiceDisconnected(ComponentName name) { 288 } 289 290 @Override get()291 public ISeccompIsolatedService get() throws InterruptedException, ExecutionException { 292 try { 293 return get(10, TimeUnit.SECONDS); 294 } catch (TimeoutException e) { 295 throw new RuntimeException(e); 296 } 297 } 298 } 299 300 public static class IsolatedService extends Service { 301 private final ISeccompIsolatedService.Stub mService = new ISeccompIsolatedService.Stub() { 302 public boolean installFilter() { 303 return installTestFilter(); 304 } 305 306 public boolean createThread() { 307 Thread thread = new Thread(new Runnable() { 308 @Override 309 public void run() { 310 try { 311 Thread.currentThread().setPriority(Thread.MIN_PRIORITY); 312 Thread.sleep(100); 313 } catch (InterruptedException e) { 314 } 315 } 316 }); 317 thread.run(); 318 try { 319 thread.join(); 320 } catch (InterruptedException e) { 321 return false; 322 } 323 return true; 324 } 325 326 public boolean getSystemInfo() { 327 long uptimeMillis = SystemClock.uptimeMillis(); 328 if (uptimeMillis < 1) { 329 Log.d(TAG, "SystemClock failed"); 330 return false; 331 } 332 333 String version = Build.VERSION.CODENAME; 334 if (version.length() == 0) { 335 Log.d(TAG, "Build.VERSION failed"); 336 return false; 337 } 338 339 long time = (new Date()).getTime(); 340 if (time < 100) { 341 Log.d(TAG, "getTime failed"); 342 return false; 343 } 344 345 return true; 346 } 347 348 public boolean writeToFile(ParcelFileDescriptor fd) { 349 FileOutputStream fOut = new FileOutputStream(fd.getFileDescriptor()); 350 try { 351 fOut.write('!'); 352 fOut.close(); 353 } catch (IOException e) { 354 return false; 355 } 356 return true; 357 } 358 359 public boolean openAshmem() { 360 byte[] buffer = {'h', 'e', 'l', 'l', 'o'}; 361 try { 362 MemoryFile file = new MemoryFile("seccomp_isolated_test", 32); 363 file.writeBytes(buffer, 0, 0, buffer.length); 364 file.close(); 365 return true; 366 } catch (IOException e) { 367 return false; 368 } 369 } 370 371 public boolean openDevFile() { 372 try { 373 FileInputStream fIn = new FileInputStream("/dev/zero"); 374 boolean succeed = fIn.read() == 0; 375 succeed &= fIn.read() == 0; 376 succeed &= fIn.read() == 0; 377 fIn.close(); 378 return succeed; 379 } catch (FileNotFoundException e) { 380 return false; 381 } catch (IOException e) { 382 return false; 383 } 384 } 385 386 public void violatePolicy() { 387 getClockBootTime(); 388 } 389 }; 390 391 @Override onBind(Intent intent)392 public IBinder onBind(Intent intent) { 393 return mService; 394 } 395 } 396 397 /** 398 * Runs the seccomp_bpf_unittest of the given name. 399 */ runKernelUnitTest(final String name)400 private native boolean runKernelUnitTest(final String name); 401 402 /** 403 * Installs a test seccomp-bpf filter program that. 404 */ installTestFilter()405 private native static boolean installTestFilter(); 406 407 /** 408 * Attempts to get the CLOCK_BOOTTIME, which is a violation of the 409 * policy specified by installTestFilter(). 410 */ getClockBootTime()411 private native static int getClockBootTime(); 412 } 413