Skip to content

Commit cc80bb3

Browse files
committed
Mark fds with cloexec, plus test
1 parent 9927ec0 commit cc80bb3

File tree

4 files changed

+162
-18
lines changed

4 files changed

+162
-18
lines changed

src/java.base/unix/native/libjava/childproc.c

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ closeSafely(int fd)
5151
return (fd == -1) ? 0 : close(fd);
5252
}
5353

54+
int
55+
markCloseOnExec(int fd)
56+
{
57+
const int flags = fcntl(fd, F_GETFD);
58+
if (flags < 0) {
59+
return -1;
60+
}
61+
if ((flags & FD_CLOEXEC) == 0) {
62+
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
63+
return -1;
64+
}
65+
}
66+
return 0;
67+
}
68+
5469
static int
5570
isAsciiDigit(char c)
5671
{
@@ -67,21 +82,10 @@ isAsciiDigit(char c)
6782
#endif
6883

6984
static int
70-
closeDescriptors(void)
85+
markDescriptorsCloseOnExec(void)
7186
{
7287
DIR *dp;
7388
struct dirent *dirp;
74-
int from_fd = FAIL_FILENO + 1;
75-
76-
/* We're trying to close all file descriptors, but opendir() might
77-
* itself be implemented using a file descriptor, and we certainly
78-
* don't want to close that while it's in use. We assume that if
79-
* opendir() is implemented using a file descriptor, then it uses
80-
* the lowest numbered file descriptor, just like open(). So we
81-
* close a couple explicitly. */
82-
83-
close(from_fd); /* for possible use by opendir() */
84-
close(from_fd + 1); /* another one for good luck */
8589

8690
#if defined(_AIX)
8791
/* AIX does not understand '/proc/self' - it requires the real process ID */
@@ -90,18 +94,21 @@ closeDescriptors(void)
9094
#endif
9195

9296
if ((dp = opendir(FD_DIR)) == NULL)
93-
return 0;
97+
return -1;
9498

9599
while ((dirp = readdir(dp)) != NULL) {
96100
int fd;
97101
if (isAsciiDigit(dirp->d_name[0]) &&
98-
(fd = strtol(dirp->d_name, NULL, 10)) >= from_fd + 2)
99-
close(fd);
102+
(fd = strtol(dirp->d_name, NULL, 10)) > STDERR_FILENO) {
103+
if (markCloseOnExec(fd) == -1) {
104+
return -1;
105+
}
106+
}
100107
}
101108

102109
closedir(dp);
103110

104-
return 1;
111+
return 0;
105112
}
106113

107114
static int
@@ -393,11 +400,11 @@ childProcess(void *arg)
393400
fail_pipe_fd = FAIL_FILENO;
394401

395402
/* close everything */
396-
if (closeDescriptors() == 0) { /* failed, close the old way */
403+
if (markDescriptorsCloseOnExec() == -1) { /* failed, close the old way */
397404
int max_fd = (int)sysconf(_SC_OPEN_MAX);
398405
int fd;
399406
for (fd = FAIL_FILENO + 1; fd < max_fd; fd++)
400-
if (close(fd) == -1 && errno != EBADF)
407+
if (markCloseOnExec(fd) == -1 && errno != EBADF)
401408
goto WhyCantJohnnyExec;
402409
}
403410

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
25+
/**
26+
* @test
27+
* @summary Check that we don't leak FDs
28+
* @library /test/lib
29+
* @run main/othervm/native -agentlib:FDLeaker FDLeakTest
30+
*/
31+
32+
import jdk.test.lib.process.ProcessTools;
33+
public class FDLeakTest {
34+
// This test has two native parts:
35+
// - a library invoked with -agentlib that opens a native file without setting FD_CLOEXEC (libFDLeaker.c). This is
36+
// necessary because there is no way to do this from Java (if Java functions correctly, all files the user could
37+
// open via its APIs should be marked with FD_CLOEXEC).
38+
// - a small native executable that tests - without using /proc - whether any file descriptors other than
39+
// stdin/out/err are open.
40+
//
41+
public static void main(String[] args) throws Exception {
42+
ProcessBuilder pb = ProcessTools.createNativeTestProcessBuilder("FDLeakTester");
43+
pb.inheritIO();
44+
Process p = pb.start();
45+
p.waitFor();
46+
if (p.exitValue() != 0) {
47+
throw new RuntimeException("Failed");
48+
}
49+
}
50+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
#include <fcntl.h>
25+
#include <stdio.h>
26+
#include <string.h>
27+
#include <unistd.h>
28+
29+
/* Check if any fd past stderr is valid; if true, print warning on stderr and return -1
30+
* Note: check without accessing /proc or similar techniques since those depend on OS and
31+
* may cause temp. file descriptors to open */
32+
int main(int argc, char** argv) {
33+
int max_fd = (int)sysconf(_SC_OPEN_MAX);
34+
if (max_fd == -1) {
35+
max_fd = 10000;
36+
}
37+
// We start past stderr fd
38+
const int max_error = 10;
39+
int errors = 0;
40+
int rc = 0;
41+
for (int fd = 3; fd < max_fd; fd++) {
42+
if (fcntl(fd, F_GETFD, 0) >= 0) {
43+
// Error: found valid file descriptor
44+
errors++;
45+
char buf[128];
46+
snprintf(buf, sizeof(buf), "*** Parent leaked filedescriptor %d ***\n", fd);
47+
rc = write(2, buf, strlen(buf));
48+
}
49+
}
50+
return errors > 0 ? -1 : 0;
51+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
#include <stdio.h>
25+
#include "jvmti.h"
26+
27+
JNIEXPORT jint JNICALL
28+
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
29+
const char* filename = "./testfile_FDLeaker.txt";
30+
FILE* f = fopen(filename, "w");
31+
if (f == NULL) {
32+
return JNI_ERR;
33+
}
34+
printf("Opened and leaked %s (%d)", filename, fileno(f));
35+
return JNI_OK;
36+
}

0 commit comments

Comments
 (0)