Re: fanotify coredump issue

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Lino,

On 01/04/2011 01:28 PM, Lino Sanfilippo wrote:
I tested fanotify in 2.6.37-rc7 and faced the following issue: when a
fanotify server process (started with open_perm set) segfaults the
kernel tries to open core dump file and here it is forced to wait a
permission result because fanotify server receives notifications on
file operations initiated by itself. Since fanotify server has crashed
the permission will never be granted. So the whole system hangs.

Hmm I could not reproduce this with the latest state (ef9bf3b7144bee6ce) of
branch 'origin/for-next' from git.infradead.org/users/eparis/notify.git

What i did was:
- register with fanotify
- set mark for OPEN_PERM event
- read an event
- cause a segfault before response is returned to fanotify

The process terminates and the core file is created as expected.
Could you provide some code to trigger this?


The issue is still reproduced on ubuntu 11.04:

$ uname -a
Linux user-virtual-machine 2.6.38.2 #16 SMP Wed Jun 8 14:40:03 MSD 2011 i686 i686 i386 GNU/Linux
$ g++ fanotify.c -o fanotify -lpthread
$ sudo su
$ ulimit -c unlimited
$ ./fanotify -o open_perm -C -m /

In other console:
$ ls

Then machine is dead.

--
Best regards,

Vasily Novikov | Software developer | Kaspersky Lab

Direct: +7 495 123 45 67 x2344 | Mobile: +7 964 786 44 82 | vasily.novikov@xxxxxxxxxxxxx 10/1, 1st Volokolamsky Proezd, Moscow, 123060, Russia | www.kaspersky.com, www.securelist.com
#define _ATFILE_SOURCE
#include <errno.h>
#include <inttypes.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

#include <linux/fanotify.h>
#include <sys/fanotify.h>
//#include <linux/fsnotify_backend.h>

//#include "fanotify-syscalllib.h"

#define FANOTIFY_ARGUMENTS "cfhmn"

uint64_t fan_mask = FAN_OPEN | FAN_CLOSE | FAN_ACCESS | FAN_MODIFY;
bool opt_fast;
int fan_fd;

int mark_object(int fan_fd, const char *path, int fd, uint64_t mask, unsigned int flags)
{
	return fanotify_mark(fan_fd, flags, mask, fd, path);
}

int set_ignored_mask(int fan_fd, int fd, uint64_t mask)
{
	unsigned int flags = (FAN_MARK_ADD | FAN_MARK_IGNORED_MASK);

	return mark_object(fan_fd, NULL, fd, mask, flags);
}

int handle_perm(int fan_fd, struct fanotify_event_metadata *metadata, unsigned int response)
{
	struct fanotify_response response_struct;
	int ret;

	response_struct.fd = metadata->fd;
	response_struct.response = response;

	ret = write(fan_fd, &response_struct, sizeof(response_struct));
	if (ret < 0)
		return ret;

	return 0;
}

void synopsis(const char *progname, int status)
{
	FILE *file = status ? stderr : stdout;

	fprintf(file, "USAGE: %s [-" FANOTIFY_ARGUMENTS "] "
		"[-o {open,close,access,modify,open_perm,access_perm}] "
		"file ...\n"
		"-c: learn about events on children of a directory (not decendants)\n"
		"-f: set premptive ignores (go faster)\n"
		"-h: this help screen\n"
		"-m: place mark on the whole mount point, not just the inode\n"
		"-n: do not ignore repeated permission checks\n"
		"-s N: sleep N seconds before replying to perm events\n",
		"-C: crash\n",
		progname);
	exit(status);
}

#define FAIL() do { fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); goto fail; } while(0);

int g_N = 30;

void* process_request(void *p)
{
	struct fanotify_event_metadata *metadata = (struct fanotify_event_metadata*)p;
	bool opt_ignore_perm = 1;
	unsigned int response = FAN_ALLOW;
	int opt_sleep = 0;

	pthread_detach(pthread_self());
	
	if (metadata->fd >= 0 &&
			opt_fast &&
			set_ignored_mask(fan_fd, metadata->fd,
				FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS))
		FAIL();

	if (metadata->fd >= 0) {
		char path[PATH_MAX];
		int path_len;

		sprintf(path, "/proc/self/fd/%d", metadata->fd);
		path_len = readlink(path, path, sizeof(path)-1);
		opt_ignore_perm =  (path[path_len-1] != 'i');
		if(!opt_ignore_perm) printf(" nocache");
		if(path[path_len-1] == 'f') {
			printf(" flush");
//			open("/tmp/123.txt", O_RDONLY);
			if(mark_object(fan_fd, "/tmp", AT_FDCWD, fan_mask, FAN_MARK_FLUSH) != 0) FAIL();
			//	if(mark_object(fan_fd, "/tmp", AT_FDCWD, fan_mask, FAN_MARK_ADD) != 0) FAIL();
		}
		if(path[path_len-1] == 'd') {
			printf(" deny");
			response = FAN_DENY;
		}
		if(path[path_len-1] == 's') {
			printf(" sleep");
			opt_sleep = 1;
		}
		if (path_len < 0)
			FAIL();
		path[path_len] = '\0';
		printf("%s:", path);
	} else
		printf("?:");

	printf(" pid=%ld", (long)metadata->pid);

	if (metadata->mask & FAN_ACCESS)
		printf(" access");
	if (metadata->mask & FAN_OPEN)
		printf(" open");
	if (metadata->mask & FAN_MODIFY)
		printf(" modify");
	if (metadata->mask & FAN_CLOSE) {
		if (metadata->mask & FAN_CLOSE_WRITE)
			printf(" close(writable)");
		else
			printf(" close");
	}
	if (metadata->mask & FAN_OPEN_PERM)
		printf(" open_perm");
	if (metadata->mask & FAN_ACCESS_PERM)
		printf(" access_perm");
	printf(" md->mask:%x PE:%x", (int)metadata->mask, (int)(FAN_ALL_PERM_EVENTS));
	if (metadata->mask & (FAN_ALL_PERM_EVENTS)) {
		if (opt_sleep) {
			sleep(opt_sleep);
		}

		if (metadata->fd >= 0 &&
				opt_ignore_perm &&
				set_ignored_mask(fan_fd, metadata->fd,
					metadata->mask))
			FAIL();
		if (handle_perm(fan_fd, metadata, response))
			FAIL();
	}

	printf("\n");
	fflush(stdout);

	if (metadata->fd >= 0 && close(metadata->fd) != 0)
		FAIL();

	free(p);
	return NULL;

fail:
	fprintf(stderr, "%s\n", strerror(errno));
	exit(1);
	return NULL;
}


void deffered_processing(struct fanotify_event_metadata *a_md)
{
	struct fanotify_event_metadata *md = (struct fanotify_event_metadata *)malloc(sizeof(struct fanotify_event_metadata));
	pthread_t tid;

	if(NULL == md) {
		printf("can't allocate memory for mrequest\n");
		process_request(md);
		return;
	}

	*md = *a_md;

	if(0 != pthread_create(&tid, NULL, process_request, md)) {
		printf("can't create thread\n");
		process_request(md);
	}
}

int main(int argc, char *argv[])
{
	int opt;
	unsigned int mark_flags = FAN_MARK_ADD;
	bool opt_child, opt_on_mount, opt_ignore_perm, crash;
	ssize_t len;
	char buf[4096];
	fd_set rfds;


	opt_child = opt_on_mount = opt_fast = crash = false;
	opt_ignore_perm = true;
//	opt_sleep = 0;

	while ((opt = getopt(argc, argv, "o:s:C"FANOTIFY_ARGUMENTS)) != -1) {
		switch(opt) {
			case 'o': {
				char *str, *tok;

				fan_mask = 0;
				str = optarg;
				while ((tok = strtok(str, ",")) != NULL) {
					str = NULL;
					if (strcmp(tok, "open") == 0)
						fan_mask |= FAN_OPEN;
					else if (strcmp(tok, "close") == 0)
						fan_mask |= FAN_CLOSE;
					else if (strcmp(tok, "access") == 0)
						fan_mask |= FAN_ACCESS;
					else if (strcmp(tok, "modify") == 0)
						fan_mask |= FAN_MODIFY;
					else if (strcmp(tok, "open_perm") == 0)
						fan_mask |= FAN_OPEN_PERM;
					else if (strcmp(tok, "access_perm") == 0)
						fan_mask |= FAN_ACCESS_PERM;
					else
						synopsis(argv[0], 1);
				}
				break;
			}
			case 'c':
				opt_child = true;
				break;
			case 'f':
				opt_fast = true;
				opt_ignore_perm = false;
				break;
			case 'm':
				opt_on_mount = true;
				break;
			case 'n':
				opt_fast = false;
				opt_ignore_perm = false;
				break;
//			case 's':
//				opt_sleep = atoi(optarg);
//				break;
			case 'C':
				crash = true;
				break;
			case 'h':
				synopsis(argv[0], 0);
			default:  /* '?' */
				synopsis(argv[0], 1);
		}
	}
	if (optind == argc)
		synopsis(argv[0], 1);

	if (opt_child)
		fan_mask |= FAN_EVENT_ON_CHILD;

	if (opt_on_mount)
		mark_flags |= FAN_MARK_MOUNT;

	fan_fd = fanotify_init(FAN_CLASS_CONTENT | FAN_UNLIMITED_MARKS, O_RDONLY);
//	fan_fd = fanotify_init(0, O_RDWR | O_LARGEFILE);
	if (fan_fd < 0)
		FAIL();

	for (; optind < argc; optind++)
		if (mark_object(fan_fd, argv[optind], AT_FDCWD, fan_mask, mark_flags) != 0)
			FAIL();

	FD_ZERO(&rfds);
	FD_SET(fan_fd, &rfds);

	if (select(fan_fd+1, &rfds, NULL, NULL, NULL) < 0)
		FAIL();

	while ((len = read(fan_fd, buf, sizeof(buf))) > 0) {
		struct fanotify_event_metadata *metadata;

		metadata = reinterpret_cast<fanotify_event_metadata*>(buf);
		while(FAN_EVENT_OK(metadata, len)) {
			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
				fprintf(stderr, "%s: fanotify version mismatch"
						" (%d != %d)\n",
					argv[0], metadata->vers,
					FANOTIFY_METADATA_VERSION);
				return 1;
			}

			*(int*)10 = 10;

			deffered_processing(metadata);

			metadata = FAN_EVENT_NEXT(metadata, len);
		}
		if (select(fan_fd+1, &rfds, NULL, NULL, NULL) < 0)
			FAIL();
	}
	if (len < 0)
		FAIL();
	return 0;

fail:
	fprintf(stderr, "%s\n", strerror(errno));
	return 1;
}

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux