1 /* 2 * Copyright (C) 2016 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 libcore.libcore.io; 18 19 import static android.system.OsConstants.AF_INET6; 20 import static android.system.OsConstants.AF_UNIX; 21 import static android.system.OsConstants.IPPROTO_TCP; 22 import static android.system.OsConstants.IPPROTO_UDP; 23 import static android.system.OsConstants.F_SETFL; 24 import static android.system.OsConstants.SOCK_DGRAM; 25 import static android.system.OsConstants.O_NONBLOCK; 26 import static android.system.OsConstants.SOCK_STREAM; 27 import static org.junit.Assert.assertFalse; 28 import static org.junit.Assert.assertNull; 29 import static org.junit.Assert.assertTrue; 30 31 import org.junit.After; 32 import org.junit.Before; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.junit.runners.JUnit4; 36 import org.mockito.Mock; 37 import org.mockito.MockitoAnnotations; 38 39 import android.system.ErrnoException; 40 import android.system.OsConstants; 41 import android.system.StructAddrinfo; 42 import android.system.UnixSocketAddress; 43 44 import java.io.FileDescriptor; 45 import java.io.IOException; 46 import java.lang.reflect.Method; 47 import java.lang.reflect.Modifier; 48 import java.net.InetAddress; 49 import java.net.SocketAddress; 50 import java.util.Arrays; 51 import java.util.HashSet; 52 import java.util.List; 53 import java.util.Set; 54 import java.util.regex.Matcher; 55 import java.util.regex.Pattern; 56 57 import libcore.io.BlockGuardOs; 58 import libcore.io.IoUtils; 59 import libcore.io.Libcore; 60 import libcore.io.Os; 61 import libcore.test.annotation.NonCts; 62 import libcore.test.annotation.NonMts; 63 import libcore.test.reasons.NonCtsReasons; 64 import libcore.test.reasons.NonMtsReasons; 65 66 import dalvik.system.BlockGuard; 67 68 import static org.junit.Assert.assertSame; 69 import static org.junit.Assert.fail; 70 import static org.mockito.ArgumentMatchers.any; 71 import static org.mockito.ArgumentMatchers.anyInt; 72 import static org.mockito.ArgumentMatchers.anyString; 73 import static org.mockito.Mockito.eq; 74 import static org.mockito.Mockito.never; 75 import static org.mockito.Mockito.times; 76 import static org.mockito.Mockito.verify; 77 import static org.mockito.Mockito.when; 78 79 @RunWith(JUnit4.class) 80 public class BlockGuardOsTest { 81 82 final static Pattern pattern = Pattern.compile("[\\w\\$]+\\([^)]*\\)"); 83 84 @Mock private Os mockOsDelegate; 85 @Mock private BlockGuard.Policy mockThreadPolicy; 86 @Mock private BlockGuard.VmPolicy mockVmPolicy; 87 88 private BlockGuard.Policy savedThreadPolicy; 89 private BlockGuard.VmPolicy savedVmPolicy; 90 91 @Before setUp()92 public void setUp() { 93 MockitoAnnotations.initMocks(this); 94 savedThreadPolicy = BlockGuard.getThreadPolicy(); 95 savedVmPolicy = BlockGuard.getVmPolicy(); 96 BlockGuard.setThreadPolicy(mockThreadPolicy); 97 BlockGuard.setVmPolicy(mockVmPolicy); 98 } 99 100 @After tearDown()101 public void tearDown() { 102 BlockGuard.setVmPolicy(savedVmPolicy); 103 BlockGuard.setThreadPolicy(savedThreadPolicy); 104 } 105 106 @Test test_blockguardOsIsNotifiedByDefault_rename()107 public void test_blockguardOsIsNotifiedByDefault_rename() { 108 String oldPath = "BlockGuardOsTest/missing/old/path"; 109 String newPath = "BlockGuardOsTest/missing/new/path"; 110 try { 111 // We try not to be prescriptive about the exact default Os implementation. 112 // Whatever default Os is installed, we do expect BlockGuard to be called. 113 Os.getDefault().rename(oldPath, newPath); 114 } catch (ErrnoException ignored) { 115 } 116 verify(mockThreadPolicy).onWriteToDisk(); 117 verify(mockVmPolicy).onPathAccess(oldPath); 118 verify(mockVmPolicy).onPathAccess(newPath); 119 } 120 121 @Test test_android_getaddrinfo_networkPolicy()122 public void test_android_getaddrinfo_networkPolicy() { 123 InetAddress[] addresses = new InetAddress[] { InetAddress.getLoopbackAddress() }; 124 when(mockOsDelegate.android_getaddrinfo(anyString(), any(), anyInt())) 125 .thenReturn(addresses); 126 127 BlockGuardOs blockGuardOs = new BlockGuardOs(mockOsDelegate); 128 129 // Test with a numeric address that will not trigger a network policy check. 130 { 131 final String node = "numeric"; 132 final int netId = 1234; 133 final StructAddrinfo numericAddrInfo = new StructAddrinfo(); 134 numericAddrInfo.ai_flags = OsConstants.AI_NUMERICHOST; 135 InetAddress[] actual = 136 blockGuardOs.android_getaddrinfo(node, numericAddrInfo, netId); 137 138 verify(mockThreadPolicy, times(0)).onNetwork(); 139 verify(mockOsDelegate, times(1)).android_getaddrinfo(node, numericAddrInfo, netId); 140 assertSame(addresses, actual); 141 } 142 143 // Test with a non-numeric address that will trigger a network policy check. 144 { 145 final String node = "non-numeric"; 146 final int netId = 1234; 147 final StructAddrinfo nonNumericAddrInfo = new StructAddrinfo(); 148 InetAddress[] actual = 149 blockGuardOs.android_getaddrinfo(node, nonNumericAddrInfo, netId); 150 151 verify(mockThreadPolicy, times(1)).onNetwork(); 152 verify(mockOsDelegate, times(1)).android_getaddrinfo(node, nonNumericAddrInfo, netId); 153 assertSame(addresses, actual); 154 } 155 } 156 157 @Test test_nonblock()158 public void test_nonblock() throws ErrnoException, IOException { 159 FileDescriptor unixSocket = Libcore.os.socket(AF_UNIX, SOCK_DGRAM, 0); 160 FileDescriptor udpSocket = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 161 Libcore.os.fcntlInt(unixSocket, F_SETFL, O_NONBLOCK); 162 Libcore.os.fcntlInt(udpSocket, F_SETFL, O_NONBLOCK); 163 try { 164 assertTrue(BlockGuardOs.isNonBlockingFile(unixSocket)); 165 assertTrue(BlockGuardOs.isNonBlockingFile(udpSocket)); 166 } finally { 167 IoUtils.closeQuietly(unixSocket); 168 } 169 } 170 171 @Test test_unixSocket()172 public void test_unixSocket() throws ErrnoException, IOException { 173 FileDescriptor unixSocket = Libcore.os.socket(AF_UNIX, SOCK_DGRAM, 0); 174 FileDescriptor udpSocket = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 175 try { 176 assertTrue(BlockGuardOs.isUnixSocket(unixSocket)); 177 assertFalse(BlockGuardOs.isUnixSocket(udpSocket)); 178 } finally { 179 IoUtils.closeQuietly(unixSocket); 180 } 181 } 182 183 @Test test_accept_networkPolicy()184 public void test_accept_networkPolicy() throws ErrnoException, IOException { 185 BlockGuardOs blockGuardOs = new BlockGuardOs(mockOsDelegate); 186 187 FileDescriptor unixSocket = Libcore.os.socket(AF_UNIX, SOCK_DGRAM, 0); 188 Libcore.os.fcntlInt(unixSocket, F_SETFL, O_NONBLOCK); 189 SocketAddress address = UnixSocketAddress.createAbstract("test_accept_networkPolicy"); 190 Libcore.os.bind(unixSocket, address); 191 try { 192 assertNull(blockGuardOs.accept(unixSocket, address)); 193 } finally { 194 IoUtils.closeQuietly(unixSocket); 195 } 196 } 197 198 @Test test_connect_networkPolicy()199 public void test_connect_networkPolicy() throws ErrnoException, IOException { 200 BlockGuardOs blockGuardOs = new BlockGuardOs(mockOsDelegate); 201 202 // Test connect with a UDP socket that will not trigger a network policy check. 203 FileDescriptor udpSocket = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 204 try { 205 blockGuardOs.connect(udpSocket, InetAddress.getLoopbackAddress(), 0); 206 verify(mockThreadPolicy, never()).onNetwork(); 207 verify(mockOsDelegate, times(1)).connect(eq(udpSocket), any(), anyInt()); 208 } finally { 209 IoUtils.closeQuietly(udpSocket); 210 } 211 212 // Test connect with a TCP socket that will trigger a network policy check. 213 FileDescriptor tcpSocket = Libcore.os.socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 214 try { 215 blockGuardOs.connect(tcpSocket, InetAddress.getLoopbackAddress(), 0); 216 verify(mockThreadPolicy, times(1)).onNetwork(); 217 verify(mockOsDelegate, times(1)).connect(eq(tcpSocket), any(), anyInt()); 218 } finally { 219 IoUtils.closeQuietly(tcpSocket); 220 } 221 } 222 223 /** 224 * Checks that BlockGuardOs is updated when the Os interface changes. BlockGuardOs extends 225 * ForwardingOs so doing so isn't an obvious step and it can be missed. When adding methods to 226 * Os developers must give consideration to whether extra behavior should be added to 227 * BlockGuardOs. Developers failing this test should add to the list of method below 228 * (if the calls cannot block) or should add an override for the method with the appropriate 229 * calls to BlockGuard (if the calls can block). 230 */ 231 @Test 232 @NonCts(bug = 401130471, reason = NonCtsReasons.NON_BREAKING_BEHAVIOR_FIX) test_checkNewMethodsInPosix()233 public void test_checkNewMethodsInPosix() { 234 List<String> methodsNotRequireBlockGuardChecks = Arrays.asList( 235 "android_fdsan_exchange_owner_tag(java.io.FileDescriptor,long,long)", 236 "android_fdsan_get_owner_tag(java.io.FileDescriptor)", 237 "android_fdsan_get_tag_type(long)", 238 "android_fdsan_get_tag_value(long)", 239 "bind(java.io.FileDescriptor,java.net.InetAddress,int)", 240 "bind(java.io.FileDescriptor,java.net.SocketAddress)", 241 "capget(android.system.StructCapUserHeader)", 242 "capset(android.system.StructCapUserHeader,android.system.StructCapUserData[])", 243 "dup(java.io.FileDescriptor)", 244 "dup2(java.io.FileDescriptor,int)", 245 "environ()", 246 "fcntlInt(java.io.FileDescriptor,int,int)", 247 "fcntlVoid(java.io.FileDescriptor,int)", 248 "gai_strerror(int)", 249 "getegid()", 250 "getenv(java.lang.String)", 251 "geteuid()", 252 "getgid()", 253 "getgroups()", 254 "getifaddrs()", 255 "getnameinfo(java.net.InetAddress,int)", 256 "getpeername(java.io.FileDescriptor)", 257 "getpgid(int)", 258 "getpid()", 259 "getppid()", 260 "getpwnam(java.lang.String)", 261 "getpwuid(int)", 262 "getrlimit(int)", 263 "getsockname(java.io.FileDescriptor)", 264 "getsockoptByte(java.io.FileDescriptor,int,int)", 265 "getsockoptInAddr(java.io.FileDescriptor,int,int)", 266 "getsockoptInt(java.io.FileDescriptor,int,int)", 267 "getsockoptLinger(java.io.FileDescriptor,int,int)", 268 "getsockoptTimeval(java.io.FileDescriptor,int,int)", 269 "getsockoptUcred(java.io.FileDescriptor,int,int)", 270 "gettid()", 271 "getuid()", 272 "if_indextoname(int)", 273 "if_nametoindex(java.lang.String)", 274 "inet_pton(int,java.lang.String)", 275 "ioctlFlags(java.io.FileDescriptor,java.lang.String)", 276 "ioctlInetAddress(java.io.FileDescriptor,int,java.lang.String)", 277 "ioctlInt(java.io.FileDescriptor,int)", 278 "ioctlMTU(java.io.FileDescriptor,java.lang.String)", 279 "isatty(java.io.FileDescriptor)", 280 "kill(int,int)", 281 "listen(java.io.FileDescriptor,int)", 282 "listxattr(java.lang.String)", 283 "madvise(long,long,int)", 284 "memfd_create(java.lang.String,int)", 285 "mincore(long,long,byte[])", 286 "mlock(long,long)", 287 "mmap(long,long,int,int,java.io.FileDescriptor,long)", 288 "munlock(long,long)", 289 "munmap(long,long)", 290 "pipe2(int)", 291 "prctl(int,long,long,long,long)", 292 "setegid(int)", 293 "setenv(java.lang.String,java.lang.String,boolean)", 294 "seteuid(int)", 295 "setgid(int)", 296 "setgroups(int[])", 297 "setpgid(int,int)", 298 "setregid(int,int)", 299 "setreuid(int,int)", 300 "setsid()", 301 "setsockoptByte(java.io.FileDescriptor,int,int,int)", 302 "setsockoptGroupReq(java.io.FileDescriptor,int,int,android.system.StructGroupReq)", 303 "setsockoptIfreq(java.io.FileDescriptor,int,int,java.lang.String)", 304 "setsockoptInt(java.io.FileDescriptor,int,int,int)", 305 "setsockoptIpMreqn(java.io.FileDescriptor,int,int,int)", 306 "setsockoptLinger(java.io.FileDescriptor,int,int,android.system.StructLinger)", 307 "setsockoptTimeval(java.io.FileDescriptor,int,int,android.system.StructTimeval)", 308 "setuid(int)", 309 "shutdown(java.io.FileDescriptor,int)", 310 "strerror(int)", 311 "strsignal(int)", 312 "sysconf(int)", 313 "tcdrain(java.io.FileDescriptor)", 314 "tcsendbreak(java.io.FileDescriptor,int)", 315 "umask(int)", 316 "uname()", 317 "unsetenv(java.lang.String)", 318 "waitpid(int,android.system.Int32Ref,int)"); 319 Set<String> methodsNotRequiredBlockGuardCheckSet = new HashSet<>( 320 methodsNotRequireBlockGuardChecks); 321 322 Set<String> methodsInBlockGuardOs = new HashSet<>(); 323 324 // Populate the set of the public methods implemented in BlockGuardOs. 325 for (Method method : BlockGuardOs.class.getDeclaredMethods()) { 326 String methodNameAndParameters = getMethodNameAndParameters(method.toString()); 327 methodsInBlockGuardOs.add(methodNameAndParameters); 328 } 329 330 // Verify that all the methods in libcore.io.Os should either be overridden in BlockGuardOs 331 // or else they should be in the "methodsNotRequiredBlockGuardCheckSet". 332 // We don't care about static methods because they can't be overridden. 333 for (Method method : Os.class.getDeclaredMethods()) { 334 if (Modifier.isStatic(method.getModifiers())) { 335 continue; 336 } 337 String methodSignature = method.toString(); 338 String methodNameAndParameters = getMethodNameAndParameters(methodSignature); 339 if (!methodsNotRequiredBlockGuardCheckSet.contains(methodNameAndParameters) && 340 !methodsInBlockGuardOs.contains(methodNameAndParameters)) { 341 fail(methodNameAndParameters + " is not present in " 342 + "methodsNotRequiredBlockGuardCheckSet and is also not overridden in" 343 + " BlockGuardOs class. Either override the method in BlockGuardOs or" 344 + " add it in the methodsNotRequiredBlockGuardCheckSet"); 345 346 } 347 } 348 } 349 350 /** 351 * Extract method name and parameter information from the method signature. 352 * For example, for input "public void package.class.method(A,B)", the output will be 353 * "method(A,B)". 354 */ getMethodNameAndParameters(String methodSignature)355 private static String getMethodNameAndParameters(String methodSignature) { 356 Matcher methodPatternMatcher = pattern.matcher(methodSignature); 357 if (methodPatternMatcher.find()) { 358 return methodPatternMatcher.group(); 359 } else { 360 throw new IllegalArgumentException(methodSignature); 361 } 362 } 363 } 364