News for hackers. Stuff that matters.
Re:Pledge() and unveil() in SerenityOS (2020) by IshKebab on Friday July 15, 2022 @09:20AM (#32107872)
Why is the interface string-based? Seems very hacky and unusual. Also, special bonus evil points for whoever decided that "rpath" should have nothing to do with RPATH.

Seriously please can all of you other programmers stop pointlessly abbreviating things that are already quite short?! "read_path" is perfectly fine.

Re:Pledge() and unveil() in SerenityOS (2020) by kotborealis on Friday July 15, 2022 @09:30AM (#32107978)
`pledge` is from OpenBSD, as suggested by the article, and it also implements it using string-based interface (https://man.openbsd.org/pledge.2)

I think, the main point is to make it as easy as possible to use.

Re:Pledge() and unveil() in SerenityOS (2020) by IshKebab on Friday July 15, 2022 @09:33AM (#32108005)
Yeah I know, I was really asking why OpenBSD did it that way. I don't think string-based arguments are easier to use. If it was a struct with a load of `bool` fields you'd get code completion, compile time type checking, built in documentation, discoverability, etc. Much easier!
Re:Pledge() and unveil() in SerenityOS (2020) by cyphar on Friday July 15, 2022 @09:43AM (#32108104)
You can't trivially extend structs in a kernel ABI (to be fair this is worse in Linux as there is more than one libc and many programs do raw syscalls bypassing the libc anyway -- though it can still be done[1]) but string APIs are simpler to use and upgrade. They can also be far more ergonomic in some cases.

The core issue is that userspace programs and libraries can be compiled using structs with the old size (in theory the libc can abstract this using symbol versioning but then the same issue lies within the libc) causing out-of-bounds memory accesses when the kernel tries to access the struct fields. There's also forwards-compatibility issues but bad memory accesses are marginally worse.

[1]: https://lwn.net/Articles/830666/

Re:Pledge() and unveil() in SerenityOS (2020) by NavinF on Friday July 15, 2022 @10:30AM (#32108571)
From the LWN article you linked:

>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.

Oh hey, that’s exactly how every WIN32 call works. I see an article from 2003 talking about why Windows is strict about the struct size parameter: https://devblogs.microsoft.com/oldnewthing/20031212-00/?p=41...

> You can't trivially extend structs in a kernel ABI

IMO the article you linked is evidence that this is trivial. Especially for syscalls that will only be called a few times in a process’s lifetime. I dunno why there’s so much bike shedding about this on the mailing list.

Edit: Ahh I didn’t realize you were the Aleksa mentioned in the article. I wish you good luck.

Re:Pledge() and unveil() in SerenityOS (2020) by cyphar on Friday July 15, 2022 @10:45AM (#32108708)
That Windows article gives a better argument than I ever could as to why putting the size in the argument list is better than in the struct. Some pre-openat2 Linux syscalls are designed in the same way. (Having no forwards nor backwards compatibility but still having struct size versioning really is an interesting design choice...)

But yes, it is relatively trivial -- my point was more that you can't just use a struct in the way the first comment suggested, you need to come up with some scheme (even if it seems trivial in retrospect).

As for the bike-shedding, that's LKML for you (though in fairness it is a bit of a tall order to try to come up with some enforceable API design rules in Linux -- syscalls with half-baked designs being added is less rare than one would hope, so clearly there's not an overarching design principle being applied already, though thankfully it's becoming pretty rare to see a completely borked syscall that clearly has no users being merged).

Re:Pledge() and unveil() in SerenityOS (2020) by loeg on Friday July 15, 2022 @10:20AM (#32108474)
The kernel can easily version APIs using struct size, as long as new members are only appended. The libc function would pass the size of the struct to the syscall, or you could have pledge() itself be a macro that computes sizeof in the caller.
Re:Pledge() and unveil() in SerenityOS (2020) by cyphar on Friday July 15, 2022 @10:25AM (#32108521)
That is the exact solution described in the link I included in my comment (I am the "Aleksa" in that article). It is not entirely trivial (certain edge cases need to be handled) but it is entirely doable. But string arguments also work if you don't have complicated data parsing requirements.

I (obviously) prefer the extensible struct solution but there are downsides (and other solutions weren't an option for Linux anyway).

Re:Pledge() and unveil() in SerenityOS (2020) by Toaster-King on Friday July 15, 2022 @09:50AM (#32108188)
There's a great blog post[1] by one of the OpenBSD developers about why they did so. tl;dr using bitmasks necessitates namespaced enums/defines that take up horizontal space, strings are easier and don't need to go through the C pre-processor.

[1] https://flak.tedunangst.com/post/string-interfaces

Re:Pledge() and unveil() in SerenityOS (2020) by IshKebab on Friday July 15, 2022 @02:58PM (#32111615)
Nice find. That article is highly unconvincing though and mostly argues against straw men.

> Although using strings subverts C’s already weak type checking, that’s probably not a major concern. One can screw up bit masks by using || in place of |. Or, as above, one can incorrectly pack the magic array. It’s usually much easier to visually audit a string than the C code used to plaster a dozen option together.

It's pretty easy to design an interface that is way way less error-prone than strings (especially ones full of single-letter differences!) and the visual auditing argument falls apart as soon as you have to `snprintf()` some string together from parts.

This code is way more readable, way less error prone, more discoverable, faster and more easily extendable than strings:

    auto config = make_pledge_config();
    config.read_path = true;
    config.stdio = true;
    pledge(&config);
You'd think security focused people would care about static type checking.
Re:Pledge() and unveil() in SerenityOS (2020) by jeshin on Friday July 15, 2022 @07:04PM (#32113669)
if you really care about static type checking, you probably wouldn't be using C
Re:Pledge() and unveil() in SerenityOS (2020) by IshKebab on Saturday July 16, 2022 @02:19AM (#32115689)
You probably would if you care about static typing and are working on a kernel syscall interface.
Re:Pledge() and unveil() in SerenityOS (2020) by jeshin on Sunday July 17, 2022 @02:49AM (#32124979)
and yet they chose to use strings for their api, eh?
Re:Pledge() and unveil() in SerenityOS (2020) by IshKebab on Sunday July 17, 2022 @02:43PM (#32129762)
Well exactly. That's why it's so weird.
Re:Pledge() and unveil() in SerenityOS (2020) by hag on Friday July 15, 2022 @10:11AM (#32108409)
Nice! I would probably have used bitmasks in this situation, but as usual there is a reason behind the choice and I get the reasoning.
Re:Pledge() and unveil() in SerenityOS (2020) by agileAlligator on Friday July 15, 2022 @09:56AM (#32108252)
great find!
Re:Pledge() and unveil() in SerenityOS (2020) by bowsamic on Friday July 15, 2022 @09:44AM (#32108113)
Probably because the ABI would change when a pledge is added
Re:Pledge() and unveil() in SerenityOS (2020) by sedatk on Sunday July 17, 2022 @06:14PM (#32131968)
Not necessarily. You can pass a bitmask of an arbitrary length using char*.
Re:Pledge() and unveil() in SerenityOS (2020) by tomjakubowski on Friday July 15, 2022 @02:44PM (#32111473)
Would printf be easier or harder to use if the format were specified with a struct? (instead of a string)
Re:Pledge() and unveil() in SerenityOS (2020) by diath on Friday July 15, 2022 @09:53AM (#32108221)