On Tue, 19 Sep 2023 21:18:04 -0400
Steven Rostedt <rost...@goodmis.org> wrote:

> Hmm, actually looking at this, it's worse than what you stated. This is
> called when a directory is closed. So if you had:
> 
>       open(dir);
> 
>       // look at all the content of this dir to create dentries
> 
>       // another task creates a new entry and looks at it too.
> 
>       close(dir);
> 
> Now we iterate over all the dentries of the dir and dput it.
> 
> I think this will cause the ref counts to get out of sync. I'll have to try
> to create this scenario and see what happens.

And yes it does break :-p

Even without this patch it breaks. That is, this bug exists currently upstream.

I run the attached file (requires libtracefs)

and then run:

  # cd /sys/kernel/tracing
  # echo 99999999 > buffer_size_kb&

Wait a bit.

This will cause the ref counts to go negative.

Then do a: trace-cmd reset

Which will remove the kprobes created by the attached program, and will
crash the kernel :-p

I have an idea on how to fix it. Let my try it out.

-- Steve
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <getopt.h>
#include <errno.h>
#include <unistd.h>
#include <tracefs.h>

static char *argv0;

static char *get_this_name(void)
{
	static char *this_name;
	char *arg;
	char *p;

	if (this_name)
		return this_name;

	arg = argv0;
	p = arg+strlen(arg);

	while (p >= arg && *p != '/')
		p--;
	p++;

	this_name = p;
	return p;
}

static void usage(void)
{
	char *p = get_this_name();

	printf("usage: %s [-c comm] trace.dat\n"
	       "\n"
	       "  Run this after running: trace-cmd record -e sched\n"
	       "\n"
	       "  Do some work and then hit Ctrl^C to stop the recording.\n"
	       "  Run this on the resulting trace.dat file\n"
	       "\n"
	       "-c comm - to look at only a specific process called 'comm'\n"
	       "\n",p);
	exit(-1);
}

static void __vdie(const char *fmt, va_list ap, int err)
{
	int ret = errno;
	char *p = get_this_name();

	if (err && errno)
		perror(p);
	else
		ret = -1;

	fprintf(stderr, "  ");
	vfprintf(stderr, fmt, ap);

	fprintf(stderr, "\n");
	exit(ret);
}

void die(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	__vdie(fmt, ap, 0);
	va_end(ap);
}

void pdie(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	__vdie(fmt, ap, 1);
	va_end(ap);
}

int main (int argc, char **argv)
{
	int dfd;
	int ret;

	ret = tracefs_kprobe_raw(NULL, "kp1", "schedule_timeout", "time=$arg1");
	if (ret < 0)
		pdie("Can't create schedule_timeout kprobe");

	dfd = tracefs_instance_file_open(NULL, "events/kprobes", O_RDONLY);
	if (dfd < 0)
		pdie("Can't open events/kprobes");

	if (!tracefs_file_exists(NULL, "events/kprobes/kp1/enable"))
		pdie("kp1/enable does not exist");

	ret = tracefs_kprobe_raw(NULL, "kp2", "schedule_hrtimeout", "expires=$arg1");
	if (ret < 0)
		pdie("Can't create schedule_hrtimeout kprobe");

	if (!tracefs_file_exists(NULL, "events/kprobes/kp2/enable"))
		pdie("kp2/enable does not exist");

	close(dfd);

//	tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE, true);

	return 0;
}

Reply via email to