|
|
Log in / Subscribe / Register

Conventions for extensible system calls

By Jonathan Corbet
September 8, 2020

LPC
The kernel does not have just one system call to rename a file; instead, there are three of them: rename(), renameat(), and renameat2(). Each was added when the previous one proved unable to support a new feature. A similar story has played out with a number of system calls: a feature is needed that doesn't fit into the existing interfaces, so a new one is created — again. At the 2020 Linux Plumbers Conference, Christian Brauner and Aleksa Sarai ran a pair of sessions focused on the creation of future-proof system calls that can be extended when the need for new features arises.

Brauner started by noting that the problem of system-call extensibility has been discussed repeatedly on the mailing lists. The same arguments tend to come up for each new system call. Usually, developers try to follow one of two patterns: a full-blown multiplexer that handles multiple functions behind a single system call, or creating a range of new, single-purpose system calls. We have burned ourselves and user space with both, he said. There are no good guidelines to follow; it would be better to establish some conventions and come to an agreement on how future kernel APIs should be designed.

The requirements for system calls should be stronger, and they should be well documented. There should be a minimal level of extensibility built into every new call, so that there is never again a need to create a renameat2(). The baseline, he said, is a flags argument; that convention is arguably observed for new system calls today. This led to a brief side discussion on why the type of the flags parameter should be unsigned int; in short, signed types can be sign extended, possibly leading to the setting of a lot of unintended flags.

Sarai took over to discuss the various ways that exist now to deal with system-call extensions. One of those is to add a new system call, which works, but it puts a big burden on user-space code, which must change to make use of this call. That includes checking to see whether the new call is supported at all on the current system and falling back to some other solution in its absence. The other extreme, he said, is multiplexers, which have significant problems of their own.

Yet another approach is to set a flag (on calls that support flags, obviously) and make the system call take a variable number of arguments; among other things, variadic calls are difficult for C libraries to support properly. It is necessary to pass all possible arguments, leading to passing garbage on the stack into the kernel. System calls could be designed with fixed-size structs as arguments, with the idea that these structs would contain enough padding to handle any future needs. The problem with this approach, he said, was that it requires an ability to predict the future, which turns out to be difficult to do.

Finally, he said, the problem could be solved by getting and using a time machine. This solution, too, suffers from practical difficulties.

Extensible structs

Brauner and Sarai are pushing a different solution that they call "extensible structs"; it is the approach used in the design of the openat2() system call. This mechanism works by marshaling parameters to a system call into a single C structure; a pointer to that structure and the size of the structure are passed as the parameters to the system call. That size parameter acts as a sort of version number. When the time comes to extend the system call in a way the requires passing new data to the kernel, new fields are added to the end of the structure, increasing the size. Those new fields are always designed so that a value of zero implies the behavior that existed before those fields were added.

When the system call is invoked from user space, the kernel compares the structure size passed in to its own idea of how big the structure should be. If the size from user space is smaller, that indicates that the caller expects to use an older version of the system call; the fields that user space did not provide are filled with zeroes and the call proceeds as usual, with no visible change in behavior. If, instead, the kernel's size is smaller than the structure passed from user space, then the kernel is the older side. In that case, all of the excess fields (from the kernel's point of view) are checked; if they are all zero, the call can proceed. Otherwise, the call fails with an E2BIG error, since user space is requesting functionality that the kernel does not know how to provide.

H. Peter Anvin questioned the use of the E2BIG return code. Sarai responded that its assigned meaning is "argument list too long"; it is a bit weird, he said, but "it makes sense if you squint".

[Aleksa Sarai, Arnd
Bergmann, Christian Brauner, and Florian Weimer]

Brauner emphasized that the rules should prohibit any further multiplexer system calls. This is almost universally agreed on now. bpf() is not an extensible system call; it is a multiplexer that happens to use extensible structs. So bpf() is not an example of how system calls should be designed in the future. The GNU C Library developers have also made it clear that they would rather not see any more multiplexer calls, since they are hard to deal with at that level. Arnd Bergmann said that a number of developers try to block multiplexer system calls, but it is hard to see them all; that is why this rule should be in the documentation, Brauner replied.

Anvin asked why the structure size is passed as a separate argument rather than being embedded in the structure itself. Brauner replied that it feels more "C-like" to pass the size in the argument list, but that it doesn't matter that much in the end. The community should pick one convention or the other, though. Anvin said that the separate size can create problems if the struct is being passed through different user-space layers that may have different ideas of what its proper size is; that could result in passing bad data to the kernel.

Kees Cook, instead, said that confusion in user space is preferable to confusion in the kernel. There could be problems if the kernel reads the size from within the structure, then uses that size to read the structure itself in to kernel space. That size might have changed in between the two reads, possibly opening up vulnerabilities within the kernel. Thus, he said, the size is better passed as a separate element. When asked which convention would work better for C libraries, Florian Weimer responded that he didn't have a strong opinion either way.

Brauner said that, in the past, Al Viro has called extensible structs a "crap insertion vector", saying that they could be used to sneak new features into the kernel without review. That view doesn't hold up, Brauner said; as an example, the feature that prompted this reaction was indeed caught in review. There is not a problem there that is worse than with any other system call, he said.

This part of the discussion closed with Brauner saying that the new conventions need to be added to the documentation. The current documentation on adding system calls is not up to date; he tried to update it in the past the but that effort "went largely unnoticed". He has been trying to build more consensus around the extensible structs idea; meanwhile, he has landed two new system calls using it. He will be working on a new version of the documentation patch that is intended to describe the current best practices as he sees them.

Probing feature support

Extensible structs might be a solution to the problem of adding features to system calls, but they do not, in themselves, address the other part of the problem: helping user space to figure out which features are actually supported on a given system. As Brauner noted, programs normally have to adopt a sort of trial-and-error approach, where they try to exercise each feature in question and see if it actually works. This process is painful at best, and it can become expensive, especially in libraries and short-running programs. It should be possible to do better. The conversation on just how to do better got started in this session, then continued in a birds-of-a-feather session later.

Sarai mentioned one proposal that has been circulating: add a "no-op" flag to the system call. The kernel would respond by returning a copy of the extensible struct with all valid flag bits set and non-flag fields filled with the highest supported value. A unique error code would be returned if the queried operation is not supported at all. Bergmann quickly pointed out that this flag would turn the system call into a sort of multiplexer with multiple functions, but noted that he could see the upside of doing things that way.

The alternative would be to create a new system call dedicated to checking which features are supported by other system calls. This would not be entirely straightforward to implement, since it requires adding a new infrastructure within the kernel for defining system-call features. Brauner noted that there are two specific types of extensibility that would have to be handled: adding a new flag, and increasing the size of the struct.

Bergmann suggested that a minimal solution might be to add a counter in the VDSO area exported by the kernel to user space; every time a feature is added, the counter would be incremented. Cook answered that it was a little too minimal; user space would benefit more from the ability to inquire about the availability of specific features. Weimer said that this would make writing portable software more difficult, since it would be necessary to test all possible permutations of features, but Cook responded that this problem already exists, and the ability to query features would just improve visibility. Mark Rutland suggested starting with a single system call defining exactly which information would be exposed; Brauner said that clone3() might be a good starting point.

There appeared to be a consensus that a separate system call is the right way to solve this problem, so the discussion turned to what this system call would look like. Brauner started with a suggestion that this new call would take the number of the system call of interest, and would return the current set of valid flags and struct size. Sarai pointed out that openat2() has two flags arguments, complicating the situation. Weimer, instead, said he would like to be able to query whether vfork() is available, but there's no flags argument there at all.

Mark Rutland suggested an API that would look something like:

    int sys_features(int syscall_no, u32 *map, size_t mapsize);

The kernel would treat map as a bitmap to be filled in describing the available features for the requested system call. Each system call would have a set of constants for each feature that may or may not be present, each corresponding to one bit that would be set in map if the feature is available. This proposal seemed to gain some support during the discussion.

Brauner said that there is also a need to automate the process of adding a system call to the kernel — preferably one not written in Perl. It could perform a number of checks, including whether the struct size is described correctly and the sys_features() information is correct. Bergmann suggested extending the DECLARE_SYSCALL() macro in the kernel to take the bitmap of supported features as an additional argument.

The conversation wandered on for a little longer, but the form of the outcome was already clear. For the extensible structs portion, the main action item will be to update the documentation to reflect the new consensus (if there is truly a consensus) on how extensibility should be handled. For the feature-query system call, the task will be to write some code showing how it would actually work. Once the concept hits the mailing lists it may end up changing significantly. One can only hope that the end result gets things right, so we won't need a sys_features2() in the future.

Index entries for this article
KernelSystem calls
ConferenceLinux Plumbers Conference/2020


to post comments

Conventions for extensible system calls

Posted Sep 8, 2020 15:33 UTC (Tue) by famzheng (subscriber, #121411) [Link] (8 responses)

What happened to "everything is a file", why not just expose features with sysfs in a well defined way so that we don't need yet another syscall to probe for syscalls?

Conventions for extensible system calls

Posted Sep 8, 2020 16:45 UTC (Tue) by dezgeg (guest, #92243) [Link]

Especially since we already have a format for describing struct layouts for tracepoints - i.e. /sys/kernel/debug/tracing/events/*/*/format

Conventions for extensible system calls

Posted Sep 9, 2020 0:28 UTC (Wed) by mathstuf (subscriber, #69389) [Link]

Some downsides I can see with that are file descriptor resource limits (which is unlikely), but setups which restrict which paths can be accessed isn't going to be getting less common.

Conventions for extensible system calls

Posted Sep 9, 2020 0:54 UTC (Wed) by liam (guest, #84133) [Link]

All you need is CRUD:)

Conventions for extensible system calls

Posted Sep 9, 2020 6:56 UTC (Wed) by diconico07 (guest, #117416) [Link] (4 responses)

My main concern with using files would be performance, you may end up calling this feature checking syscall quite often (maybe even do that within C library), so between calling a dedicated syscall and opening,reading,closing a file (min 3 syscalls) the dedicated syscall wins.

Conventions for extensible system calls

Posted Sep 9, 2020 7:29 UTC (Wed) by famzheng (subscriber, #121411) [Link]

I think that's a fair concern, can open/read/close be made a single syscall eg with io_uring?

The file approach may have much better introspective capabilities. It's easy to version it. If it were implemented a syscall, how do we probe for the feature of this probing syscall itself?

Conventions for extensible system calls

Posted Sep 9, 2020 11:01 UTC (Wed) by artizirk (subscriber, #113443) [Link]

readfile() could work quite well for this usecase.

https://lwn.net/Articles/813827/

Conventions for extensible system calls

Posted Sep 9, 2020 16:04 UTC (Wed) by dezgeg (guest, #92243) [Link] (1 responses)

I do not see this as a big problem, this feature-checking can be done once during the first call and then cached. I think glibc already does such things today (by trying to use a syscall and if the result is ENOSYS, remember that and fall back to something else).

Conventions for extensible system calls

Posted Sep 10, 2020 6:17 UTC (Thu) by notriddle (subscriber, #130608) [Link]

That’s the minimum bar, then. Whatever design they go with has to be faster than the trial-and-error version.

Conventions for extensible system calls

Posted Sep 8, 2020 15:46 UTC (Tue) by alonz (subscriber, #815) [Link] (11 responses)

I'm surprised nobody mentioned the security issues with this approach – the struct containing the arguments would now have embedded pointers, including pointers to e.g. filenames, but would be impossible to securely inspect using seccomp or similar mechanisms.

Or has this somehow been solved (in a generic fashion) while I wasn't looking?

Conventions for extensible system calls

Posted Sep 8, 2020 18:14 UTC (Tue) by nickodell (subscriber, #125165) [Link]

There are various ideas for how to address that: https://lwn.net/Articles/822256/

Conventions for extensible system calls

Posted Sep 9, 2020 1:05 UTC (Wed) by imMute (guest, #96323) [Link]

>but would be impossible to securely inspect using seccomp or similar mechanisms.

Impossible today, or impossible forever? *Today* seccomp can't inspect embedded pointers, but there's no reason it can't in the future.
It's not a great idea to design APIs around current limitations, especially when said limitations have other reasons for being overcome.

Conventions for extensible system calls

Posted Sep 9, 2020 10:29 UTC (Wed) by cyphar (subscriber, #110703) [Link] (1 responses)

Kees has been on how to expand seccomp to deal with pointer-to-struct syscalls (he gave a talk about it at LPC as well, and we've been talking with him about this problem as well). It should be noted that this really isn't a new problem -- there are already other syscalls which take struct pointers and seccomp can't filter them properly right now either (though admittedly, the extensibility does require a bit more design work if we want filters to also be forward-compatible).

I do want to point out that (even though I assume it was just an example), filenames are something you don't want to try to restrict with seccomp. That's what LSMs are for -- there's no reasonable way to be sure that a path-based filter will actually stop someone from accessing a path.

Conventions for extensible system calls

Posted Sep 14, 2020 12:43 UTC (Mon) by nix (subscriber, #2304) [Link]

I do want to point out that (even though I assume it was just an example), filenames are something you don't want to try to restrict with seccomp.
The driving use case for modern seccomp was web browsers. Absolutely all of these have seccomp filters that filter by path, because glibc and other libraries routinely used by sandboxed rendering processes want to access particular paths -- usually root-writable-only, things like stuff under /sys, /dev, /usr/lib, that sort of thing -- but the whole point of the sandbox is that they can't go rootling around in your home directory (or, users being prone to weird stuff, /dir-off-the-root-directory-where-I-keep-my-bank-account-details).
That's what LSMs are for -- there's no reasonable way to be sure that a path-based filter will actually stop someone from accessing a path.
Having unprivileged web browsers hack your LSM configuration is unlikely to be the most practical approach. (And it is actually perfectly reliable to use this method to stop people accessing all paths other than a limited subset which is rarely-mutated and is only-root-writable: you can rely on races not being present and you don't need to worry about hostile attackers being able to modify the directories you're allowing through.)

Conventions for extensible system calls

Posted Sep 9, 2020 16:29 UTC (Wed) by iabervon (subscriber, #722) [Link]

Even if they don't solve that problem in general, it would be possible to arrange that any syscall that follows the extensible system call pattern first gets decoded into the logical arguments and that gets passed to seccomp. Of course, this exposes the opposite issue: newer kernels will effectively switch to a version of the syscall with more arguments and flags than existing seccomp filters are expecting, and something has to decide what to do about that.

Conventions for extensible system calls

Posted Sep 9, 2020 20:16 UTC (Wed) by Twistlock (guest, #135955) [Link] (5 responses)

> I'm surprised nobody mentioned the security issues with this approach – the struct containing the arguments would now have embedded pointers, including pointers to e.g. filenames, but would be impossible to securely inspect using seccomp or similar mechanisms.

I don't think the pressing issue here is with embedded pointers, since seccomp can't filter pointers regardless if they're embedded in a struct. The problem is more with arguments that would have been passed by value, but will now be hidden behind a struct pointer (in future syscalls that follow this design).

Conventions for extensible system calls

Posted Sep 9, 2020 21:34 UTC (Wed) by Wol (subscriber, #4433) [Link] (4 responses)

Sounds like what we need is some way of saying "syscalls take one argument, a struct passed by value".

Might play havoc with normal C calling conventions, though :-)

Cheers,
Wol

Conventions for extensible system calls

Posted Sep 10, 2020 12:00 UTC (Thu) by davecb (subscriber, #1574) [Link]

The multicians found they needed to take a similar approach. For library and system calls that took more than a very few parameters, they passed a versioned struct.

--DRBrown.TSDC@Hi-Multics.ARPA(:-))

Conventions for extensible system calls

Posted Sep 14, 2020 12:47 UTC (Mon) by nix (subscriber, #2304) [Link] (2 responses)

That's easy enough -- the problem is preventing a parallel userspace thread from modifying the struct between the point at which the seccomp filter evaluates it and the point at which the args are used. This problem doesn't arise with actual arguments because those end up on the kernel stack, which userspace cannot modify at all. (And no, marking the struct as nonmodifiable by userspace for the duration of the syscall would be horrendously expensive, and because of the page granularity of protection changes would tend to mark a lot of other variables as nonmodifiable as well, with likely-disastrous results.)

Conventions for extensible system calls

Posted Sep 14, 2020 13:47 UTC (Mon) by cyphar (subscriber, #110703) [Link] (1 responses)

The solution being proposed is that seccomp does the copy first, operates on it, and then gives the copied data (which has been checked by the filter) to the syscall proper. The kernel has to do the copy anyway, doing the copy earlier in the syscall path and then re-using the copy would add no overhead.

Conventions for extensible system calls

Posted Sep 14, 2020 17:29 UTC (Mon) by nix (subscriber, #2304) [Link]

This will probably take some interesting fiddling around, given that the copies now are scattered all over the place in countless copy_from_user calls. I look forward to watching the fireworks!

Conventions for extensible system calls

Posted Sep 8, 2020 15:56 UTC (Tue) by willy (subscriber, #9762) [Link]

I remain confused by using "unsigned int" for flags. Using "unsigned long" makes more sense as it allows for 64-bit only features to be selected in bits which don't exist for 32-bit machines, leaving space in the low flags for features which are useful for both 32 and 64 bit machines.

Somewhere we made a mess of this already is with hugetlb size flags. Bits 26-31 are used for order 0-63. But there's no need for a 32 bit process to be able to specify an order above 31, so we would have been better off using bits 27-32 which lets 32-bit processes ask for order 0-31 and 64 bit processes can go all the way to 63.

(Actually there's no need to support orders below 13, so we could stuff the bits even harder into 28-33; supporting orders 13-28 on 32-bit and 13-63 on 64-bit, but some might complain that order 30 (1GB) pages couldn't be requested on x86-32)

Conventions for extensible system calls

Posted Sep 8, 2020 18:50 UTC (Tue) by fenncruz (subscriber, #81417) [Link] (1 responses)

Whats the logic behind using the size of the struct instead of just passing a version number?

Conventions for extensible system calls

Posted Sep 8, 2020 19:44 UTC (Tue) by Villemoes (subscriber, #91911) [Link]

A version number wouldn't allow the "new userspace on old kernel". As the older kernel would not in now how much of userspace memory to check for all-zeroes, an old kernel would always have to reject too high version numbers, instead of gracefully handling the many cases where userspace hasn't actually used any of the newer features.

Also, having the size passed directly avoids each syscall having their own version->size table, avoiding a cache miss and a place to worry about Spectre.

Conventions for extensible system calls

Posted Sep 9, 2020 7:09 UTC (Wed) by tdz (subscriber, #58733) [Link] (7 responses)

I fail to see how do these structs make a difference. The struct size (i.e. version number) is nothing else than the number at the end of rename() or a new flag. If either kernel or userspace does not support it, it doesn't work.

I used to work on Android userspace drivers, which use similar versioning conventions in their APIs. Versioned structs didn't simplify anything there. What they do is to complicate matters if you what to handle them with a code generator or wrap them somehow.

Conventions for extensible system calls

Posted Sep 9, 2020 10:33 UTC (Wed) by cyphar (subscriber, #110703) [Link] (6 responses)

I wrote a pretty detailed explanation for how the compatibility works in the openat2 man page. The short answer is that the struct size isn't just a version number, it allows the kernel to know how much of the passed struct userspace knows about (and in the case of an old kernel, whether the userspace program is requesting a feature it doesn't know about). Creating a new syscall means that userspace has to support both the old and new versions so that it can work on old kernels, but with this design userspace doesn't need to do anything to continue to work on old kernels (assuming it doesn't try to use a feature the old kernel doesn't support).

Conventions for extensible system calls

Posted Sep 9, 2020 12:23 UTC (Wed) by tdz (subscriber, #58733) [Link] (5 responses)

> I wrote a pretty detailed explanation for how the compatibility works in the openat2 man page.

It still doesn't make sense to me.

> The short answer is that the struct size isn't just a version number, it allows the kernel to know how much of the passed struct userspace knows about (and in the case of an old kernel, whether the userspace program is requesting a feature it doesn't know about).

Using *any* number instead of size tells the kernel the same; as long that number increases with the number of supported features. And that's a version number.

An actual version number has the (admittedly questionable) benefit, that you could pack parameters for additional features into existing, unused bits. The struct size doesn't allow this. You have to append parameter to the struct's end.

> Creating a new syscall means that userspace has to support both the old and new versions so that it can work on old kernels, but with this design userspace doesn't need to do anything to continue to work on old kernels (assuming it doesn't try to use a feature the old kernel doesn't support).

The algorithm for compatibility is simple: always use the oldest syscall that supports whatever you're trying to do.

These versioned structs (or version numbers) just move the problem from userspace to kernel, but don't solve anything.

Conventions for extensible system calls

Posted Sep 9, 2020 14:31 UTC (Wed) by mathstuf (subscriber, #69389) [Link] (4 responses)

> Using *any* number instead of size tells the kernel the same; as long that number increases with the number of supported features. And that's a version number.

The main benefit with using a size is that when running on an older kernel, that kernel now knows how much to check for all-zeros before rejecting the call. If I use a bigger structure than it knows about, but it is all zero beyond what it does, the old kernel can still satisfy the call. With a version number, anything it doesn't know about is automatically unsatisfiable since it doesn't know how far into the struct to check.

Conventions for extensible system calls

Posted Sep 9, 2020 17:36 UTC (Wed) by tdz (subscriber, #58733) [Link] (3 responses)

Well, the trick only works if these zeros don't carry any additional meaning by themselves.

But just to be clear. I'm not speaking in favor version numbers either. IMHO if there's an incompatible change, a new syscall should be introduced. Anything else is fragile fiddling.

Conventions for extensible system calls

Posted Sep 9, 2020 19:26 UTC (Wed) by mathstuf (subscriber, #69389) [Link]

> Well, the trick only works if these zeros don't carry any additional meaning by themselves.

The new semantics have to be able to express the old semantics as well, so you may as well make that case the zero case for the new fields.

> IMHO if there's an incompatible change, a new syscall should be introduced. Anything else is fragile fiddling.

IIRC, syscall numbers are a fairly limited resource. I don't know how close we are to the limit though. I remember some LWN article mentioning it, but maybe it was in reference to the limit being lifted? Does anyone have numbers on how often syscall structures get expanded?

In any case, I think everyone would agree that most syscalls will need some future changes over time (especially ones being introduced today). I think it's better to think about how those changes will work into the future, both on the kernel and the userspace side. If completely different syscalls are made for structure expansions, it means, as a user, I need to either bind the syscall myself or wait for glibc/some other library to expose it and have some hierarchy of call precedence for what I'm doing. I don't think that seems to make sense in the long run personally.

Conventions for extensible system calls

Posted Sep 10, 2020 6:13 UTC (Thu) by cyphar (subscriber, #110703) [Link] (1 responses)

> Well, the trick only works if these zeros don't carry any additional meaning by themselves.

Yes, that is part of the API promise. You need to be able to support the old semantics on a new kernel anyway. And this isn't a new requirement either -- any fixed-size struct extension system also has to require that zero means "the old behaviour". I'm not sure if there is a kernel API that doesn't act like this.

> IMHO if there's an incompatible change, a new syscall should be introduced. Anything else is fragile fiddling.

This isn't about handling incompatible changes, this is about "we want to add a new feature to an existing syscall". Without extensible structs (in the most extreme version of the view you've described) you would need to create a new syscall each time you wanted to add a field to a struct. I'm sure you'd agree that this would be ludicrous, since adding new syscalls for each new feature creates extra work for userspace (not to mention that a lot of newer syscalls don't have glibc wrappers so all of the fallback code has to be open-coded into every project that uses that syscall).

The only thing that this design allows which is different to previous extension designs is that you can add fields to a structure passed to a syscall in a way that preserves forwards and backwards compatibility, without having to reserve a fixed amount of padding (the issue with reserving a fixed amount of padding is that you have to be able to predict how many extensions will be needed for the entire life of that syscall -- it's just simpler to not need to worry about it). That's really all this is -- though it does have the nice consequence of removing a whole bunch of needless busywork from userspace.

It's not even that new! In addition to clone3() and openat2(), some older syscalls (like sched_set_attr(), perf_event_open(), and buried inside the bpf() union) all use the same design. All we're trying to do is to document this as being one of the few ways extensible syscalls should be designed (the other major way being the classic flags argument).

Conventions for extensible system calls

Posted Sep 10, 2020 12:06 UTC (Thu) by davecb (subscriber, #1574) [Link]

As always, communicating a new, better algorithm is harder than inventing one. The multicians did a similar thing, but the best reference is a retrospective by Paul Stachour, published years later (I was his editor (;-)) https://cacm.acm.org/magazines/2009/11/48444-you-dont-know-jack-about-software-maintenance/fulltext

Conventions for extensible system calls

Posted Sep 10, 2020 6:39 UTC (Thu) by amarao (guest, #87073) [Link]

I can't stop thinking that the true problem here is 'machine-readable' documentation for syscall. Some form of BNF to declare available syntax. One more problem is that syscall must be binary and fast. But docs aren't. Should kernel keep a ascii version of description of each syscall in machine and human readable form?


Copyright © 2020, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds