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]
Message-ID: <1338597331.2770.14.camel@bwh-desktop.uk.solarflarecom.com>
Date:	Sat, 2 Jun 2012 01:35:31 +0100
From:	Ben Hutchings <bhutchings@...arflare.com>
To:	<netdev@...r.kernel.org>
CC:	Michał Mirosław <mirq-linux@...e.qmqm.pl>,
	Mahesh Bandewar <maheshb@...gle.com>
Subject: [PATCH ethtool 1/7] Run tests in-process

Wrap main(), exit(), and resource management so that ethtool commands
can be tested without starting a new process and without leaking.
This will allow deeper teesting that covers ioctl requests and
responses.

Signed-off-by: Ben Hutchings <bhutchings@...arflare.com>
---
 .gitignore     |    1 -
 Makefile.am    |    6 +-
 ethtool.c      |    9 +-
 internal.h     |   42 ++++++++
 test-cmdline.c |    7 ++
 test-common.c  |  311 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 6 files changed, 332 insertions(+), 44 deletions(-)

diff --git a/.gitignore b/.gitignore
index e10c4ef..62bf520 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,6 @@ ethtool.spec
 ethtool.8
 ethtool
 test-cmdline
-test-one-cmdline
 stamp-h1
 config.*
 aclocal.m4
diff --git a/Makefile.am b/Makefile.am
index 789cd9a..d1aec43 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,11 +12,9 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \
 		  rxclass.c sfpid.c
 
 TESTS = test-cmdline
-check_PROGRAMS = test-cmdline test-one-cmdline
-test_cmdline_SOURCES = test-cmdline.c test-common.c
+check_PROGRAMS = test-cmdline
+test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES) 
 test_cmdline_CFLAGS = -DTEST_ETHTOOL
-test_one_cmdline_SOURCES = $(ethtool_SOURCES)
-test_one_cmdline_CFLAGS = -DTEST_ETHTOOL
 
 dist-hook:
 	cp $(top_srcdir)/ethtool.spec $(distdir)
diff --git a/ethtool.c b/ethtool.c
index f18f611..65bdd38 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -3261,16 +3261,13 @@ static int do_getmodule(struct cmd_context *ctx)
 	return 0;
 }
 
+#ifndef TEST_ETHTOOL
 int send_ioctl(struct cmd_context *ctx, void *cmd)
 {
-#ifndef TEST_ETHTOOL
 	ctx->ifr.ifr_data = cmd;
 	return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);
-#else
-	/* If we get this far then parsing succeeded */
-	exit(0);
-#endif
 }
+#endif
 
 static int show_usage(struct cmd_context *ctx);
 
@@ -3451,7 +3448,7 @@ static int show_usage(struct cmd_context *ctx)
 	return 0;
 }
 
-int main(int argc, char **argp, char **envp)
+int main(int argc, char **argp)
 {
 	int (*func)(struct cmd_context *);
 	int want_device;
diff --git a/internal.h b/internal.h
index 55f0d8a..10abe25 100644
--- a/internal.h
+++ b/internal.h
@@ -6,7 +6,11 @@
 #ifdef HAVE_CONFIG_H
 #include "ethtool-config.h"
 #endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/types.h>
+#include <unistd.h>
 #include <endian.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
@@ -98,6 +102,44 @@ struct cmd_context {
 
 #ifdef TEST_ETHTOOL
 int test_cmdline(const char *args);
+
+#ifndef TEST_NO_WRAPPERS
+int test_main(int argc, char **argp);
+#define main(...) test_main(__VA_ARGS__)
+void test_exit(int rc) __attribute__((noreturn));
+#undef exit
+#define exit(rc) test_exit(rc)
+void *test_malloc(size_t size);
+#undef malloc
+#define malloc(size) test_malloc(size)
+void *test_calloc(size_t nmemb, size_t size);
+#undef calloc
+#define calloc(nmemb, size) test_calloc(nmemb, size)
+char *test_strdup(const char *s);
+#undef strdup
+#define strdup(s) test_strdup(s)
+void *test_free(void *ptr);
+#undef free
+#define free(ptr) test_free(ptr)
+void *test_realloc(void *ptr, size_t size);
+#undef realloc
+#define realloc(ptr, size) test_realloc(ptr, size)
+int test_open(const char *pathname, int flag, ...);
+#undef open
+#define open(...) test_open(__VA_ARGS__)
+int test_socket(int domain, int type, int protocol);
+#undef socket
+#define socket(...) test_socket(__VA_ARGS__)
+int test_close(int fd);
+#undef close
+#define close(fd) test_close(fd)
+FILE *test_fopen(const char *path, const char *mode);
+#undef fopen
+#define fopen(path, mode) test_fopen(path, mode)
+int test_fclose(FILE *fh);
+#undef fclose
+#define fclose(fh) test_fclose(fh)
+#endif
 #endif
 
 int send_ioctl(struct cmd_context *ctx, void *cmd);
diff --git a/test-cmdline.c b/test-cmdline.c
index c30b0e6..c62bb97 100644
--- a/test-cmdline.c
+++ b/test-cmdline.c
@@ -9,6 +9,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#define TEST_NO_WRAPPERS
 #include "internal.h"
 
 static struct test_case {
@@ -232,6 +233,12 @@ static struct test_case {
 	{ 1, "-0" },
 };
 
+int send_ioctl(struct cmd_context *ctx, void *cmd)
+{
+	/* If we get this far then parsing succeeded */
+	test_exit(0);
+}
+
 int main(void)
 {
 	struct test_case *tc;
diff --git a/test-common.c b/test-common.c
index 4ea84c8..69853aa 100644
--- a/test-common.c
+++ b/test-common.c
@@ -2,27 +2,268 @@
  * Common test functions for ethtool
  * Copyright 2011 Solarflare Communications Inc.
  *
+ * Partly derived from kernel <linux/list.h>.
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published
  * by the Free Software Foundation, incorporated herein by reference.
  */
 
+#include <assert.h>
+#include <setjmp.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/fcntl.h>
-#include <sys/wait.h>
 #include <unistd.h>
+#define TEST_NO_WRAPPERS
 #include "internal.h"
 
+/* List utilities */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+static void init_list_head(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+static void list_add(struct list_head *new, struct list_head *head)
+{
+	head->next->prev = new;
+	new->next = head->next;
+	new->prev = head;
+	head->next = new;
+}
+
+static void list_del(struct list_head *entry)
+{
+	entry->next->prev = entry->prev;
+	entry->prev->next = entry->next;
+	entry->next = NULL;
+	entry->prev = NULL;
+}
+
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/* Free memory at end of test */
+
+static struct list_head malloc_list = LIST_HEAD_INIT(malloc_list);
+
+void *test_malloc(size_t size)
+{
+	struct list_head *block = malloc(sizeof(*block) + size);
+
+	if (!block)
+		return NULL;
+	list_add(block, &malloc_list);
+	return block + 1;
+}
+
+void *test_calloc(size_t nmemb, size_t size)
+{
+	void *ptr = test_malloc(nmemb * size);
+
+	if (ptr)
+		memset(ptr, 0, nmemb * size);
+	return ptr;
+}
+
+char *test_strdup(const char *s)
+{
+	size_t size = strlen(s) + 1;
+	char *dup = test_malloc(size);
+
+	if (dup)
+		memcpy(dup, s, size);
+	return dup;
+}
+
+void test_free(void *ptr)
+{
+	struct list_head *block;
+
+	if (!ptr)
+		return;
+	block = (struct list_head *)ptr - 1;
+	list_del(block);
+	free(block);
+}
+
+void *test_realloc(void *ptr, size_t size)
+{
+	struct list_head *block;
+
+	if (ptr) {
+		block = (struct list_head *)ptr - 1;
+		list_del(block);
+	}
+	block = realloc(block, sizeof(*block) + size);
+	if (!block)
+		return NULL;
+	list_add(block, &malloc_list);
+	return block + 1;
+}
+
+static void test_free_all(void)
+{
+	struct list_head *block, *next;
+
+	list_for_each_safe(block, next, &malloc_list)
+		free(block);
+	init_list_head(&malloc_list);
+}
+
+/* Close files at end of test */
+
+struct file_node {
+	struct list_head link;
+	FILE *fh;
+	int fd;
+};
+
+static struct list_head file_list = LIST_HEAD_INIT(file_list);
+
+int test_open(const char *pathname, int flag, ...)
+{
+	struct file_node *node;
+	mode_t mode;
+
+	if (flag & O_CREAT) {
+		va_list ap;
+		va_start(ap, flag);
+		mode = va_arg(ap, mode_t);
+		va_end(ap);
+	} else {
+		mode = 0;
+	}
+
+	node = malloc(sizeof(*node));
+	if (!node)
+		return -1;
+
+	node->fd = open(pathname, flag, mode);
+	if (node->fd < 0) {
+		free(node);
+		return -1;
+	}
+
+	node->fh = NULL;
+	list_add(&node->link, &file_list);
+	return node->fd;
+}
+
+int test_socket(int domain, int type, int protocol)
+{
+	struct file_node *node;
+
+	node = malloc(sizeof(*node));
+	if (!node)
+		return -1;
+
+	node->fd = socket(domain, type, protocol);
+	if (node->fd < 0) {
+		free(node);
+		return -1;
+	}
+
+	node->fh = NULL;
+	list_add(&node->link, &file_list);
+	return node->fd;
+}
+
+int test_close(int fd)
+{
+	struct list_head *head, *next;
+
+	if (fd >= 0) {
+		list_for_each_safe(head, next, &file_list) {
+			if (((struct file_node *)head)->fd == fd) {
+				list_del(head);
+				free(head);
+				break;
+			}
+		}
+	}
+
+	return close(fd);
+}
+
+FILE *test_fopen(const char *path, const char *mode)
+{
+	struct file_node *node;
+
+	node = malloc(sizeof(*node));
+	if (!node)
+		return NULL;
+
+	node->fh = fopen(path, mode);
+	if (!node->fh) {
+		free(node);
+		return NULL;
+	}
+
+	node->fd = -1;
+	list_add(&node->link, &file_list);
+	return node->fh;	
+}
+
+int test_fclose(FILE *fh)
+{
+	struct list_head *head, *next;
+
+	assert(fh);
+
+	list_for_each_safe(head, next, &file_list) {
+		if (((struct file_node *)head)->fh == fh) {
+			list_del(head);
+			free(head);
+			break;
+		}
+	}
+
+	return fclose(fh);
+}
+
+static void test_close_all(void)
+{
+	struct list_head *head, *next;
+	struct file_node *node;
+
+	list_for_each_safe(head, next, &file_list) {
+		node = (struct file_node *)head;
+		if (node->fh)
+			fclose(node->fh);
+		else
+			close(node->fd);
+		free(node);
+	}
+	init_list_head(&file_list);
+}
+
+/* Wrap test main function */
+
+static jmp_buf test_return;
+
+void test_exit(int rc)
+{
+	longjmp(test_return, rc + 1);
+}
+
 int test_cmdline(const char *args)
 {
 	int argc, i;
 	char **argv;
 	const char *arg;
 	size_t len;
-	pid_t pid;
-	int dev_null;
-	int status;
+	int dev_null = -1, old_stdout = -1, old_stderr = -1;
 	int rc;
 
 	/* Convert line to argv */
@@ -37,12 +278,12 @@ int test_cmdline(const char *args)
 			break;
 		arg += len + 1;
 	}
-	argv = calloc(argc + 1, sizeof(argv[0]));
-	argv[0] = strdup("ethtool");
+	argv = test_calloc(argc + 1, sizeof(argv[0]));
+	argv[0] = test_strdup("ethtool");
 	arg = args;
 	for (i = 1; i < argc; i++) {
 		len = strcspn(arg, " ");
-		argv[i] = malloc(len + 1);
+		argv[i] = test_malloc(len + 1);
 		memcpy(argv[i], arg, len);
 		argv[i][len] = 0;
 		arg += len + 1;
@@ -56,37 +297,41 @@ int test_cmdline(const char *args)
 	}
 
 	fflush(NULL);
-	pid = fork();
-
-	/* Child */
-	if (pid == 0) {
-		dup2(dev_null, STDIN_FILENO);
-		if (!getenv("ETHTOOL_TEST_VERBOSE")) {
-			dup2(dev_null, STDOUT_FILENO);
-			dup2(dev_null, STDERR_FILENO);
+	dup2(dev_null, STDIN_FILENO);
+	if (!getenv("TEST_TEST_VERBOSE")) {
+		old_stdout = dup(STDOUT_FILENO);
+		if (old_stdout < 0) {
+			perror("dup stdout");
+			rc = -1;
+			goto out;
 		}
-		execv("./test-one-cmdline", argv);
-		_exit(126);
+		dup2(dev_null, STDOUT_FILENO);
+		old_stderr = dup(STDERR_FILENO);
+		if (old_stderr < 0) {
+			perror("dup stderr");
+			rc = -1;
+			goto out;
+		}
+		dup2(dev_null, STDERR_FILENO);
 	}
 
-	/* Parent */
-	if (pid < 0) {
-		perror("fork");
-		close(dev_null);
-		rc = -1;
-		goto out;
+	rc = setjmp(test_return);
+	rc = rc ? rc - 1 : test_main(argc, argv);
+
+out:
+	fflush(NULL);
+	if (old_stderr >= 0) {
+		dup2(old_stderr, STDERR_FILENO);
+		close(old_stderr);
 	}
-	close(dev_null);
-	if (waitpid(pid, &status, 0) < 0) {
-		perror("waitpid");
-		rc = -1;
-		goto out;
+	if (old_stdout >= 0) {
+		dup2(old_stdout, STDOUT_FILENO);
+		close(old_stdout);
 	}
-	rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+	if (dev_null >= 0)
+		close(dev_null);
 
-out:
-	for (i = 0; i < argc; i++)
-		free(argv[i]);
-	free(argv);
+	test_free_all();
+	test_close_all();
 	return rc;
 }
-- 
1.7.7.6



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ