An in-kernel file loading interface
Over time, though, we have seen the introduction of kernel code that does, indeed, read files. The first step in that direction was probably the in-kernel module loader, which replaced the user-space loader back in 2002. The module loader does not actually open files; it depends on user space to hand it a file descriptor corresponding to the module to be loaded. But, given that, it does read the module code directly, perform the necessary symbol resolution, and bind it into the kernel.
The door opened wider when the firmware-loading mechanism was moved in-kernel; in this case, the file containing the firmware is being opened by name from within the kernel. The integrity management architecture code also has to open files, and it seems likely that other uses will show up over time. Since there is no standard way to open and read a file within the kernel, there is a separate implementation for each of these users, each of which does things in its own way.
Mimi Zohar recently decided that it was time to make file reading a first-class supported operation within the kernel; the result is this patch set adding a common file loader. It makes this operation easier to perform, but, as will be seen, it still seems like it's not really meant for common use.
At the lowest level, Mimi's patch set adds a new function to read a file's contents into memory:
int kernel_read_file(struct file *file, void **buf, loff_t *size,
loff_t max_size, enum kernel_read_file_id id);
This function will read the data from the open file indicated by file; up to max_size bytes will be read. It will allocate a buffer (using vmalloc()) to hold the file's contents, storing a pointer in *buf; the caller should free the buffer when it is no longer needed. The actual length of the file will be placed in *size. If the file is larger than max_size, nothing will be allocated or read, and -EFBIG will be returned.
The id argument is, arguably, where the interface (intentionally) loses a bit of generality. It is an enum type meant to indicate the purpose for which the file is being read; the values defined in the patch are READING_KEXEC_IMAGE, READING_KEXEC_INITRAMFS, READING_FIRMWARE, READING_MODULE, and READING_POLICY. The READING_POLICY option appears to be the motivation for the patch set; the IMA code can use it to read the policy and perform signature checking on the policy file. Developers wanting to use this interface will, most likely, have to add their own kernel_read_file_id constant to describe what they are doing.
There are a couple of helpers built on top of kernel_read_file():
int kernel_read_file_from_path(char *path, void **buf, loff_t *size,
loff_t max_size,
enum kernel_read_file_id id);
int kernel_read_file_from_fd(int fd, void **buf, loff_t *size,
loff_t max_size, enum kernel_read_file_id id);
As might be expected, the first one opens and reads a file given its pathname, while the second takes an open file descriptor and reads from that.
One advantage to implementing this functionality in a single place is that it becomes possible to apply a uniform security policy in all settings where the kernel tries to read a file. To that end, Mimi's patch set adds two new security hooks (security_kernel_read_file() and security_kernel_post_read_file()) that can pass judgment on file-reading operations. The security_kernel_module_from_file() and security_kernel_fw_from_file() hooks have been removed in favor of the new hooks. This is the purpose of the kernel_file_read_id parameter described above; it is passed to the loaded security module(s) and can be checked by the current security policy.
This patch set has been through a few revisions and has gotten
acknowledgments from a number of the relevant developers. At this point,
there would appear to be few obstacles between it and the mainline kernel.
So, in the near future, the kernel is likely to have a set of generic
functions for opening and reading files, but any future users will have to
tell the kernel what they are up to.
| Index entries for this article | |
|---|---|
| Kernel | Filesystems/Virtual filesystem layer |
Posted Feb 18, 2016 7:00 UTC (Thu)
by gioele (subscriber, #61675)
[Link] (2 responses)
On recent processors with SMAP/SMEP, most parts of the kernel no longer have full control over the memory. Low-overhead IPC is (hopefully) coming soon with KDBUS/BUS1. Drivers for slow USB devices are in userspace. Filesystems can be written in userspace with FUSE. Now kernel code can access files similarly to how userspace programs do.
I suppose one day the division between the core and the rest of the kernel will be clear and enforced. And they will live in the same repository only for historical reasons.
Posted Feb 18, 2016 15:44 UTC (Thu)
by tau (subscriber, #79651)
[Link] (1 responses)
Actually I think it's a pity that firmware loading policy ended up moving into the kernel. The reasons for that were more to do with interpersonal friction than sound technical reasons.
Posted Feb 23, 2016 17:09 UTC (Tue)
by nix (subscriber, #2304)
[Link]
(Only, of course, you have to look at it so vaguely that more or less all facts are blurred away. The kernel is wildly different from a userspace program, and no file-reading API will change that.)
Posted Feb 18, 2016 8:25 UTC (Thu)
by eru (subscriber, #2753)
[Link] (5 responses)
Hasn't the kernel always been able to read files for implementing exec? I never thought there is anything special in kernel reading files. (But then, I am not a kernel programmer).
Posted Feb 21, 2016 0:20 UTC (Sun)
by giraffedata (guest, #1954)
[Link]
Yes, and the kernel has always been able to read files for implementing read, too :-)
Exec is essentially a file operation (it even has its own file permission bit). That puts it in a different class from kernel facilities that use files. The latter smells of a layering violation.
Posted Feb 21, 2016 2:09 UTC (Sun)
by anselm (subscriber, #2796)
[Link] (3 responses)
I was under the impression that what exec() does with files is more closely related to mmap() than to read().
Posted Feb 21, 2016 11:43 UTC (Sun)
by PaXTeam (guest, #24616)
[Link] (2 responses)
Posted Feb 21, 2016 11:54 UTC (Sun)
by anselm (subscriber, #2796)
[Link] (1 responses)
Makes sense to me (thank you for the clarification).
The important point as far as I'm concerned is that the kernel doesn't “read” a large executable into memory in its entirety before it is started, but maps it into memory and fetches pages from the file as required according to page faults arising from running the code.
Posted Feb 21, 2016 13:29 UTC (Sun)
by PaXTeam (guest, #24616)
[Link]
Posted Feb 19, 2016 0:47 UTC (Fri)
by zenaan (guest, #3778)
[Link] (1 responses)
I thought this ground (of flags arguments) was finally well trodden enough to avoid the inevitable future kernel_read_file2 or whatever.
Just overload 'enum kernel_read_file_id id' instead then?
Posted Feb 19, 2016 1:19 UTC (Fri)
by corbet (editor, #1)
[Link]
An in-kernel file loading interface
An in-kernel file loading interface
An in-kernel file loading interface
An in-kernel file loading interface
An in-kernel file loading interface
Hasn't the kernel always been able to read files for implementing exec? I never thought there is anything special in kernel reading files.
An in-kernel file loading interface
An in-kernel file loading interface
An in-kernel file loading interface
An in-kernel file loading interface
An in-kernel file loading interface
This is an internal kernel interface, it can be easily changed if need be.
An in-kernel file loading interface
