• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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