lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Tue, 18 May 2021 02:53:00 +0400
From:   Dmitrii Banshchikov <me@...que.spb.ru>
To:     bpf@...r.kernel.org
Cc:     Dmitrii Banshchikov <me@...que.spb.ru>, ast@...nel.org,
        davem@...emloft.net, daniel@...earbox.net, andrii@...nel.org,
        kafai@...com, songliubraving@...com, yhs@...com,
        john.fastabend@...il.com, kpsingh@...nel.org,
        netdev@...r.kernel.org, rdna@...com
Subject: [PATCH bpf-next 03/11] bpfilter: Add IO functions

Introduce IO functions for:
1) reading and writing data from a descriptor: read_exact(), write_exact(),
2) reading and writing memory of other processes: pvm_read(), pvm_write().

read_exact() and write_exact() are wrappers over read(2)/write(2) with
correct handling of partial read/write. These functions are intended to
be used for communication over pipe with the kernel part of bpfilter.

pvm_read() and pvm_write() are wrappers over
process_vm_readv(2)/process_vm_writev(2) with an interface that uses a
single buffer instead of vectored form. These functions are intended to
be used for readining/writing memory buffers supplied to iptables ABI
setsockopt(2) from other processes.

Signed-off-by: Dmitrii Banshchikov <me@...que.spb.ru>
---
 net/bpfilter/Makefile                         |   2 +-
 net/bpfilter/io.c                             |  77 ++++++++++++++
 net/bpfilter/io.h                             |  18 ++++
 .../testing/selftests/bpf/bpfilter/.gitignore |   2 +
 tools/testing/selftests/bpf/bpfilter/Makefile |  17 +++
 .../testing/selftests/bpf/bpfilter/test_io.c  | 100 ++++++++++++++++++
 6 files changed, 215 insertions(+), 1 deletion(-)
 create mode 100644 net/bpfilter/io.c
 create mode 100644 net/bpfilter/io.h
 create mode 100644 tools/testing/selftests/bpf/bpfilter/.gitignore
 create mode 100644 tools/testing/selftests/bpf/bpfilter/Makefile
 create mode 100644 tools/testing/selftests/bpf/bpfilter/test_io.c

diff --git a/net/bpfilter/Makefile b/net/bpfilter/Makefile
index 874d5ef6237d..69a6c139fc7a 100644
--- a/net/bpfilter/Makefile
+++ b/net/bpfilter/Makefile
@@ -4,7 +4,7 @@
 #
 
 userprogs := bpfilter_umh
-bpfilter_umh-objs := main.o bflog.o
+bpfilter_umh-objs := main.o bflog.o io.o
 userccflags += -I $(srctree)/tools/include/ -I $(srctree)/tools/include/uapi
 
 ifeq ($(CONFIG_BPFILTER_UMH), y)
diff --git a/net/bpfilter/io.c b/net/bpfilter/io.c
new file mode 100644
index 000000000000..e645ae9d7a50
--- /dev/null
+++ b/net/bpfilter/io.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Telegram FZ-LLC
+ */
+
+#define _GNU_SOURCE
+
+#include "io.h"
+
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#define do_exact(fd, op, buffer, count)                                                            \
+	({                                                                                         \
+		size_t total = 0;                                                                  \
+		int err = 0;                                                                       \
+												   \
+		do {                                                                               \
+			const ssize_t part = op(fd, (buffer) + total, (count) - total);            \
+			if (part > 0) {                                                            \
+				total += part;                                                     \
+			} else if (part == 0 && (count) > 0) {                                     \
+				err = -EIO;                                                        \
+				break;                                                             \
+			} else if (part == -1) {                                                   \
+				if (errno == EINTR)                                                \
+					continue;                                                  \
+				err = -errno;                                                      \
+				break;                                                             \
+			}                                                                          \
+		} while (total < (count));                                                         \
+												   \
+		err;                                                                               \
+	})
+
+int read_exact(int fd, void *buffer, size_t count)
+{
+	return do_exact(fd, read, buffer, count);
+}
+
+int write_exact(int fd, const void *buffer, size_t count)
+{
+	return do_exact(fd, write, buffer, count);
+}
+
+int pvm_read(pid_t pid, void *to, const void *from, size_t count)
+{
+	const struct iovec r_iov = { .iov_base = (void *)from, .iov_len = count };
+	const struct iovec l_iov = { .iov_base = to, .iov_len = count };
+	size_t total_bytes;
+
+	total_bytes = process_vm_readv(pid, &l_iov, 1, &r_iov, 1, 0);
+	if (total_bytes == -1)
+		return -errno;
+
+	if (total_bytes != count)
+		return -EFAULT;
+
+	return 0;
+}
+
+int pvm_write(pid_t pid, void *to, const void *from, size_t count)
+{
+	const struct iovec l_iov = { .iov_base = (void *)from, .iov_len = count };
+	const struct iovec r_iov = { .iov_base = to, .iov_len = count };
+	size_t total_bytes;
+
+	total_bytes = process_vm_writev(pid, &l_iov, 1, &r_iov, 1, 0);
+	if (total_bytes == -1)
+		return -errno;
+
+	if (total_bytes != count)
+		return -EFAULT;
+
+	return 0;
+}
diff --git a/net/bpfilter/io.h b/net/bpfilter/io.h
new file mode 100644
index 000000000000..ab56c8bb8e61
--- /dev/null
+++ b/net/bpfilter/io.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Telegram FZ-LLC
+ */
+
+#ifndef NET_BPFILTER_IO_H
+#define NET_BPFILTER_IO_H
+
+#include <stddef.h>
+#include <sys/types.h>
+
+int read_exact(int fd, void *buffer, size_t count);
+int write_exact(int fd, const void *buffer, size_t count);
+
+int pvm_read(pid_t pid, void *to, const void *from, size_t count);
+int pvm_write(pid_t pid, void *to, const void *from, size_t count);
+
+#endif // NET_BPFILTER_IO_H
diff --git a/tools/testing/selftests/bpf/bpfilter/.gitignore b/tools/testing/selftests/bpf/bpfilter/.gitignore
new file mode 100644
index 000000000000..f5785e366013
--- /dev/null
+++ b/tools/testing/selftests/bpf/bpfilter/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+test_io
diff --git a/tools/testing/selftests/bpf/bpfilter/Makefile b/tools/testing/selftests/bpf/bpfilter/Makefile
new file mode 100644
index 000000000000..c02d72d89199
--- /dev/null
+++ b/tools/testing/selftests/bpf/bpfilter/Makefile
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+
+top_srcdir = ../../../../..
+TOOLSDIR := $(abspath ../../../../)
+TOOLSINCDIR := $(TOOLSDIR)/include
+APIDIR := $(TOOLSINCDIR)/uapi
+BPFILTERSRCDIR := $(top_srcdir)/net/bpfilter
+
+CFLAGS += -Wall -g -pthread -I$(TOOLSINCDIR) -I$(APIDIR) -I$(BPFILTERSRCDIR)
+
+TEST_GEN_PROGS += test_io
+
+KSFT_KHDR_INSTALL := 1
+
+include ../../lib.mk
+
+$(OUTPUT)/test_io: test_io.c $(BPFILTERSRCDIR)/io.c
diff --git a/tools/testing/selftests/bpf/bpfilter/test_io.c b/tools/testing/selftests/bpf/bpfilter/test_io.c
new file mode 100644
index 000000000000..e4294930c581
--- /dev/null
+++ b/tools/testing/selftests/bpf/bpfilter/test_io.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "io.h"
+
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../../kselftest_harness.h"
+
+FIXTURE(test_pvm)
+{
+	int wstatus;
+	int fd[2];
+	pid_t pid;
+	pid_t ppid;
+	char expected[5];
+	char actual[5];
+};
+
+FIXTURE_SETUP(test_pvm)
+{
+	snprintf(self->expected, sizeof(self->expected), "ipfw");
+	memset(self->actual, 0, sizeof(self->actual));
+	self->ppid = getpid();
+	ASSERT_EQ(pipe(self->fd), 0);
+	self->pid = fork();
+	ASSERT_NE(self->pid, -1) TH_LOG("Cannot fork(): %m\n");
+	close(self->fd[!!self->pid]);
+};
+
+FIXTURE_TEARDOWN(test_pvm)
+{
+	int wstatus;
+
+	if (!self->pid)
+		exit(0);
+
+	kill(self->pid, SIGKILL);
+	waitpid(self->pid, &wstatus, -2);
+	close(self->fd[1]);
+}
+
+TEST_F(test_pvm, read)
+{
+	if (!self->pid) {
+		const uint8_t baton = 'x';
+
+		memcpy(self->actual, self->expected, sizeof(self->actual));
+		ASSERT_EQ(write(self->fd[1], &baton, sizeof(baton)), sizeof(baton));
+
+		pause();
+		exit(0);
+	} else {
+		int err;
+		uint8_t baton;
+
+		EXPECT_EQ(read(self->fd[0], &baton, sizeof(baton)), sizeof(baton));
+		EXPECT_EQ(baton, 'x');
+
+		err = pvm_read(self->pid, &self->actual, &self->actual, sizeof(self->actual));
+		EXPECT_EQ(err, 0)
+		TH_LOG("Cannot pvm_read(): %s\n", strerror(-err));
+
+		EXPECT_EQ(memcmp(&self->expected, &self->actual, sizeof(self->actual)), 0);
+	}
+}
+
+TEST_F(test_pvm, write)
+{
+	if (getuid())
+		SKIP(return, "pvm_write requires CAP_SYS_PTRACE");
+
+	if (!self->pid) {
+		const uint8_t baton = 'x';
+		int err;
+
+		err = pvm_write(self->ppid, &self->actual, &self->expected, sizeof(self->expected));
+		EXPECT_EQ(err, 0) TH_LOG("Cannot pvm_write: %s\n", strerror(-err));
+
+		ASSERT_EQ(write(self->fd[1], &baton, sizeof(baton)), sizeof(baton));
+
+		pause();
+		exit(0);
+
+	} else {
+		uint8_t baton;
+
+		EXPECT_EQ(read(self->fd[0], &baton, sizeof(baton)), sizeof(baton));
+		EXPECT_EQ(baton, 'x');
+
+		EXPECT_EQ(memcmp(&self->expected, &self->actual, sizeof(self->actual)), 0);
+	}
+}
+
+TEST_HARNESS_MAIN
-- 
2.25.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ