all: add optional close_fds feature to reproducers

Instead of always closing open fds (number 3 to 30) after each program,
add an options called EnableCloseFds. It can be passed to syz-execprog,
syz-prog2c and syz-stress via the -enable and -disable flags. Set the
default value to true. Also minimize C repros over it, except for when
repeat is enabled.
diff --git a/executor/common.h b/executor/common.h
index 5f33d3b..10e5b96 100644
--- a/executor/common.h
+++ b/executor/common.h
@@ -483,6 +483,9 @@
 	}
 	for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++)
 		sleep_ms(1);
+#if SYZ_HAVE_CLOSE_FDS
+	close_fds();
+#endif
 #if SYZ_COLLIDE
 	if (!collide) {
 		collide = 1;
@@ -571,8 +574,8 @@
 			close(kOutPipeFd);
 #endif
 			execute_one();
-#if SYZ_HAVE_RESET_TEST
-			reset_test();
+#if SYZ_HAVE_CLOSE_FDS && !SYZ_THREADED
+			close_fds();
 #endif
 			doexit(0);
 #endif
@@ -659,6 +662,9 @@
 #endif
 {
 	/*SYSCALLS*/
+#if SYZ_HAVE_CLOSE_FDS && !SYZ_THREADED && !SYZ_REPEAT
+	close_fds();
+#endif
 }
 #endif
 
@@ -690,6 +696,10 @@
 			use_temporary_dir();
 #endif
 			/*SANDBOX_FUNC*/
+#if SYZ_HAVE_CLOSE_FDS && !SYZ_THREADED && !SYZ_REPEAT && !SYZ_SANDBOX_NONE && \
+    !SYZ_SANDBOX_SETUID && !SYZ_SANDBOX_NAMESPACE && !SYZ_SANDBOX_ANDROID_UNTRUSTED_APP
+			close_fds();
+#endif
 #if SYZ_PROCS
 		}
 	}
diff --git a/executor/common_linux.h b/executor/common_linux.h
index b849555..0fd7a15 100644
--- a/executor/common_linux.h
+++ b/executor/common_linux.h
@@ -2612,12 +2612,20 @@
 	flush_tun();
 #endif
 }
+#endif
 
-#define SYZ_HAVE_RESET_TEST 1
-static void reset_test()
+#if SYZ_EXECUTOR || SYZ_ENABLE_CLOSE_FDS
+#define SYZ_HAVE_CLOSE_FDS 1
+static void close_fds()
 {
+#if SYZ_EXECUTOR
+	if (!flag_enable_close_fds)
+		return;
+#endif
 	// Keeping a 9p transport pipe open will hang the proccess dead,
 	// so close all opened file descriptors.
+	// Also close all USB emulation descriptors to trigger exit from USB
+	// event loop to collect coverage.
 	int fd;
 	for (fd = 3; fd < 30; fd++)
 		close(fd);
diff --git a/executor/executor.cc b/executor/executor.cc
index f1f6ba2..bbbb2da 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -119,6 +119,7 @@
 static bool flag_enable_net_reset;
 static bool flag_enable_cgroups;
 static bool flag_enable_binfmt_misc;
+static bool flag_enable_close_fds;
 
 static bool flag_collect_cover;
 static bool flag_dedup_cover;
@@ -454,6 +455,7 @@
 	flag_enable_net_reset = flags & (1 << 9);
 	flag_enable_cgroups = flags & (1 << 10);
 	flag_enable_binfmt_misc = flags & (1 << 11);
+	flag_enable_close_fds = flags & (1 << 12);
 }
 
 #if SYZ_EXECUTOR_USES_FORK_SERVER
@@ -732,6 +734,10 @@
 		}
 	}
 
+#if SYZ_HAVE_CLOSE_FDS
+	close_fds();
+#endif
+
 	if (flag_collide && !flag_inject_fault && !colliding && !collide) {
 		debug("enabling collider\n");
 		collide = colliding = true;
diff --git a/pkg/csource/common.go b/pkg/csource/common.go
index 204256d..d0c6340 100644
--- a/pkg/csource/common.go
+++ b/pkg/csource/common.go
@@ -89,6 +89,7 @@
 		"SYZ_ENABLE_NETDEV":                 opts.EnableNetDev,
 		"SYZ_RESET_NET_NAMESPACE":           opts.EnableNetReset,
 		"SYZ_ENABLE_BINFMT_MISC":            opts.EnableBinfmtMisc,
+		"SYZ_ENABLE_CLOSE_FDS":              opts.EnableCloseFds,
 		"SYZ_USE_TMP_DIR":                   opts.UseTmpDir,
 		"SYZ_HANDLE_SEGV":                   opts.HandleSegv,
 		"SYZ_REPRO":                         opts.Repro,
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go
index 71a3810..4bd86bc 100644
--- a/pkg/csource/generated.go
+++ b/pkg/csource/generated.go
@@ -4319,10 +4319,16 @@
 	flush_tun();
 #endif
 }
+#endif
 
-#define SYZ_HAVE_RESET_TEST 1
-static void reset_test()
+#if SYZ_EXECUTOR || SYZ_ENABLE_CLOSE_FDS
+#define SYZ_HAVE_CLOSE_FDS 1
+static void close_fds()
 {
+#if SYZ_EXECUTOR
+	if (!flag_enable_close_fds)
+		return;
+#endif
 	int fd;
 	for (fd = 3; fd < 30; fd++)
 		close(fd);
@@ -4614,6 +4620,9 @@
 	}
 	for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++)
 		sleep_ms(1);
+#if SYZ_HAVE_CLOSE_FDS
+	close_fds();
+#endif
 #if SYZ_COLLIDE
 	if (!collide) {
 		collide = 1;
@@ -4698,8 +4707,8 @@
 			close(kOutPipeFd);
 #endif
 			execute_one();
-#if SYZ_HAVE_RESET_TEST
-			reset_test();
+#if SYZ_HAVE_CLOSE_FDS && !SYZ_THREADED
+			close_fds();
 #endif
 			doexit(0);
 #endif
@@ -4771,6 +4780,9 @@
 #endif
 {
 	/*SYSCALLS*/
+#if SYZ_HAVE_CLOSE_FDS && !SYZ_THREADED && !SYZ_REPEAT
+	close_fds();
+#endif
 }
 #endif
 #if GOOS_akaros && SYZ_REPEAT
@@ -4800,6 +4812,10 @@
 			use_temporary_dir();
 #endif
 			/*SANDBOX_FUNC*/
+#if SYZ_HAVE_CLOSE_FDS && !SYZ_THREADED && !SYZ_REPEAT && !SYZ_SANDBOX_NONE && \
+    !SYZ_SANDBOX_SETUID && !SYZ_SANDBOX_NAMESPACE && !SYZ_SANDBOX_ANDROID_UNTRUSTED_APP
+			close_fds();
+#endif
 #if SYZ_PROCS
 		}
 	}
diff --git a/pkg/csource/options.go b/pkg/csource/options.go
index d5b3961..0a95396 100644
--- a/pkg/csource/options.go
+++ b/pkg/csource/options.go
@@ -34,6 +34,7 @@
 	EnableNetReset   bool `json:"resetnet,omitempty"`
 	EnableCgroups    bool `json:"cgroups,omitempty"`
 	EnableBinfmtMisc bool `json:"binfmt_misc,omitempty"`
+	EnableCloseFds   bool `json:"close_fds"`
 
 	UseTmpDir  bool `json:"tmpdir,omitempty"`
 	HandleSegv bool `json:"segv,omitempty"`
@@ -117,6 +118,9 @@
 	if opts.EnableBinfmtMisc {
 		return fmt.Errorf("EnableBinfmtMisc is not supported on %v", OS)
 	}
+	if opts.EnableCloseFds {
+		return fmt.Errorf("EnableCloseFds is not supported on %v", OS)
+	}
 	if opts.Sandbox == sandboxNamespace ||
 		(opts.Sandbox == sandboxSetuid && !(OS == openbsd || OS == freebsd)) ||
 		opts.Sandbox == sandboxAndroidUntrustedApp {
@@ -140,6 +144,7 @@
 		EnableNetReset:   true,
 		EnableCgroups:    true,
 		EnableBinfmtMisc: true,
+		EnableCloseFds:   true,
 		UseTmpDir:        true,
 		HandleSegv:       true,
 		Repro:            true,
@@ -150,6 +155,7 @@
 		opts.EnableNetReset = false
 		opts.EnableCgroups = false
 		opts.EnableBinfmtMisc = false
+		opts.EnableCloseFds = false
 	}
 	if cfg.Sandbox == "" || cfg.Sandbox == "setuid" {
 		opts.EnableNetReset = false
@@ -170,6 +176,9 @@
 
 func DeserializeOptions(data []byte) (Options, error) {
 	var opts Options
+	// Before EnableCloseFds was added, close_fds() was always called,
+	// so default to true.
+	opts.EnableCloseFds = true
 	if err := json.Unmarshal(data, &opts); err == nil {
 		return opts, nil
 	}
@@ -225,6 +234,7 @@
 		"net_reset":   {"reset network namespace between programs", value},
 		"cgroups":     {"setup cgroups for testing", value},
 		"binfmt_misc": {"setup binfmt_misc for testing", value},
+		"close_fds":   {"close fds after each program", value},
 	}
 }
 
diff --git a/pkg/csource/options_test.go b/pkg/csource/options_test.go
index 0240208..13afa84 100644
--- a/pkg/csource/options_test.go
+++ b/pkg/csource/options_test.go
@@ -44,6 +44,7 @@
 			EnableNetReset:   true,
 			EnableCgroups:    true,
 			EnableBinfmtMisc: false,
+			EnableCloseFds:   true,
 			UseTmpDir:        true,
 			HandleSegv:       true,
 			Repro:            true,
@@ -65,6 +66,7 @@
 			EnableNetReset:   true,
 			EnableCgroups:    true,
 			EnableBinfmtMisc: false,
+			EnableCloseFds:   true,
 			UseTmpDir:        true,
 			HandleSegv:       true,
 			Repro:            true,
@@ -81,6 +83,7 @@
 			EnableTun:        true,
 			EnableCgroups:    false,
 			EnableBinfmtMisc: false,
+			EnableCloseFds:   true,
 			UseTmpDir:        true,
 			HandleSegv:       true,
 			Repro:            false,
@@ -97,6 +100,7 @@
 			EnableTun:        true,
 			EnableCgroups:    false,
 			EnableBinfmtMisc: false,
+			EnableCloseFds:   true,
 			UseTmpDir:        true,
 			HandleSegv:       true,
 			Repro:            false,
@@ -113,6 +117,7 @@
 			EnableTun:        true,
 			EnableCgroups:    true,
 			EnableBinfmtMisc: false,
+			EnableCloseFds:   true,
 			UseTmpDir:        true,
 			HandleSegv:       true,
 			Repro:            false,
@@ -208,28 +213,31 @@
 		Features map[string]bool
 	}{
 		{"none", "none", true, map[string]bool{
-			"tun": true, "net_dev": true, "net_reset": true, "cgroups": true, "binfmt_misc": true,
+			"tun": true, "net_dev": true, "net_reset": true, "cgroups": true, "binfmt_misc": true, "close_fds": true,
 		}},
 		{"none", "none", false, map[string]bool{
-			"tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false,
+			"tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false, "close_fds": false,
 		}},
 		{"all", "none", true, map[string]bool{
-			"tun": true, "net_dev": true, "net_reset": true, "cgroups": true, "binfmt_misc": true,
+			"tun": true, "net_dev": true, "net_reset": true, "cgroups": true, "binfmt_misc": true, "close_fds": true,
 		}},
 		{"", "none", true, map[string]bool{
-			"tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false,
+			"tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false, "close_fds": false,
 		}},
 		{"none", "all", true, map[string]bool{
-			"tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false,
+			"tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false, "close_fds": false,
 		}},
 		{"none", "", true, map[string]bool{
-			"tun": true, "net_dev": true, "net_reset": true, "cgroups": true, "binfmt_misc": true,
+			"tun": true, "net_dev": true, "net_reset": true, "cgroups": true, "binfmt_misc": true, "close_fds": true,
 		}},
 		{"tun,net_dev", "none", true, map[string]bool{
-			"tun": true, "net_dev": true, "net_reset": false, "cgroups": false, "binfmt_misc": false,
+			"tun": true, "net_dev": true, "net_reset": false, "cgroups": false, "binfmt_misc": false, "close_fds": false,
 		}},
 		{"none", "cgroups,net_dev", true, map[string]bool{
-			"tun": true, "net_dev": false, "net_reset": true, "cgroups": false, "binfmt_misc": true,
+			"tun": true, "net_dev": false, "net_reset": true, "cgroups": false, "binfmt_misc": true, "close_fds": true,
+		}},
+		{"close_fds", "none", true, map[string]bool{
+			"tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false, "close_fds": true,
 		}},
 	}
 	for i, test := range tests {
diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go
index f3bc029..ab6c93a 100644
--- a/pkg/ipc/ipc.go
+++ b/pkg/ipc/ipc.go
@@ -38,6 +38,7 @@
 	FlagEnableNetReset                                  // reset network namespace between programs
 	FlagEnableCgroups                                   // setup cgroups for testing
 	FlagEnableBinfmtMisc                                // setup binfmt_misc for testing
+	FlagEnableCloseFds                                  // close fds after each program
 	// Executor does not know about these:
 	FlagUseShmem      // use shared memory instead of pipes for communication
 	FlagUseForkServer // use extended protocol with handshake
diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go
index 5db4116..af39016 100644
--- a/pkg/repro/repro.go
+++ b/pkg/repro/repro.go
@@ -809,6 +809,7 @@
 		opts.EnableNetReset = false
 		opts.EnableCgroups = false
 		opts.EnableBinfmtMisc = false
+		opts.EnableCloseFds = false
 		return true
 	},
 	func(opts *csource.Options) bool {
@@ -847,6 +848,15 @@
 		return true
 	},
 	func(opts *csource.Options) bool {
+		// We don't want to remove close_fds() call when repeat is enabled,
+		// since that can lead to deadlocks, see executor/common_linux.h.
+		if !opts.EnableCloseFds || opts.Repeat {
+			return false
+		}
+		opts.EnableCloseFds = false
+		return true
+	},
+	func(opts *csource.Options) bool {
 		if !opts.UseTmpDir || opts.Sandbox == "namespace" || opts.EnableCgroups {
 			return false
 		}
diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go
index d0ff157..b969380 100644
--- a/syz-fuzzer/fuzzer.go
+++ b/syz-fuzzer/fuzzer.go
@@ -202,6 +202,7 @@
 	config.Flags |= ipc.FlagEnableNetReset
 	config.Flags |= ipc.FlagEnableCgroups
 	config.Flags |= ipc.FlagEnableBinfmtMisc
+	config.Flags |= ipc.FlagEnableCloseFds
 
 	if *flagRunTest {
 		runTest(target, manager, *flagName, config.Executor)
diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go
index 07bae45..b2902ba 100644
--- a/tools/syz-execprog/execprog.go
+++ b/tools/syz-execprog/execprog.go
@@ -324,5 +324,8 @@
 	if featuresFlags["binfmt_misc"].Enabled {
 		config.Flags |= ipc.FlagEnableBinfmtMisc
 	}
+	if featuresFlags["close_fds"].Enabled {
+		config.Flags |= ipc.FlagEnableCloseFds
+	}
 	return config, execOpts
 }
diff --git a/tools/syz-prog2c/prog2c.go b/tools/syz-prog2c/prog2c.go
index d076ad2..86f703c 100644
--- a/tools/syz-prog2c/prog2c.go
+++ b/tools/syz-prog2c/prog2c.go
@@ -84,6 +84,7 @@
 		EnableNetReset:   features["net_reset"].Enabled,
 		EnableCgroups:    features["cgroups"].Enabled,
 		EnableBinfmtMisc: features["binfmt_misc"].Enabled,
+		EnableCloseFds:   features["close_fds"].Enabled,
 		UseTmpDir:        *flagUseTmpDir,
 		HandleSegv:       *flagHandleSegv,
 		Repro:            false,
diff --git a/tools/syz-stress/stress.go b/tools/syz-stress/stress.go
index e287b44..390a1fb 100644
--- a/tools/syz-stress/stress.go
+++ b/tools/syz-stress/stress.go
@@ -94,6 +94,9 @@
 	if featuresFlags["binfmt_misc"].Enabled {
 		config.Flags |= ipc.FlagEnableBinfmtMisc
 	}
+	if featuresFlags["close_fds"].Enabled {
+		config.Flags |= ipc.FlagEnableCloseFds
+	}
 	gate = ipc.NewGate(2**flagProcs, nil)
 	for pid := 0; pid < *flagProcs; pid++ {
 		pid := pid