changeset 270:0718f538c5f9

dep/animone: filter open files by access mode
author Paper <paper@paper.us.eu.org>
date Fri, 12 Apr 2024 19:13:50 -0400
parents 3efac0541151
children f01b6e9c8fa2
files dep/animone/configure.ac dep/animone/src/fd/bsd.cc dep/animone/src/fd/proc.cc dep/animone/src/fd/xnu.cc
diffstat 4 files changed, 79 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/dep/animone/configure.ac	Fri Apr 12 05:23:45 2024 -0400
+++ b/dep/animone/configure.ac	Fri Apr 12 19:13:50 2024 -0400
@@ -42,18 +42,15 @@
 		;;
 	*)
 		dnl BSDs
-		build_bsd=yes
-		AC_DEFINE([BSD])
 		AC_CHECK_LIB([util], [kinfo_getfile], [build_libutil=yes], [build_libutil=no])
-
-		dnl if we have this function it means kvm_openfiles likely also supports NULL values,
-		dnl i.e., loading the currently running kernel
 		AC_CHECK_LIB([kvm], [kvm_getfiles], [build_kvm=yes], [build_kvm=no])
 
 		if test "x$build_kvm" = "xyes"; then
 			AC_DEFINE([LIBKVM])
+			AC_DEFINE([BSD])
 		elif test "x$build_libutil" = "xyes"; then
 			AC_DEFINE([LIBUTIL])
+			AC_DEFINE([BSD])
 		fi
 		;;
 esac
--- a/dep/animone/src/fd/bsd.cc	Fri Apr 12 05:23:45 2024 -0400
+++ b/dep/animone/src/fd/bsd.cc	Fri Apr 12 19:13:50 2024 -0400
@@ -13,6 +13,7 @@
 
 #ifdef HAVE_KVM_GETFILES
 #	include <kvm.h>
+#	include <fts.h>
 #elif defined(LIBUTIL)
 #	include <libutil.h>
 #endif
@@ -110,10 +111,8 @@
 		return false;
 
 	/* actually get our results */
-	if (sysctl((const int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, result.get(), &length, NULL, 0) == ENOMEM) {
-		result.reset();
-		throw std::bad_alloc();
-	}
+	if (sysctl((const int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, result.get(), &length, NULL, 0) == ENOMEM)
+		return false;
 
 	if (length < sizeof(struct kinfo_proc))
 		return false;
@@ -126,10 +125,47 @@
 #endif
 }
 
+#ifdef HAVE_KVM_GETFILES
+static bool GetOpenFileName(const struct kinfo_file& file, std::string& name) {
+	/* OpenBSD doesn't provide a native API for this, so we have
+	 * to do it ourselves */
+	static constexpr std::string_view root = "/";
+
+	FTS* file_system = fts_open(root.data(), FTS_COMFOLLOW | FTS_NOCHDIR, nullptr);
+	if (!file_system)
+		return false;
+
+	/* Search through the filesystem for a file that matches our
+	 * kinfo_file structure */
+	FTSENT* parent = nullptr;
+	while ((parent = fts_read(file_system))) {
+		FTSENT* child = fts_children(file_system, 0);
+		while (child && child->fts_link) {
+			child = child->fts_link;
+			if (!S_ISREG(child->fts_statp->st_mode) || !S_ISLNK(child->fts_statp->st_mode))
+				continue;
+
+			if (child->fts_statp->st_dev != file->va_fsid)
+				continue;
+
+			if (child->fts_statp->st_ino != file->va_fileid)
+				continue;
+
+			name = std::string(child->fts_path) + child->fts_name;
+			fts_close(file_system);
+			return true;
+		}
+	}
+
+	fts_close(filesystem);
+	return false;
+}
+#endif /* HAVE_KVM_GETFILES */
+
 bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
 #ifdef HAVE_KVM_GETFILES
 	char errbuf[_POSIX2_LINE_MAX];
-	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
+	kvm_t* kernel = kvm_openfiles(nullptr, nullptr, nullptr, O_RDONLY, errbuf);
 	if (!kernel)
 		return false;
 
@@ -142,7 +178,15 @@
 		}
 
 		for (int i = 0; i < cnt; i++) {
-			if (!open_file_proc({pid, kfile[i].kf_path})) {
+			uint32_t oflags = kfile[i].kf_flags & O_ACCMODE;
+			if (oflags == O_WRONLY || oflags == O_RDWR)
+				continue;
+
+			std::string name;
+			if (!GetOpenFileName(kfile[i], name))
+				continue;
+
+			if (!open_file_proc({pid, name})) {
 				kvm_close(kernel);
 				return false;
 			}
@@ -150,10 +194,8 @@
 	}
 
 	kvm_close(kernel);
-
 	return true;
 #elif defined(LIBUTIL)
-	/* does this code even work? */
 	for (const auto& pid : pids) {
 		int cnt;
 		std::unique_ptr<struct kinfo_file[]> files(kinfo_getfile(pid, &cnt));
@@ -162,7 +204,11 @@
 
 		for (int i = 0; i < cnt; i++) {
 			const struct kinfo_file& current = files[i];
-			if (current.kf_vnode_type != KF_VTYPE_VREG)
+			if (current.kf_vnode_type != KF_VTYPE_VREG || current.kf_vnode_type != KF_VTYPE_VLNK)
+				continue;
+
+			const int oflags = current.kf_flags & O_ACCMODE;
+			if (oflags == O_WRONLY || oflags == O_RDWR)
 				continue;
 
 			if (!open_file_proc({pid, current.kf_path}))
@@ -172,29 +218,8 @@
 
 	return true;
 #else
-	for (const auto& pid : pids) {
-		int mib[6] = {CTL_KERN, KERN_FILE2, KERN_FILE_BYPID, pid, sizeof(struct kinfo_file), 0};
-
-		size_t len = 0;
-		if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &len, NULL, 0) == -1)
-			return false;
-
-		mib[5] = len / sizeof(struct kinfo_file);
-
-		std::unique_ptr<struct kinfo_file[]> buf(new struct kinfo_file[mib[5]]);
-		if (!buf)
-			return false;
-
-		if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf.get(), &len, NULL, 0) == -1)
-			return false;
-
-		/* TODO: check kfile[i].ki_ofileflags */
-		for (size_t i = 0; i < mib[5]; i++)
-			if (!open_file_proc({pid, kfile[i].kf_path}))
-				return false;
-	}
-
-	return true;
+	/* NetBSD doesn't even provide a real API for this */
+	return false;
 #endif
 }
 
--- a/dep/animone/src/fd/proc.cc	Fri Apr 12 05:23:45 2024 -0400
+++ b/dep/animone/src/fd/proc.cc	Fri Apr 12 19:13:50 2024 -0400
@@ -36,7 +36,9 @@
 		if (line.find("flags:", 0) == 0)
 			flags = util::StringToInt(line.substr(line.find_last_not_of("0123456789") + 1));
 
-	if (flags & O_WRONLY || flags & O_RDWR)
+	/* check if the file was opened in a write mode */
+	int accflags = flags & O_ACCMODE;
+	if (accflags == O_WRONLY || accflags == O_RDWR)
 		return false;
 
 	return true;
@@ -65,11 +67,9 @@
 	static constexpr std::array<std::string_view, 9> invalid_paths = {"/boot", "/dev", "/bin", "/usr", "/opt",
 	                                                                  "/proc", "/var", "/etc", "/dev"};
 
-	for (const auto& invalid_path : invalid_paths) {
-		if (!path.rfind(invalid_path, 0)) {
+	for (const auto& invalid_path : invalid_paths)
+		if (!path.rfind(invalid_path, 0))
 			return true;
-		}
-	}
 
 	return false;
 }
--- a/dep/animone/src/fd/xnu.cc	Fri Apr 12 05:23:45 2024 -0400
+++ b/dep/animone/src/fd/xnu.cc	Fri Apr 12 19:13:50 2024 -0400
@@ -14,6 +14,18 @@
 #include <sys/types.h>
 #include <sys/user.h>
 
+/* you may be asking: WTF is FWRITE?
+ * well, from bsd/sys/fcntl.h in the XNU kernel:
+ *
+ *    Kernel encoding of open mode; separate read and write bits that are
+ *    independently testable: 1 greater than [O_RDONLY and O_WRONLY].
+ *
+ * It's just how the kernel defines write mode.
+*/
+#ifndef FWRITE
+#define FWRITE	0x0002
+#endif
+
 namespace animone::internal::xnu {
 
 bool EnumerateOpenProcesses(process_proc_t process_proc) {
@@ -66,11 +78,9 @@
 				if (sz != PROC_PIDFDVNODEPATHINFO_SIZE)
 					return false;
 
-				// This doesn't work (for unknown reasons). I assume somethings fucked up with
-				// my assumptions; I don't care enough to look into it tbh
-				//
-				// if (vnodeInfo.pfi.fi_openflags & O_WRONLY || vnodeInfo.pfi.fi_openflags & O_RDWR)
-				//     continue;
+				/* why would a media player open a file in write mode? */
+				if (vnodeInfo.pfi.fi_openflags & FWRITE)
+					continue;
 
 				if (!open_file_proc({pid, vnodeInfo.pvip.vip_path}))
 					return false;