pledge should be used to restrict a program to whatever it is necessary to do,
rather than everything the library can do. So if I use libimaginarydb to parse
a csv file I've already read into a memory buffer (nearly pledge("", NULL)),
but the library can read/write/create files, do remote db connections, and
fork shells, we don't want it to do all that (pledge("rpath wpath cpath dns
inet exec proc", NULL)).

> On Jun 13, 2016, at 12:56 PM, <[email protected]>
<[email protected]> wrote:
>
> I have thought of a way pledge(2) can be made a little more
> library-friendly.
>
> This is not a patch, but just a thought.
> There are 2 setups I have thought of:
>
> =============== 1. Variable arguments ===============
>
> int pledge(const char *promises, const char *paths[])
> {
>    return vpledge(1, promises, paths);
> }
>
> int vpledge(const size_t npledge, ...);
>
> -----
>
> In a program, this may be something like this:
>
> #include <stdio.h>
> #include <unistd.h>
> #include <ultralib.h>
> #include <extralib.h>
> #include <superlib.h>
> #include <mechalib.h>
>
> int main(void) {
>    if(vpledge(5, "stdio rpath wpath cpath", NULL,
>        ultra_promises, ultra_pledgepaths,
>        extra_promises, NULL, super_promises, NULL,
>        mecha_promises, mecha_pledgepaths) == -1)
>        errx("pledge");
>
>    ... [other code] ...
> };
>
>
> -------
>
> In vpledge(), "npledge" refers to the number of pledge-pairs, which
> consist of:
>
>    const char *promises, const char *paths[]
>
> These have the same semantics as the original pledge().
>
> A library can export *_promises and *_pledgepaths symbols, pointing to
> static text. This allows the library to change without the program
> being affected because the new library changes call something outside
> the original pledge() of a program.
>
> ============= 2. Using a struct ==================
>
> -----
>
> struct pledge {
>    char *promises;
>    char *paths[];
> };
>
> -----
>
> int pledge(const char *promises, const char *paths[])
> {
>    struct pledge pl = {
>        .promises, paths
>    };
>
>    return pledges(1, &pl);
> }
>
> int pledges(const size_t npledge, const struct pledge pledge_array[]);
>
> -----
>
> In a program, this may be something like this:
>
> #include <stdio.h>
> #include <unistd.h>
> #include <ultralib.h>
> #include <extralib.h>
> #include <superlib.h>
> #include <mechalib.h>
>
> int main(void) {
>    struct pledge pl[4];
>
>    pl[0].promises = "stdio rpath wpath cpath";
>    ultra_getpledge(&pl[1]);
>    extra_getpledge(&pl[2]);
>    super_getpledge(&pl[3]);
>    mecha_getpledge(&pl[4]);
>
>    if(pledges(5, pl) == -1)
>        errx("pledge");
>
>    ... [other code] ...
>
>
> };
>
>
> -------
>
>
> A library can tell the application what pledges are in use as follows:
>
>
> static const char *pledge_promises = "stdio fattr sendfd recvfd"
>
> void ultra_getpledge(struct pledge *const pl)
> {
>    pl->promises = pledge_promises;
>    pl->paths = NULL;
> }
>
>
> ==============
>
> I think that #1 has the advantage of it being easier to code so a
> program can ratchet down its abilities. #2 allows one to group the
> pledge arguments into a single struct.
>
> Thoughts?

Reply via email to