Re: kernel BUG at ./include/linux/mm.h:LINE! (3)

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

 



On Wed, 3 Jan 2018 12:26:04 +0300
"Kirill A. Shutemov" <kirill@xxxxxxxxxxxxx> wrote:

> > > -	unsigned long offset, chunk_idx;
> > > +	unsigned long offset, chunk_idx, flags;
> > >  	struct page *pageptr;
> > >  
> > > +	mutex_lock(&rp->fetch_lock);
> > > +	spin_lock_irqsave(&rp->b_lock, flags);
> > >  	offset = vmf->pgoff << PAGE_SHIFT;

> > I think that grabbing the spinlock is not really necessary in
> > this case. The ->b_lock is designed for things that are accessed
> > from interrupts that Host Controller Driver serves -- mostly
> > various pointers. By defintion it's not covering things that
> > are related to re-allocation. Now, the re-allocation itself
> > grabs it, because it resets indexes into the new buffer, but
> > does not appear to apply here, does it now?  
> 
> Please, double check everything. I remember that the mutex wasn't enough
> to stop bug from triggering. But I didn't spend much time understanding
> the code.

Attached is the test that I used to reproduce the problem, and my
patch with just the ->fetch_lock fixes it. That said, your test
suite may be more comprehensive, or you may have a device that
generates enough USB traffic with associated monitoring callbacks.
But I don't see it.

At this point, I'm going to post the patch with a Signed-Off-By.

-- Pete

(this reproduces very quickly)

/*
 * usbmontest: The crash test for usbmon, crashes kernel 4.15
 *
 * Copyright (c) 2007 Red Hat, Inc.
 * Copyright (c) 2016 Mike Frysinger <vapier@xxxxxxxxxx>
 * Copyright (c) 2018 Pete Zaitcev <zaitcev@xxxxxxxxxx>
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <inttypes.h>
#include <stdarg.h>

#define TAG "usbmontest"

#ifdef __GNUC__
#define __unused __attribute__((unused))
#else
#define __unused /**/
#endif

#define MON_IOC_MAGIC 0x92

#define MON_IOCG_STATS _IOR(MON_IOC_MAGIC, 3, struct usbmon_stats)

#define MON_IOCT_RING_SIZE _IO(MON_IOC_MAGIC, 4)

#define MON_IOCQ_RING_SIZE _IO(MON_IOC_MAGIC, 5)

struct usbmon_mfetch_arg {
	unsigned int *offvec;		/* Vector of events fetched */
	unsigned int nfetch;		/* Num. of events to fetch / fetched */
	unsigned int nflush;		/* Number of events to flush */
};

#define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct usbmon_mfetch_arg)

/*
 */
struct params {
	int ifnum;	/* USB bus number */
	char *devname;	/* /dev/usbmonN */

	unsigned int map_size;
};
const unsigned int page_size = 8192;	/* 2x the size on x86, cross-platform */
const unsigned int num_pages = 3;

void Usage(void);

void parse_params(struct params *p, char **argv);
void make_device(const struct params *p);
int find_major(void);

struct params par;

int main(int argc __unused, char **argv)
{
	int fd;
	unsigned char *data_buff;
	unsigned int n;
	int wstat;
	int rc;

	parse_params(&par, argv+1);

	/*
	 * Two reasons to do this:
	 * 1. Reduce weird error messages.
	 * 2. If we create device nodes, we want them owned by root.
	 */
	if (geteuid() != 0) {
		fprintf(stderr, TAG ": Must run as root\n");
		exit(1);
	}

	if ((fd = open(par.devname, O_RDWR)) == -1) {
		if (errno == ENOENT) {
			make_device(&par);
			fd = open(par.devname, O_RDWR);
		}
		if (fd == -1) {
			if (errno == ENODEV && par.ifnum == 0) {
				fprintf(stderr, TAG
				    ": Can't open pseudo-bus zero at %s"
				    " (probably not supported by kernel)\n",
				    par.devname);
			} else {
				fprintf(stderr, TAG ": Can't open %s: %s\n",
				    par.devname, strerror(errno));
			}
			exit(1);
		}
	}

	rc = ioctl(fd, MON_IOCT_RING_SIZE, par.map_size);
	if (rc == -1) {
		fprintf(stderr, TAG ": Cannot set ring size (%d): %s\n",
		    par.map_size, strerror(errno));
		exit(1);
	}

	rc = fork();
	if (rc < 0) {
		fprintf(stderr, TAG ": fork: %s\n", strerror(errno));
		exit(1);
	}
	if (rc != 0) {
		/*
		 * This loop races the mmap and makes it carsh with BUG() in
		 * include/linux/mm.h:831.
		 */
		for (;;) {
			rc = ioctl(fd, MON_IOCT_RING_SIZE, par.map_size);
			if (rc == -1) {
				fprintf(stderr, TAG ": Cannot set ring size (%d): %s\n",
				    par.map_size, strerror(errno));
				exit(1);
			}

			rc = waitpid(0, &wstat, WNOHANG);
			if (rc < 0) {
				fprintf(stderr, TAG ": waitpid: %s\n",
				    strerror(errno));
				exit(1);
			}
			/*
			 * Exit if the child crashed, this way the user does
			 * not need to hunt and kill the runwaway mapping loop.
			 */
			if (rc != 0)
				exit(0);
		}
	} else {
		/*
		 * This loop sets up and tears down the mmap continuously
		 * in order to cause page faults. Merely accessing the mapped
		 * buffer causes the faults one time at the beginning only.
		 */
		for (;;) {
			data_buff = mmap(0, par.map_size, PROT_READ, MAP_SHARED, fd, 0);
			if (data_buff == MAP_FAILED) {
				fprintf(stderr, TAG ": Cannot mmap: %s\n",
				    strerror(errno));
				exit(1);
			}

			/* dear gcc, please don't optimize orz */
			for (n = 0; n < num_pages; n++) {
				unsigned char x;
				x = ((volatile unsigned char *)data_buff)[n * page_size];
			}

			munmap(data_buff, par.map_size);
		}
	}

	// return 0;
}

void parse_params(struct params *p, char **argv)
{
	char *arg;
	long num;

	memset(p, 0, sizeof(struct params));

	while ((arg = *argv++) != NULL) {
		if (arg[0] == '-') {
			if (arg[1] == 0)
				Usage();
			switch (arg[1]) {
			case 'i':
				if (arg[2] != 0)
					Usage();
				if ((arg = *argv++) == NULL)
					Usage();
				if (strncmp(arg, "usb", 3) == 0)
					arg += 3;
				if (!isdigit(arg[0]))
					Usage();
				errno = 0;
				num = strtol(arg, NULL, 10);
				if (errno != 0)
					Usage();
				if (num < 0 || num >= 128) {
					fprintf(stderr, TAG ": Bus number %ld"
					   " is out of bounds\n", num);
					exit(2);
				}
				p->ifnum = num;
				break;
			default:
				Usage();
			}
		} else {
			Usage();
		}
	}

	if (p->devname == NULL) {
		if ((p->devname = malloc(100)) == NULL) {
			fprintf(stderr, TAG ": No core\n");
			exit(1);
		}
		snprintf(p->devname, 100, "/dev/usbmon%d", p->ifnum);
	}

	p->map_size = num_pages * page_size;
}

void make_device(const struct params *p)
{
	int major;
	dev_t dev;

	major = find_major();
	dev = makedev(major, p->ifnum);
	if (mknod(p->devname, S_IFCHR|S_IRUSR|S_IWUSR, dev) != 0) {
		fprintf(stderr, TAG ": Can't make device %s: %s\n",
		    p->devname, strerror(errno));
		exit(1);
	}
}

int find_major(void)
{
	long num;
	FILE *df;
	enum { LEN = 50 };
	char buff[LEN], c, *p;
	char *major, *mname;

	if ((df = fopen("/proc/devices", "r")) == NULL) {
		fprintf(stderr, TAG ": Can't open /proc/devices\n");
		exit(1);
	}
	num = -1;
	while (fgets(buff, LEN, df) != NULL) {
		p = buff;
		major = NULL;
		mname = NULL;
		for (p = buff; (c = *p) != 0; p++) {
			if (major == NULL) {
				if (c != ' ') {
					major = p;
				}
			} else if (mname == NULL) {
				if (!isdigit(c) && c != ' ') {
					mname = p;
				}
			} else {
				if (c == '\n') {
					*p = 0;
					break;
				}
			}
		}
		if (major != NULL && mname != NULL) {
			if (strcmp(mname, "usbmon") == 0) {
				errno = 0;
				num = strtol(major, NULL, 10);
				if (errno != 0) {
					fprintf(stderr, TAG ": Syntax error "
					    "in /proc/devices\n");
					exit(1);
				}
				break;
			}
		}
	}
	fclose(df);

	if (num == -1) {
		fprintf(stderr, TAG ": Can't find usbmon in /proc/devices\n");
		exit(1);
	}

	if (num <= 0 || num > INT_MAX) {
		fprintf(stderr, TAG ": Weird major %ld in /proc/devices\n",
		    num);
		exit(1);
	}

	return (int) num;
}

void Usage(void)
{
	fprintf(stderr, "Usage: "
	    "usbmontest [-i usbN]\n");
	exit(2);
}
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux