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

lxcfs: handle NULL path in lxcfs_releasedir/lxcfs_release #577

Merged
merged 1 commit into from
Jan 12, 2023

Conversation

deleriux
Copy link

@deleriux deleriux commented Jan 11, 2023

This fixes SEGV in lxcfs_release/releasedir when path=NULL.
Avoid the use of paths doing the release and prefer to inspect the type file_info struct.
Print an error in the case no valid type can be detected.

@deleriux
Copy link
Author

This is the method I've approached this problem with we discussed yesterday.
I fleshed out the file type checks with some simple defines, then created a inline function to inspect the file type internally.

The release/releasedir code now primarily uses this mechanism to release files back to memory. It spits an error in the event it doesn't know what to do.

Flush is a noop so no work was required.

Might be a nice-to-have to extend the same checks into other callbacks as its a little quicker than a strcmp(), but that isn't the concern of this fix.

This still needs testing properly somewhere, I'll roll it out onto my own test systems tomorrow and check its not blowing up massively or leaking memory somehow.

src/lxcfs.c Outdated
@@ -1032,6 +1059,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.

it makes no sense to add this here, as we discussed before, cfg is always initialized by zeroes. If you want to add this just to make the code more explicit, then we need to explicitly initialize all other options from cfg structure, IMHO.

Copy link
Author

@deleriux deleriux Jan 12, 2023

Choose a reason for hiding this comment

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

Dropped this change, agree its unnecessary and implies a cause where none exists.

src/lxcfs.c Outdated
up_users();
ret = do_sys_release(path, fi);
down_users();
return ret;
return ret;
Copy link
Member

Choose a reason for hiding this comment

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

extra spaces after ;

Copy link
Author

Choose a reason for hiding this comment

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

Fixed

@mihalicyn
Copy link
Member

mihalicyn commented Jan 11, 2023

This commit cb5f18a looks strange because later you are removing files from it 27f2cfd

Hint: you can use git rebase -i HEAD~N_commits to edit particular commits, and also you can use git add -i (interactive staging) simplify splitting changes between commits. Then git push -f for PR update.

@deleriux
Copy link
Author

deleriux commented Jan 12, 2023

Thanks. Whilst I'm fairly proficient in C my git-etiquette is not unfortunately that great! I'll try to clean up my commits and remove the bits that are unnecessary.

As you can tell I took an indirect approach at first (to preserve the dlopen() consistency) but in the end I dont think it is necessary, extends the API without offering much to the end user and the extra runtime linker steps make it a slower constant approach than the strcmp() method.

I felt going to the effort of doing type-checks should at least potentially result in a slightly faster overall runtime experience.

src/bindings.h Outdated
@@ -31,6 +31,7 @@
#define LXCFS_NUMSTRLEN64 21

enum lxcfs_virt_t {
LXC_TYPE_START,
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure that it's safe to change the start value of this enum. Cause we support live lxc library reload after this change LXC_TYPE_CGDIR becomes 1 instead of 0. These values getting placed in the memory and kept between library reloads. So, let's not change this enum declaration at all. You can just place a comment inside enum definition which attracts developer attention to the macros LXCFS_TYPE_CGROUP, LXCFS_TYPE_PROC, LXCFS_TYPE_SYS.

Copy link
Author

Choose a reason for hiding this comment

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

I agree here, totally didn't think of the case of reloading.

Generally we restart the service and rebind the mounts in the containers - there is a neat way to do this without restarting the containers, I'm still developing the software but am happy to share it later down the line.

Copy link
Member

Choose a reason for hiding this comment

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

I agree here, totally didn't think of the case of reloading.

Generally we restart the service and rebind the mounts in the containers - there is a neat way to do this without restarting the containers, I'm still developing the software but am happy to share it later down the line.

great! I'm working on a way to recover the lxcfs from crashes another way. With preserving fuse connection between userspace and kernel. It's also not ready yet :)

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure that it's safe to change the start value of this enum. Cause we support live lxc library reload after this change LXC_TYPE_CGDIR becomes 1 instead of 0. These values getting placed in the memory and kept between library reloads. So, let's not change this enum declaration at all.

I think we should explictly number it though. That's what I do nowadays for all enums I add becuase it eliminates a whole bunch of subtle bugs if you force the numbering.

src/bindings.h Outdated
#define LXCFS_TYPE_CGROUP(type) (type >= LXC_TYPE_CGDIR && type <= LXC_TYPE_CGFILE)
#define LXCFS_TYPE_PROC(type) (type >= LXC_TYPE_PROC_MEMINFO && type <= LXC_TYPE_PROC_SLABINFO)
#define LXCFS_TYPE_SYS(type) (type >= LXC_TYPE_SYS && type <= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE)
#define LXCFS_TYPE_OK(type) (type >= LXC_TYPE_START && type <= LXC_TYPE_END)
Copy link
Member

@mihalicyn mihalicyn Jan 12, 2023

Choose a reason for hiding this comment

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

#define LXCFS_TYPE_OK(type) (type > LXC_TYPE_START && type < LXC_TYPE_END), correct?

But anyway, I think we don't need to have this separate values LXC_TYPE_START and LXC_TYPE_END.

#define LXCFS_TYPE_OK(type) (type >= LXC_TYPE_CGDIR && type <= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE) looks sufficient

Copy link
Author

Choose a reason for hiding this comment

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

I saw and fixed this one earlier (the range checks being incorrect) might be best practice to keep the LXC_TYPE_END, it prevents needing to adjust LXCFS_TYPE_OK macro later on in the future and its pretty obvious to anyone going forwards not to add to the end of it.

Copy link
Member

Choose a reason for hiding this comment

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

agree with you :)

src/lxcfs.c Outdated
if (path && strcmp(path, "/") == 0)
return 0;

lxcfs_error("lxcfs_releasedir() unknown file type: path=%s, type=%d\n",
Copy link
Member

Choose a reason for hiding this comment

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

you don't need extra \n as lxcfs_error prints a new line automatically

Copy link
Author

Choose a reason for hiding this comment

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

Fixed in latest commit.

src/lxcfs.c Outdated
}

lxcfs_error("lxcfs_release() unknown file type: path=%s, type=%d\n",
Copy link
Member

Choose a reason for hiding this comment

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

\n is excess

Copy link
Author

Choose a reason for hiding this comment

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

And again.

@deleriux deleriux force-pushed the file_info_api branch 2 times, most recently from 0b749c9 to 04277d9 Compare January 12, 2023 11:13
@deleriux
Copy link
Author

I've made the changes and cleaned up the commit history somewhat. I'll test the deployment out and see how it fares today.

src/bindings.h Outdated
#define LXCFS_TYPE_CGROUP(type) (type >= LXC_TYPE_CGDIR && type <= LXC_TYPE_CGFILE)
#define LXCFS_TYPE_PROC(type) (type >= LXC_TYPE_PROC_MEMINFO && type <= LXC_TYPE_PROC_SLABINFO)
#define LXCFS_TYPE_SYS(type) (type >= LXC_TYPE_SYS && type <= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE)
#define LXCFS_TYPE_OK(type) (type > LXC_TYPE_CGDIR && type < LXC_TYPE_END)
Copy link
Member

Choose a reason for hiding this comment

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

#define LXCFS_TYPE_OK(type) (type >= LXC_TYPE_CGDIR && type < LXC_TYPE_END)?

Copy link
Author

Choose a reason for hiding this comment

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

Spotted and fixed myself already. Thanks.

src/lxcfs.c Outdated
up_users();
ret = do_sys_release(path, fi);
down_users();
return ret;
}

lxcfs_error("lxcfs_release() unknown file type: path=%s, type=%d",
Copy link
Member

Choose a reason for hiding this comment

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

lxcfs_error already prints the function name from where it's called ;)

Copy link
Author

Choose a reason for hiding this comment

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

Fixed

src/lxcfs.c Outdated
if (path && strcmp(path, "/") == 0)
return 0;

lxcfs_error("lxcfs_releasedir() unknown file type: path=%s, type=%d",
Copy link
Member

Choose a reason for hiding this comment

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

function name is already printed inside the lxcfs_error

Copy link
Author

Choose a reason for hiding this comment

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

Fixed

@mihalicyn
Copy link
Member

mihalicyn commented Jan 12, 2023

Thanks @deleriux for working on that. I think it's also important to mention in the commit message and PR description that you are trying to fix SEGV with this patch :) Because right now, at first glance, it looks just like an improvement. But in fact, this is a bugfix. Let's rename PR too to reflect the reason why you are doing all of that :)

@mihalicyn
Copy link
Member

mihalicyn commented Jan 12, 2023

Let's call other reviewers :)

cc @tych0

@deleriux deleriux force-pushed the file_info_api branch 2 times, most recently from 6bc0e36 to 478e579 Compare January 12, 2023 11:48
Copy link
Member

@mihalicyn mihalicyn left a comment

Choose a reason for hiding this comment

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

the first line in the commit message should be like lxcfs: handle NULL path in lxcfs_releasedir/lxcfs_release.

And please rename PR accordingly.

@deleriux
Copy link
Author

../src/lxcfs.c: 813: lxcfs_releasedir: unknown file type: path=/proc, type=-1
Looks like there is more work here to do to handle base paths in /proc which aren't typically designated with a type themselves.

@deleriux
Copy link
Author

Fixed the latest minor niggle. There is no need to check for this in lxcfs_release since the inode is always a directory.
I also changed the commit message as reflected.

@deleriux deleriux changed the title File info api lxcfs: handle NULL path in lxcfs_releasedir/lxcfs_release Jan 12, 2023
src/lxcfs.c Outdated
up_users();
ret = do_sys_releasedir(path, fi);
down_users();
return ret;
}

if (LXCFS_TYPE_PROC(type))
Copy link
Member

Choose a reason for hiding this comment

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

you don't need this check at all then. Cause for /proc directory we don't fill fi with any info in lxcfs_opendir:

static int lxcfs_opendir(const char *path, struct fuse_file_info *fi)
{
	int ret;

	if (strcmp(path, "/") == 0)
		return 0; // don't touch "fi"

	if (strncmp(path, "/cgroup", 7) == 0) {
		up_users();
		ret = do_cg_opendir(path, fi); // << fills "fi" with something
		down_users();
		return ret;
	}

	if (strcmp(path, "/proc") == 0)
		return 0; // don't touch "fi"

	if (strncmp(path, "/sys", 4) == 0) {
		up_users();
		ret = do_sys_opendir(path, fi); // << fills "fi" with something
		down_users();
		return ret;
	}

	return -ENOENT;
}

Copy link
Author

Choose a reason for hiding this comment

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

Excellent point. I should try to extend my head-logic a bit further ;). Updated.

mihalicyn
mihalicyn previously approved these changes Jan 12, 2023
Copy link
Member

@mihalicyn mihalicyn left a comment

Choose a reason for hiding this comment

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

LGTM

Thanks!

@mihalicyn
Copy link
Member

And yes, one small suggestion. We can replace lxcfs_error("unknown file type: path=%s, type=%d", path, type); to lxcfs_error("unknown file type: path=%s, type=%d, fi->fh=%" PRIu64, path, type, fi->fh);

@deleriux
Copy link
Author

Fixed to include the pointer address. I assume you really only care if its a 0 or close to it though...

@mihalicyn
Copy link
Member

Fixed to include the pointer address. I assume you really only care if its a 0 or close to it though...

in fact yes. But if we will have a coredump in a particular case this address may help to debug.

src/lxcfs.c Outdated
up_users();
ret = do_sys_release(path, fi);
down_users();
return ret;
}

lxcfs_error("unknown file type: path=%s, type=%d, type=%" PRIu64,
Copy link
Member

Choose a reason for hiding this comment

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

type= to fi->fh=

Copy link
Author

Choose a reason for hiding this comment

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

oof.. thanks.

mihalicyn
mihalicyn previously approved these changes Jan 12, 2023
src/bindings.h Outdated
@@ -67,8 +67,15 @@ enum lxcfs_virt_t {

LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE,
#define LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE_PATH "/sys/devices/system/cpu/online"
LXC_TYPE_END,
Copy link
Member

Choose a reason for hiding this comment

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

Traditionally, we've suffixed things like that with MAX so I'd prefer LXC_TYPE_MAX.

Copy link
Author

Choose a reason for hiding this comment

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

Changed style from END to MAX.

Copy link
Author

Choose a reason for hiding this comment

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

Added the requested comment also.

src/bindings.h Outdated
#define LXCFS_TYPE_CGROUP(type) (type >= LXC_TYPE_CGDIR && type <= LXC_TYPE_CGFILE)
#define LXCFS_TYPE_PROC(type) (type >= LXC_TYPE_PROC_MEMINFO && type <= LXC_TYPE_PROC_SLABINFO)
#define LXCFS_TYPE_SYS(type) (type >= LXC_TYPE_SYS && type <= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE)
#define LXCFS_TYPE_OK(type) (type >= LXC_TYPE_CGDIR && type < LXC_TYPE_END)
Copy link
Member

Choose a reason for hiding this comment

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

This is error prone as it depends on ordering in the enum. The ordering isn't enforced however. So there needs to be at least a comment on top of the enum explaining this. Also, we should explicitly number the enum. This also makes it harder to accidently break ordering requirements.

Copy link
Member

Choose a reason for hiding this comment

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

I thought about that too, after we extend this enum with new possible values it will be needed to fix these macros like that:
#define LXCFS_TYPE_PROC(type) ((type >= LXC_TYPE_PROC_MEMINFO && type <= LXC_TYPE_PROC_SLABINFO) || type == LXC_TYPE_PROC_SOMETHING). Not so fancy, but...

Copy link
Member

Choose a reason for hiding this comment

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

Technically, right now it's also not correct to add new values in between the enum. Cause we can perform reloading of liblxcfs in between open and close of the file descriptor. Then because enum values shift, some condition checks may become incorrect. For example https://github.com/lxc/lxcfs/blob/master/src/sysfs_fuse.c#L372

Copy link
Author

@deleriux deleriux Jan 12, 2023

Choose a reason for hiding this comment

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

I also did give this some thought beforehand. I thought some ways to do this could be..

  1. re-order the enum numbering explicitly granting space for future files. IE
enum lxcfs_virt_t { 
  LXC_TYPE_CGROUP_BASE = 0x00000,
...
  LXC_TYPE_CGROUP_MAX,

  LXC_TYPE_PROC_BASE = 0x10000,
...
  LXC_TYPE_PROC_MAX,

  LXC_TYPE_SYS_BASE = 0x20000,
...
  LXC_TYPE_SYS_MAX,
}
  1. extend file_info to include a 'class' parameter, ditch the defines, remove file_info_type in favour of file_info_class.

For 2. Obviously you'd basically have to go through every file type already configured and put them in the right class, but at that point ordering no longer matters as you're relying on a different set of metadata to work out what goes where..

Also you could hijack the unused file_info.buf pointer and change it to a unsigned char abi_version[sizeof(char *)] that you could check going forwards. This would let you conditionally and smartly extend the runtime ABI yet be able to deal with previous versions of the ABI changing.
This is of course assuming *buf is zeroed on initialization currently (version 0 implictly).
This also felt like a whole load of architectural work for what is essentially a bugfix!

For 1. Its much simpler to implement and prevents in the future having to make a headache again. That however breaks the ABI in a manner which would not survive a reload so didn't imagine that would get any traction..

Copy link
Author

Choose a reason for hiding this comment

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

Just also thought you could actually do LXC_TYPE_BASE = 0x00000, copy all the file types as-is, then have new namespaces for each type and defines for which you use going forwards. That gives you both the means to maintain reload compatibility by checking if the type is in a 'base' namespace or the given correct namespace.

Its a little janky but probably simpler than doing some abi extension.

Copy link
Member

Choose a reason for hiding this comment

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

For now just leaving a comment is fine. We just want to fix the immediate bug. Reworking it to something better should be a separate step. But feel free to follow this up with a series.

Copy link
Author

@deleriux deleriux Jan 12, 2023

Choose a reason for hiding this comment

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

Added the requested comment above the enum indicating only appending to the bottom (above LXC_TYPE_MAX).

@tych0
Copy link
Member

tych0 commented Jan 12, 2023

Can you talk about what case the path would be null? At first glance this seems like a weird call for libfuse to make.

@mihalicyn
Copy link
Member

Can you talk about what case the path would be null? At first glance this seems like a weird call for libfuse to make.

#575 (comment)

@tych0
Copy link
Member

tych0 commented Jan 12, 2023

So I guess it's really the kernel, not libfuse, that's triggering these, given that they're just passthroughs from libfuse? I still don't understand why, but maybe it's a mystery for the ages...

@mihalicyn
Copy link
Member

mihalicyn commented Jan 12, 2023

So I guess it's really the kernel, not libfuse, that's triggering these, given that they're just passthroughs from libfuse? I still don't understand why, but maybe it's a mystery for the ages...

As far as I know, the kernel doesn't know anything about the path, or, speaking more correctly, the kernel gives the path from open*() syscalls to fuse daemon only for the first time through a FUSE_LOOKUP [1] request, then userspace should provide the kernel with inode number for this file descriptor. The later kernel will only send this number as file identification [2]. So, libfuse handles this path resolve for our convenience. I feel that we have some bugs in libfuse, we have to add some workaround in our code to prevent crashes at least, IMHO.

[1] https://github.com/torvalds/linux/blob/764822972d64e7f3e6792278ecc7a3b3c81087cd/fs/fuse/dir.c#L190
[2] https://github.com/torvalds/linux/blob/764822972d64e7f3e6792278ecc7a3b3c81087cd/fs/fuse/file.c#L40

@tych0
Copy link
Member

tych0 commented Jan 12, 2023

Yeah, that's what I was wondering about. Makes sense, thanks. Patch LGTM.

Implement a file_info_type function.
Create a series of defines for the file type

Inspect the file type for each file handler and perform the relevant
release code to free any memory.

If the type cannot be determined, print an error and return -EINVAL.

This should also be slightly faster than a strcmp() but its not likely
that measurable.

This code could be used elsewhere in the process to reduce the strcmp
requirements, but for now just handle the release/releasedir case.

This fixes SEGV in lxcfs_relase/releasedir when path=NULL but invoked a
strcmp().

Signed-off-by: Matthew Ife <matthewi@mustardsystems.com>
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.

None yet

4 participants