blob: a7d9702e3bef0ff9f30d21be4aa7158a39b86ba0 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.security.cts;
import static android.system.OsConstants.*;
import static org.junit.Assert.fail;
import android.system.ErrnoException;
import android.system.Os;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileDescriptor;
@RunWith(AndroidJUnit4.class)
public class FileDescriptorTest {
/**
* Verify that all file descriptors (excluding STDIN, STDOUT, and STDERR)
* have enabled O_CLOEXEC. O_CLOEXEC is essential for avoiding
* file descriptor leaks across exec() boundaries. Leaking FDs
* wastes resources and can lead to security bugs if an exec()ed
* process improperly uses the file descriptor.
*
* When using native code, we cannot rely on Runtime.exec and
* ProcessBuilder.start closing file descriptors for us. The
* native code may make it's own calls to fork() / exec()
* without pruning open file descriptors.
*
* This test works by scanning all open file descriptors in
* /proc/self/fd/*, calling fcntl(F_GETFD) on the file descriptor,
* and verifying that the FD_CLOEXEC flag is set.
*/
@Test
public void testCLOEXEC() throws Exception {
StringBuilder failMsg = new StringBuilder(
"The following FDs do not have O_CLOEXEC enabled:\n");
boolean failed = false;
File[] files = new File("/proc/self/fd").listFiles();
for (File file : files) {
int fdint = Integer.parseInt(file.getName());
if (fdint < 3) {
// STDIN, STDOUT, and STDERR are expected to not have O_CLOEXEC
continue;
}
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fdint);
try {
int flags = Os.fcntlInt(fd, F_GETFD, 0);
if ((flags & FD_CLOEXEC) == 0) {
failed = true;
String link = Os.readlink(file.toString());
failMsg.append(" ").append(file).append(" -> \"")
.append(link).append("\"\n");
}
} catch (ErrnoException e) {
// There's no guarantee that the file descriptor will still
// be available when we try to run fcntl on it. The primary
// reason why this will occur is because the file descriptor
// underlying the listFiles() call above will be gone by
// the time we go to examine it, but it could also happen
// due to multi-threaded race conditions.
if (e.errno != EBADF) {
throw e;
}
}
}
if (failed) {
fail(failMsg.toString());
}
}
}