Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle NULL in releasedir #575

Closed
wants to merge 1 commit into from
Closed

Handle NULL in releasedir #575

wants to merge 1 commit into from

Conversation

deleriux
Copy link

Whilst libfuse seems to indicate this should not occur when nullpath_ok is switched off, this does not always seem to be the case. A straightforward workaround is to handle the NULL case of path in lxcfs_releasedir such that no crash can occur in snaps.

Example crash we caught a few days ago:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007fa33020bbee in ?? () at ../sysdeps/x86_64/multiarch/strcpy-evex.S:933 from /lib/x86_64-linux-gnu/libc.so.6
933     ../sysdeps/x86_64/multiarch/strcpy-evex.S: No such file or directory.
lxc/lxd#1  0x0000564ac32ea508 in lxcfs_releasedir (path=0x0, fi=0x7fa30fffec80) at ../src/src/lxcfs.c:774
lxc/lxd#2  0x00007fa3302c62b7 in ?? () from /snap/lxd/current/lib/x86_64-linux-gnu/libfuse3.so.3
lxc/lxd#3  0x00007fa3302cd683 in ?? () from /snap/lxd/current/lib/x86_64-linux-gnu/libfuse3.so.3
lxc/lxd#4  0x00007fa3302cfc59 in ?? () from /snap/lxd/current/lib/x86_64-linux-gnu/libfuse3.so.3
lxc/lxd#5  0x00007fa3302cb407 in ?? () from /snap/lxd/current/lib/x86_64-linux-gnu/libfuse3.so.3
lxc/lxd#6  0x00007fa33029d609 in ?? ()
lxc/lxd#7  0x0000000000000000 in ?? ()

Signed-off-by: Matthew Ife 75210448+deleriux@users.noreply.github.com

Whilst libfuse seems to indicate this should not occur when `nullpath_ok` is switched off, this does not always seem to be the case.
A straightforward workaround is to handle the NULL case of `path` in `lxcfs_releasedir` such that no crash can occur in snaps.

Signed-off-by: Matthew Ife <75210448+deleriux@users.noreply.github.com>
@@ -771,6 +771,13 @@ static int lxcfs_releasedir(const char *path, struct fuse_file_info *fi)
{
int ret;

if (path == NULL) {
up_users();
ret = do_cg_releasedir(path, fi);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we are calling do_cg_releasedir here? Why not do_sys_releasedir? Without path we can't determine what to call here. I think we have to print out some error to the LXCFS log and return -EINVAL.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with libfuse code but fi seems to always be initialized.
Both do_cg_releasedir and do_sys_releasedir just call (via dlopen()) do_release_file_info.

I am (potentially unsafely) assuming that whatever user data exists in fi will always be the same struct being released here.

The problem with not doing anything at all is may end up just slowly leaking memory.

Passing -EINVAL will result in unexpected behaviour in userspace applications. The POSIX norm for files unlinked/rmdir'd from VFS with existing open handles that are subsequently closed by the application is to return 0 to the application and not -EINVAL.

Spitting a log output might keep the process running but its pretty unlikely with the information kept in the userspace structure will reveal what the original path was that was rmdir'd. You could extend the userspace structure to add tracking though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am (potentially unsafely) assuming that whatever user data exists in fi will always be the same struct being released here.

Yep, that's what I'm trying to say. Right now that makes no difference to call do_cg_releasedir or do_sys_releasedir but in the future, this may be changed. We can't rely on that.

The problem with not doing anything at all is may end up just slowly leaking memory.

Yep, but a memory leak combined with the warning message in logs is better than potentially incorrect memory free ;-)

Passing -EINVAL will result in unexpected behaviour in userspace applications.

I've checked how this retvalue is handled in libfuse... and... it's just ignored. :-)
https://github.com/libfuse/libfuse/blob/33736958b61d90d9db9b03441103035316f09abc/lib/fuse.c#L1982
Take a look on fuse_fs_releasedir() calls.

Spitting a log output might keep the process running but its pretty unlikely with the information kept in the userspace structure will reveal what the original path was that was rmdir'd. You could extend the userspace structure to add tracking though.

Yep, but this warning will be a sign of a problem. And yes, extending the log message with some additional info, in this case, is a good idea.

Copy link
Author

@deleriux deleriux Jan 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've checked how this retvalue is handled in libfuse... and... it's just ignored. :-)

Thats great! I agree in that case leak/EINVAL is nicer.

If I can get the time approved I shall try to work out what the condition is that results in this behaviour as it might reveal something more fundamental going on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I can get the time approved I shall try to work out what the condition is that results in this behaviour as it might reveal something more fundamental going on.

I've checked libfuse code and I can only see 3 places where NULL path may be passed to fs callbacks:

  1. releasedir callback https://github.com/libfuse/libfuse/blob/33736958b61d90d9db9b03441103035316f09abc/lib/fuse.c#L3731
  2. release callback https://github.com/libfuse/libfuse/blob/33736958b61d90d9db9b03441103035316f09abc/lib/fuse.c#L4075
  3. flush callback https://github.com/libfuse/libfuse/blob/33736958b61d90d9db9b03441103035316f09abc/lib/fuse.c#L4097

I think we have to cover all of them from the LXCFS side. Are you ready to update this PR with covering these cases?
As we discussed before if we are getting a NULL path we can just throw an error and print a verbose log message with struct fuse_file_info fields, plus struct file_info too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, we can use (struct file_info)->type to detect what to call do_cg_releasedir or do_sys_releasedir... Then we don't need to know the path at all in the release* callbacks!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With a bit of hacking, I managed to resolve the userspace component of the coredumps I have in the backtraces.
These are 3 different crash examples.

(gdb) print *(struct file_info *)fi->fh                                                                 
$2 = {controller = 0x7f0408105af0 "systemd", 
  cgroup = 0x7f04081075e0 "lxc.payload.infra-app-test-31/system.slice/snap.mount", file = 0x0 <foo>, 
  type = 0, buf = 0x0 <foo>, buflen = 0, size = 779116910, cached = 1853189997}

And

(gdb) print *(struct file_info *)fi->fh
$1 = {controller = 0x7f02f4106a50 "devices", cgroup = 0x7f02f4118740 "lxc.payload.infra-app-test-33/system.slice/sssd-pac.socket", file = 0x0 <foo>, type = 0, buf = 0x0 <foo>, buflen = 0, 
  size = 778989426, cached = 1987208563}

And

(gdb) print *(struct file_info *)fi->fh
$4 = {controller = 0x7f9f90000ca0 "systemd", cgroup = 0x7f9f9000d470 "lxc.payload.infra-app-test-31/system.slice/system-getty.slice", file = 0x0 <foo>, type = 0, buf = 0x0 <foo>, buflen = 0, 
  size = 779318626, cached = 1987208563}

We use a lot of cgroup v1 namespaces on these hosts, however (which I have no backtraces for unfortunately) we have another that exhibits the same behaviour that is using cgroupv2 only).

In any case they are always cgroup files that crash in my case.

We pretty much can hammer our hosts (having 150+ active containers at at time).

I will update the PR with code tomorrow that handles all 3 cases cleanly.

@@ -1032,6 +1039,7 @@ static void *lxcfs_init(struct fuse_conn_info *conn)

#if HAVE_FUSE3
cfg->direct_io = 1;
cfg->nullpath_ok = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've asked that already https://github.com/lxc/lxd-pkg-snap/issues/110#issuecomment-1377472107
Did you catch the case when this option has a non-zero value? (I mean by printf-ing it's value or using gdb memory inspection).

@mihalicyn
Copy link
Member

Just to make a link #573

@mihalicyn
Copy link
Member

Let's close this PR cause we have #577

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants